Can a bash array be used in place of eval set — “$params”?

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP












3














I'm taking a look at the optparse library for bash option parsing, specifically this bit in the generated code:



params=""
while [ $# -ne 0 ]; do
param="$1"
shift

case "$param" in

--my-long-flag)
params="$params -m";;
--another-flag)
params="$params -a";;
"-?"|--help)
usage
exit 0;;
*)
if [[ "$param" == --* ]]; then
echo -e "Unrecognized long option: $param"
usage
exit 1
fi
params="$params "$param"";; ##### THIS LINE
esac
done

eval set -- "$params" ##### AND THIS LINE

# then a typical while getopts loop


Would there be any real reason to use eval here? The input to eval seems to be properly sanitized. But wouldn't it work the same to use:



params=()
# ...
--my-long-flag)
params+=("-m");;
--another-flag)
params+=("-a");;
# ...
params+=("$param");;
# ...
set -- "$params[@]"


That seems cleaner to me.



In fact, wouldn't this allow options to be parsed directly out of the params array (without even using set) by using while getopts "ma" option "$params[@]"; do instead of while getopts "ma" option; do?










share|improve this question




























    3














    I'm taking a look at the optparse library for bash option parsing, specifically this bit in the generated code:



    params=""
    while [ $# -ne 0 ]; do
    param="$1"
    shift

    case "$param" in

    --my-long-flag)
    params="$params -m";;
    --another-flag)
    params="$params -a";;
    "-?"|--help)
    usage
    exit 0;;
    *)
    if [[ "$param" == --* ]]; then
    echo -e "Unrecognized long option: $param"
    usage
    exit 1
    fi
    params="$params "$param"";; ##### THIS LINE
    esac
    done

    eval set -- "$params" ##### AND THIS LINE

    # then a typical while getopts loop


    Would there be any real reason to use eval here? The input to eval seems to be properly sanitized. But wouldn't it work the same to use:



    params=()
    # ...
    --my-long-flag)
    params+=("-m");;
    --another-flag)
    params+=("-a");;
    # ...
    params+=("$param");;
    # ...
    set -- "$params[@]"


    That seems cleaner to me.



    In fact, wouldn't this allow options to be parsed directly out of the params array (without even using set) by using while getopts "ma" option "$params[@]"; do instead of while getopts "ma" option; do?










    share|improve this question


























      3












      3








      3







      I'm taking a look at the optparse library for bash option parsing, specifically this bit in the generated code:



      params=""
      while [ $# -ne 0 ]; do
      param="$1"
      shift

      case "$param" in

      --my-long-flag)
      params="$params -m";;
      --another-flag)
      params="$params -a";;
      "-?"|--help)
      usage
      exit 0;;
      *)
      if [[ "$param" == --* ]]; then
      echo -e "Unrecognized long option: $param"
      usage
      exit 1
      fi
      params="$params "$param"";; ##### THIS LINE
      esac
      done

      eval set -- "$params" ##### AND THIS LINE

      # then a typical while getopts loop


      Would there be any real reason to use eval here? The input to eval seems to be properly sanitized. But wouldn't it work the same to use:



      params=()
      # ...
      --my-long-flag)
      params+=("-m");;
      --another-flag)
      params+=("-a");;
      # ...
      params+=("$param");;
      # ...
      set -- "$params[@]"


      That seems cleaner to me.



      In fact, wouldn't this allow options to be parsed directly out of the params array (without even using set) by using while getopts "ma" option "$params[@]"; do instead of while getopts "ma" option; do?










      share|improve this question















      I'm taking a look at the optparse library for bash option parsing, specifically this bit in the generated code:



      params=""
      while [ $# -ne 0 ]; do
      param="$1"
      shift

      case "$param" in

      --my-long-flag)
      params="$params -m";;
      --another-flag)
      params="$params -a";;
      "-?"|--help)
      usage
      exit 0;;
      *)
      if [[ "$param" == --* ]]; then
      echo -e "Unrecognized long option: $param"
      usage
      exit 1
      fi
      params="$params "$param"";; ##### THIS LINE
      esac
      done

      eval set -- "$params" ##### AND THIS LINE

      # then a typical while getopts loop


      Would there be any real reason to use eval here? The input to eval seems to be properly sanitized. But wouldn't it work the same to use:



      params=()
      # ...
      --my-long-flag)
      params+=("-m");;
      --another-flag)
      params+=("-a");;
      # ...
      params+=("$param");;
      # ...
      set -- "$params[@]"


      That seems cleaner to me.



      In fact, wouldn't this allow options to be parsed directly out of the params array (without even using set) by using while getopts "ma" option "$params[@]"; do instead of while getopts "ma" option; do?







      bash array options parameter getopts






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Jan 6 '16 at 0:06

























      asked Jan 5 '16 at 20:21









      Wildcard

      22.6k962164




      22.6k962164




















          2 Answers
          2






          active

          oldest

          votes


















          2














          You don't need to use a bash array here (but do so if it feels better).



          Here's how to do it for /bin/sh:



          #!/bin/sh

          for arg do
          shift

          case "$arg" in
          --my-long-flag)
          set -- "$@" -m ;;
          --another-flag)
          set -- "$@" -a ;;
          "-?"|--help)
          usage
          exit 0 ;;
          --*)
          printf 'Unrecognised long option: %sn' "$arg" >$2
          usage
          exit 1 ;;
          *)
          set -- "$@" "$arg"
          esac
          done


          This is cleaner than the bash array solution (personal opinion) since it doesn't need to introduce another variable. It's also better than the auto-generated code that you show as it retains each command line argument as a separate item in "$@". This is good, because this allows the user to pass arguments containing whitespace characters as long as they are quoted (which the auto-generated code does not do).




          Style comments:



          • The loop above is supposed to translate long options into short options for a loop over getopts later. As such, it breaks from that task by actually acting on some options, such as -? and --help. IMHO, these should instead be translated to -h (or some suitable short option).



          • It also does the translation of long options past the point where options should not be accepted. Calling the script as



            ./script.sh --my-long-flag -- -?


            should not interpret -? as an option due to the -- (meaning "options ends here"). Likewise,



            ./script.sh filename --my-long-flag


            should not interpret --my-long-option as an option, as the parsing of options should stop at the first non-option.



          Here's a variant that takes the above into account:



          #!/bin/sh

          parse=YES

          for arg do
          shift

          if [ "$parse" = YES ]; then
          case "$arg" in
          --my-long-flag)
          set -- "$@" -m ;;
          --another-flag)
          set -- "$@" -a ;;
          --help)
          set -- "$@" -h ;;
          --)
          parse=NO
          set -- "$@" -- ;;
          --*)
          printf 'Unrecognised long option: %sn' "$arg" >$2
          usage
          exit 1 ;;
          *)
          parse=NO
          set -- "$@" "$arg"
          esac
          else
          set -- "$@" "$arg"
          fi

          done


          What this does not allow is long options with separate option arguments, such as --option hello (the hello would be treated as a non-option and the option parsing would end). Something like --option=hello would be fairly easy to handle with a bit of extra tinkering though.






          share|improve this answer






















          • I would love an explanation of how iterating over the positional parameters interacts with changing those positional parameters at the same time.
            – Wildcard
            Dec 20 '18 at 22:23






          • 1




            @Wildcard The standard for loop always iterates over a static list, so once it's started, you can do whatever with $@. The loop range/items is not re-evaluated in each iteration.
            – Kusalananda
            Dec 20 '18 at 22:58







          • 1




            @Wildcard In this instance, you could also do for dummy do arg=$1; shift; ...; done. This would just iterate as many times as the original $@ had elements but the dummy variable would not be used.
            – Kusalananda
            Dec 20 '18 at 23:09



















          4














          The question is "Should a bash array be used in place of eval set — “$params”?", and the answer is yes!.



          In your script, the input to eval is clearly not properly sanitized. Try



           yourscript '`xterm`'


          and you'll see that an xterm is started even though the backticks are properly quoted by single quotes. (Compare with



           echo '`xterm`'


          which does not start an xterm.)



          Fixing the bug while keepting eval is very difficult.
          Even changing the line



           params="$params "$param"";;


          to



           params="$params '$param'";;


          would not help: Now



           yourscript '`xterm`'


          no longer starts an xterm, but



           yourscript '' `xterm` ''


          still does.






          share|improve this answer






















          • Thanks! I'm working on a remote box over ssh, so I don't have xterm available, but I tested it with myscript '`touch /tmp/hacked`' (and compared it to echo '`touch /tmp/hacked`'). I didn't even need the double quotes or the spaces, though; is there a different edge case those are hacking around?
            – Wildcard
            Jan 5 '16 at 23:35











          • Also, I've never used bash arrays before—I wrote the "cleaner" version that uses arrays after a brief once-over of the applicable man page section. Did I get it right? Any pitfalls in it that you see?
            – Wildcard
            Jan 5 '16 at 23:36











          • Also NB: This is not my script. I linked to the optparse library in my question; that's the source of this code. However, the optparse library is sourced from some other code that I have to maintain, so I want to clean up the vulnerabilities. I've never used eval in a script and probably never will. ;)
            – Wildcard
            Jan 5 '16 at 23:40






          • 1




            @Wildcard yes, you're right, the double quotes or the spaces are unnecessary, since the script tries to protect $param with double quotes, and backticks are evaluated even within double quotes. If $param were protected with single quote, one would need a more complicated argument.
            – Uwe
            Jan 5 '16 at 23:57






          • 2




            @AlexejMagura These are literal backticks. They are properly quoted. They are not supposed to be interpreted -- but they are interpreted.
            – Uwe
            Jan 19 '17 at 21:49










          Your Answer








          StackExchange.ready(function()
          var channelOptions =
          tags: "".split(" "),
          id: "106"
          ;
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function()
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled)
          StackExchange.using("snippets", function()
          createEditor();
          );

          else
          createEditor();

          );

          function createEditor()
          StackExchange.prepareEditor(
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader:
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          ,
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          );



          );













          draft saved

          draft discarded


















          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f253481%2fcan-a-bash-array-be-used-in-place-of-eval-set-params%23new-answer', 'question_page');

          );

          Post as a guest















          Required, but never shown

























          2 Answers
          2






          active

          oldest

          votes








          2 Answers
          2






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          2














          You don't need to use a bash array here (but do so if it feels better).



          Here's how to do it for /bin/sh:



          #!/bin/sh

          for arg do
          shift

          case "$arg" in
          --my-long-flag)
          set -- "$@" -m ;;
          --another-flag)
          set -- "$@" -a ;;
          "-?"|--help)
          usage
          exit 0 ;;
          --*)
          printf 'Unrecognised long option: %sn' "$arg" >$2
          usage
          exit 1 ;;
          *)
          set -- "$@" "$arg"
          esac
          done


          This is cleaner than the bash array solution (personal opinion) since it doesn't need to introduce another variable. It's also better than the auto-generated code that you show as it retains each command line argument as a separate item in "$@". This is good, because this allows the user to pass arguments containing whitespace characters as long as they are quoted (which the auto-generated code does not do).




          Style comments:



          • The loop above is supposed to translate long options into short options for a loop over getopts later. As such, it breaks from that task by actually acting on some options, such as -? and --help. IMHO, these should instead be translated to -h (or some suitable short option).



          • It also does the translation of long options past the point where options should not be accepted. Calling the script as



            ./script.sh --my-long-flag -- -?


            should not interpret -? as an option due to the -- (meaning "options ends here"). Likewise,



            ./script.sh filename --my-long-flag


            should not interpret --my-long-option as an option, as the parsing of options should stop at the first non-option.



          Here's a variant that takes the above into account:



          #!/bin/sh

          parse=YES

          for arg do
          shift

          if [ "$parse" = YES ]; then
          case "$arg" in
          --my-long-flag)
          set -- "$@" -m ;;
          --another-flag)
          set -- "$@" -a ;;
          --help)
          set -- "$@" -h ;;
          --)
          parse=NO
          set -- "$@" -- ;;
          --*)
          printf 'Unrecognised long option: %sn' "$arg" >$2
          usage
          exit 1 ;;
          *)
          parse=NO
          set -- "$@" "$arg"
          esac
          else
          set -- "$@" "$arg"
          fi

          done


          What this does not allow is long options with separate option arguments, such as --option hello (the hello would be treated as a non-option and the option parsing would end). Something like --option=hello would be fairly easy to handle with a bit of extra tinkering though.






          share|improve this answer






















          • I would love an explanation of how iterating over the positional parameters interacts with changing those positional parameters at the same time.
            – Wildcard
            Dec 20 '18 at 22:23






          • 1




            @Wildcard The standard for loop always iterates over a static list, so once it's started, you can do whatever with $@. The loop range/items is not re-evaluated in each iteration.
            – Kusalananda
            Dec 20 '18 at 22:58







          • 1




            @Wildcard In this instance, you could also do for dummy do arg=$1; shift; ...; done. This would just iterate as many times as the original $@ had elements but the dummy variable would not be used.
            – Kusalananda
            Dec 20 '18 at 23:09
















          2














          You don't need to use a bash array here (but do so if it feels better).



          Here's how to do it for /bin/sh:



          #!/bin/sh

          for arg do
          shift

          case "$arg" in
          --my-long-flag)
          set -- "$@" -m ;;
          --another-flag)
          set -- "$@" -a ;;
          "-?"|--help)
          usage
          exit 0 ;;
          --*)
          printf 'Unrecognised long option: %sn' "$arg" >$2
          usage
          exit 1 ;;
          *)
          set -- "$@" "$arg"
          esac
          done


          This is cleaner than the bash array solution (personal opinion) since it doesn't need to introduce another variable. It's also better than the auto-generated code that you show as it retains each command line argument as a separate item in "$@". This is good, because this allows the user to pass arguments containing whitespace characters as long as they are quoted (which the auto-generated code does not do).




          Style comments:



          • The loop above is supposed to translate long options into short options for a loop over getopts later. As such, it breaks from that task by actually acting on some options, such as -? and --help. IMHO, these should instead be translated to -h (or some suitable short option).



          • It also does the translation of long options past the point where options should not be accepted. Calling the script as



            ./script.sh --my-long-flag -- -?


            should not interpret -? as an option due to the -- (meaning "options ends here"). Likewise,



            ./script.sh filename --my-long-flag


            should not interpret --my-long-option as an option, as the parsing of options should stop at the first non-option.



          Here's a variant that takes the above into account:



          #!/bin/sh

          parse=YES

          for arg do
          shift

          if [ "$parse" = YES ]; then
          case "$arg" in
          --my-long-flag)
          set -- "$@" -m ;;
          --another-flag)
          set -- "$@" -a ;;
          --help)
          set -- "$@" -h ;;
          --)
          parse=NO
          set -- "$@" -- ;;
          --*)
          printf 'Unrecognised long option: %sn' "$arg" >$2
          usage
          exit 1 ;;
          *)
          parse=NO
          set -- "$@" "$arg"
          esac
          else
          set -- "$@" "$arg"
          fi

          done


          What this does not allow is long options with separate option arguments, such as --option hello (the hello would be treated as a non-option and the option parsing would end). Something like --option=hello would be fairly easy to handle with a bit of extra tinkering though.






          share|improve this answer






















          • I would love an explanation of how iterating over the positional parameters interacts with changing those positional parameters at the same time.
            – Wildcard
            Dec 20 '18 at 22:23






          • 1




            @Wildcard The standard for loop always iterates over a static list, so once it's started, you can do whatever with $@. The loop range/items is not re-evaluated in each iteration.
            – Kusalananda
            Dec 20 '18 at 22:58







          • 1




            @Wildcard In this instance, you could also do for dummy do arg=$1; shift; ...; done. This would just iterate as many times as the original $@ had elements but the dummy variable would not be used.
            – Kusalananda
            Dec 20 '18 at 23:09














          2












          2








          2






          You don't need to use a bash array here (but do so if it feels better).



          Here's how to do it for /bin/sh:



          #!/bin/sh

          for arg do
          shift

          case "$arg" in
          --my-long-flag)
          set -- "$@" -m ;;
          --another-flag)
          set -- "$@" -a ;;
          "-?"|--help)
          usage
          exit 0 ;;
          --*)
          printf 'Unrecognised long option: %sn' "$arg" >$2
          usage
          exit 1 ;;
          *)
          set -- "$@" "$arg"
          esac
          done


          This is cleaner than the bash array solution (personal opinion) since it doesn't need to introduce another variable. It's also better than the auto-generated code that you show as it retains each command line argument as a separate item in "$@". This is good, because this allows the user to pass arguments containing whitespace characters as long as they are quoted (which the auto-generated code does not do).




          Style comments:



          • The loop above is supposed to translate long options into short options for a loop over getopts later. As such, it breaks from that task by actually acting on some options, such as -? and --help. IMHO, these should instead be translated to -h (or some suitable short option).



          • It also does the translation of long options past the point where options should not be accepted. Calling the script as



            ./script.sh --my-long-flag -- -?


            should not interpret -? as an option due to the -- (meaning "options ends here"). Likewise,



            ./script.sh filename --my-long-flag


            should not interpret --my-long-option as an option, as the parsing of options should stop at the first non-option.



          Here's a variant that takes the above into account:



          #!/bin/sh

          parse=YES

          for arg do
          shift

          if [ "$parse" = YES ]; then
          case "$arg" in
          --my-long-flag)
          set -- "$@" -m ;;
          --another-flag)
          set -- "$@" -a ;;
          --help)
          set -- "$@" -h ;;
          --)
          parse=NO
          set -- "$@" -- ;;
          --*)
          printf 'Unrecognised long option: %sn' "$arg" >$2
          usage
          exit 1 ;;
          *)
          parse=NO
          set -- "$@" "$arg"
          esac
          else
          set -- "$@" "$arg"
          fi

          done


          What this does not allow is long options with separate option arguments, such as --option hello (the hello would be treated as a non-option and the option parsing would end). Something like --option=hello would be fairly easy to handle with a bit of extra tinkering though.






          share|improve this answer














          You don't need to use a bash array here (but do so if it feels better).



          Here's how to do it for /bin/sh:



          #!/bin/sh

          for arg do
          shift

          case "$arg" in
          --my-long-flag)
          set -- "$@" -m ;;
          --another-flag)
          set -- "$@" -a ;;
          "-?"|--help)
          usage
          exit 0 ;;
          --*)
          printf 'Unrecognised long option: %sn' "$arg" >$2
          usage
          exit 1 ;;
          *)
          set -- "$@" "$arg"
          esac
          done


          This is cleaner than the bash array solution (personal opinion) since it doesn't need to introduce another variable. It's also better than the auto-generated code that you show as it retains each command line argument as a separate item in "$@". This is good, because this allows the user to pass arguments containing whitespace characters as long as they are quoted (which the auto-generated code does not do).




          Style comments:



          • The loop above is supposed to translate long options into short options for a loop over getopts later. As such, it breaks from that task by actually acting on some options, such as -? and --help. IMHO, these should instead be translated to -h (or some suitable short option).



          • It also does the translation of long options past the point where options should not be accepted. Calling the script as



            ./script.sh --my-long-flag -- -?


            should not interpret -? as an option due to the -- (meaning "options ends here"). Likewise,



            ./script.sh filename --my-long-flag


            should not interpret --my-long-option as an option, as the parsing of options should stop at the first non-option.



          Here's a variant that takes the above into account:



          #!/bin/sh

          parse=YES

          for arg do
          shift

          if [ "$parse" = YES ]; then
          case "$arg" in
          --my-long-flag)
          set -- "$@" -m ;;
          --another-flag)
          set -- "$@" -a ;;
          --help)
          set -- "$@" -h ;;
          --)
          parse=NO
          set -- "$@" -- ;;
          --*)
          printf 'Unrecognised long option: %sn' "$arg" >$2
          usage
          exit 1 ;;
          *)
          parse=NO
          set -- "$@" "$arg"
          esac
          else
          set -- "$@" "$arg"
          fi

          done


          What this does not allow is long options with separate option arguments, such as --option hello (the hello would be treated as a non-option and the option parsing would end). Something like --option=hello would be fairly easy to handle with a bit of extra tinkering though.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Dec 20 '18 at 23:38

























          answered Dec 20 '18 at 21:54









          Kusalananda

          122k16229374




          122k16229374











          • I would love an explanation of how iterating over the positional parameters interacts with changing those positional parameters at the same time.
            – Wildcard
            Dec 20 '18 at 22:23






          • 1




            @Wildcard The standard for loop always iterates over a static list, so once it's started, you can do whatever with $@. The loop range/items is not re-evaluated in each iteration.
            – Kusalananda
            Dec 20 '18 at 22:58







          • 1




            @Wildcard In this instance, you could also do for dummy do arg=$1; shift; ...; done. This would just iterate as many times as the original $@ had elements but the dummy variable would not be used.
            – Kusalananda
            Dec 20 '18 at 23:09

















          • I would love an explanation of how iterating over the positional parameters interacts with changing those positional parameters at the same time.
            – Wildcard
            Dec 20 '18 at 22:23






          • 1




            @Wildcard The standard for loop always iterates over a static list, so once it's started, you can do whatever with $@. The loop range/items is not re-evaluated in each iteration.
            – Kusalananda
            Dec 20 '18 at 22:58







          • 1




            @Wildcard In this instance, you could also do for dummy do arg=$1; shift; ...; done. This would just iterate as many times as the original $@ had elements but the dummy variable would not be used.
            – Kusalananda
            Dec 20 '18 at 23:09
















          I would love an explanation of how iterating over the positional parameters interacts with changing those positional parameters at the same time.
          – Wildcard
          Dec 20 '18 at 22:23




          I would love an explanation of how iterating over the positional parameters interacts with changing those positional parameters at the same time.
          – Wildcard
          Dec 20 '18 at 22:23




          1




          1




          @Wildcard The standard for loop always iterates over a static list, so once it's started, you can do whatever with $@. The loop range/items is not re-evaluated in each iteration.
          – Kusalananda
          Dec 20 '18 at 22:58





          @Wildcard The standard for loop always iterates over a static list, so once it's started, you can do whatever with $@. The loop range/items is not re-evaluated in each iteration.
          – Kusalananda
          Dec 20 '18 at 22:58





          1




          1




          @Wildcard In this instance, you could also do for dummy do arg=$1; shift; ...; done. This would just iterate as many times as the original $@ had elements but the dummy variable would not be used.
          – Kusalananda
          Dec 20 '18 at 23:09





          @Wildcard In this instance, you could also do for dummy do arg=$1; shift; ...; done. This would just iterate as many times as the original $@ had elements but the dummy variable would not be used.
          – Kusalananda
          Dec 20 '18 at 23:09














          4














          The question is "Should a bash array be used in place of eval set — “$params”?", and the answer is yes!.



          In your script, the input to eval is clearly not properly sanitized. Try



           yourscript '`xterm`'


          and you'll see that an xterm is started even though the backticks are properly quoted by single quotes. (Compare with



           echo '`xterm`'


          which does not start an xterm.)



          Fixing the bug while keepting eval is very difficult.
          Even changing the line



           params="$params "$param"";;


          to



           params="$params '$param'";;


          would not help: Now



           yourscript '`xterm`'


          no longer starts an xterm, but



           yourscript '' `xterm` ''


          still does.






          share|improve this answer






















          • Thanks! I'm working on a remote box over ssh, so I don't have xterm available, but I tested it with myscript '`touch /tmp/hacked`' (and compared it to echo '`touch /tmp/hacked`'). I didn't even need the double quotes or the spaces, though; is there a different edge case those are hacking around?
            – Wildcard
            Jan 5 '16 at 23:35











          • Also, I've never used bash arrays before—I wrote the "cleaner" version that uses arrays after a brief once-over of the applicable man page section. Did I get it right? Any pitfalls in it that you see?
            – Wildcard
            Jan 5 '16 at 23:36











          • Also NB: This is not my script. I linked to the optparse library in my question; that's the source of this code. However, the optparse library is sourced from some other code that I have to maintain, so I want to clean up the vulnerabilities. I've never used eval in a script and probably never will. ;)
            – Wildcard
            Jan 5 '16 at 23:40






          • 1




            @Wildcard yes, you're right, the double quotes or the spaces are unnecessary, since the script tries to protect $param with double quotes, and backticks are evaluated even within double quotes. If $param were protected with single quote, one would need a more complicated argument.
            – Uwe
            Jan 5 '16 at 23:57






          • 2




            @AlexejMagura These are literal backticks. They are properly quoted. They are not supposed to be interpreted -- but they are interpreted.
            – Uwe
            Jan 19 '17 at 21:49















          4














          The question is "Should a bash array be used in place of eval set — “$params”?", and the answer is yes!.



          In your script, the input to eval is clearly not properly sanitized. Try



           yourscript '`xterm`'


          and you'll see that an xterm is started even though the backticks are properly quoted by single quotes. (Compare with



           echo '`xterm`'


          which does not start an xterm.)



          Fixing the bug while keepting eval is very difficult.
          Even changing the line



           params="$params "$param"";;


          to



           params="$params '$param'";;


          would not help: Now



           yourscript '`xterm`'


          no longer starts an xterm, but



           yourscript '' `xterm` ''


          still does.






          share|improve this answer






















          • Thanks! I'm working on a remote box over ssh, so I don't have xterm available, but I tested it with myscript '`touch /tmp/hacked`' (and compared it to echo '`touch /tmp/hacked`'). I didn't even need the double quotes or the spaces, though; is there a different edge case those are hacking around?
            – Wildcard
            Jan 5 '16 at 23:35











          • Also, I've never used bash arrays before—I wrote the "cleaner" version that uses arrays after a brief once-over of the applicable man page section. Did I get it right? Any pitfalls in it that you see?
            – Wildcard
            Jan 5 '16 at 23:36











          • Also NB: This is not my script. I linked to the optparse library in my question; that's the source of this code. However, the optparse library is sourced from some other code that I have to maintain, so I want to clean up the vulnerabilities. I've never used eval in a script and probably never will. ;)
            – Wildcard
            Jan 5 '16 at 23:40






          • 1




            @Wildcard yes, you're right, the double quotes or the spaces are unnecessary, since the script tries to protect $param with double quotes, and backticks are evaluated even within double quotes. If $param were protected with single quote, one would need a more complicated argument.
            – Uwe
            Jan 5 '16 at 23:57






          • 2




            @AlexejMagura These are literal backticks. They are properly quoted. They are not supposed to be interpreted -- but they are interpreted.
            – Uwe
            Jan 19 '17 at 21:49













          4












          4








          4






          The question is "Should a bash array be used in place of eval set — “$params”?", and the answer is yes!.



          In your script, the input to eval is clearly not properly sanitized. Try



           yourscript '`xterm`'


          and you'll see that an xterm is started even though the backticks are properly quoted by single quotes. (Compare with



           echo '`xterm`'


          which does not start an xterm.)



          Fixing the bug while keepting eval is very difficult.
          Even changing the line



           params="$params "$param"";;


          to



           params="$params '$param'";;


          would not help: Now



           yourscript '`xterm`'


          no longer starts an xterm, but



           yourscript '' `xterm` ''


          still does.






          share|improve this answer














          The question is "Should a bash array be used in place of eval set — “$params”?", and the answer is yes!.



          In your script, the input to eval is clearly not properly sanitized. Try



           yourscript '`xterm`'


          and you'll see that an xterm is started even though the backticks are properly quoted by single quotes. (Compare with



           echo '`xterm`'


          which does not start an xterm.)



          Fixing the bug while keepting eval is very difficult.
          Even changing the line



           params="$params "$param"";;


          to



           params="$params '$param'";;


          would not help: Now



           yourscript '`xterm`'


          no longer starts an xterm, but



           yourscript '' `xterm` ''


          still does.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Jan 6 '16 at 0:19

























          answered Jan 5 '16 at 23:21









          Uwe

          3,0171217




          3,0171217











          • Thanks! I'm working on a remote box over ssh, so I don't have xterm available, but I tested it with myscript '`touch /tmp/hacked`' (and compared it to echo '`touch /tmp/hacked`'). I didn't even need the double quotes or the spaces, though; is there a different edge case those are hacking around?
            – Wildcard
            Jan 5 '16 at 23:35











          • Also, I've never used bash arrays before—I wrote the "cleaner" version that uses arrays after a brief once-over of the applicable man page section. Did I get it right? Any pitfalls in it that you see?
            – Wildcard
            Jan 5 '16 at 23:36











          • Also NB: This is not my script. I linked to the optparse library in my question; that's the source of this code. However, the optparse library is sourced from some other code that I have to maintain, so I want to clean up the vulnerabilities. I've never used eval in a script and probably never will. ;)
            – Wildcard
            Jan 5 '16 at 23:40






          • 1




            @Wildcard yes, you're right, the double quotes or the spaces are unnecessary, since the script tries to protect $param with double quotes, and backticks are evaluated even within double quotes. If $param were protected with single quote, one would need a more complicated argument.
            – Uwe
            Jan 5 '16 at 23:57






          • 2




            @AlexejMagura These are literal backticks. They are properly quoted. They are not supposed to be interpreted -- but they are interpreted.
            – Uwe
            Jan 19 '17 at 21:49
















          • Thanks! I'm working on a remote box over ssh, so I don't have xterm available, but I tested it with myscript '`touch /tmp/hacked`' (and compared it to echo '`touch /tmp/hacked`'). I didn't even need the double quotes or the spaces, though; is there a different edge case those are hacking around?
            – Wildcard
            Jan 5 '16 at 23:35











          • Also, I've never used bash arrays before—I wrote the "cleaner" version that uses arrays after a brief once-over of the applicable man page section. Did I get it right? Any pitfalls in it that you see?
            – Wildcard
            Jan 5 '16 at 23:36











          • Also NB: This is not my script. I linked to the optparse library in my question; that's the source of this code. However, the optparse library is sourced from some other code that I have to maintain, so I want to clean up the vulnerabilities. I've never used eval in a script and probably never will. ;)
            – Wildcard
            Jan 5 '16 at 23:40






          • 1




            @Wildcard yes, you're right, the double quotes or the spaces are unnecessary, since the script tries to protect $param with double quotes, and backticks are evaluated even within double quotes. If $param were protected with single quote, one would need a more complicated argument.
            – Uwe
            Jan 5 '16 at 23:57






          • 2




            @AlexejMagura These are literal backticks. They are properly quoted. They are not supposed to be interpreted -- but they are interpreted.
            – Uwe
            Jan 19 '17 at 21:49















          Thanks! I'm working on a remote box over ssh, so I don't have xterm available, but I tested it with myscript '`touch /tmp/hacked`' (and compared it to echo '`touch /tmp/hacked`'). I didn't even need the double quotes or the spaces, though; is there a different edge case those are hacking around?
          – Wildcard
          Jan 5 '16 at 23:35





          Thanks! I'm working on a remote box over ssh, so I don't have xterm available, but I tested it with myscript '`touch /tmp/hacked`' (and compared it to echo '`touch /tmp/hacked`'). I didn't even need the double quotes or the spaces, though; is there a different edge case those are hacking around?
          – Wildcard
          Jan 5 '16 at 23:35













          Also, I've never used bash arrays before—I wrote the "cleaner" version that uses arrays after a brief once-over of the applicable man page section. Did I get it right? Any pitfalls in it that you see?
          – Wildcard
          Jan 5 '16 at 23:36





          Also, I've never used bash arrays before—I wrote the "cleaner" version that uses arrays after a brief once-over of the applicable man page section. Did I get it right? Any pitfalls in it that you see?
          – Wildcard
          Jan 5 '16 at 23:36













          Also NB: This is not my script. I linked to the optparse library in my question; that's the source of this code. However, the optparse library is sourced from some other code that I have to maintain, so I want to clean up the vulnerabilities. I've never used eval in a script and probably never will. ;)
          – Wildcard
          Jan 5 '16 at 23:40




          Also NB: This is not my script. I linked to the optparse library in my question; that's the source of this code. However, the optparse library is sourced from some other code that I have to maintain, so I want to clean up the vulnerabilities. I've never used eval in a script and probably never will. ;)
          – Wildcard
          Jan 5 '16 at 23:40




          1




          1




          @Wildcard yes, you're right, the double quotes or the spaces are unnecessary, since the script tries to protect $param with double quotes, and backticks are evaluated even within double quotes. If $param were protected with single quote, one would need a more complicated argument.
          – Uwe
          Jan 5 '16 at 23:57




          @Wildcard yes, you're right, the double quotes or the spaces are unnecessary, since the script tries to protect $param with double quotes, and backticks are evaluated even within double quotes. If $param were protected with single quote, one would need a more complicated argument.
          – Uwe
          Jan 5 '16 at 23:57




          2




          2




          @AlexejMagura These are literal backticks. They are properly quoted. They are not supposed to be interpreted -- but they are interpreted.
          – Uwe
          Jan 19 '17 at 21:49




          @AlexejMagura These are literal backticks. They are properly quoted. They are not supposed to be interpreted -- but they are interpreted.
          – Uwe
          Jan 19 '17 at 21:49

















          draft saved

          draft discarded
















































          Thanks for contributing an answer to Unix & Linux Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid


          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.

          To learn more, see our tips on writing great answers.





          Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


          Please pay close attention to the following guidance:


          • Please be sure to answer the question. Provide details and share your research!

          But avoid


          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.

          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f253481%2fcan-a-bash-array-be-used-in-place-of-eval-set-params%23new-answer', 'question_page');

          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown