How to pass command line parameters with quotes stored in single variable?

前端 未结 5 1093
闹比i
闹比i 2021-01-07 03:58

I want to call external application from shell script, but this shell script gets parameters (from other script) in a single variable. All was OK until I did not have to use

相关标签:
5条回答
  • 2021-01-07 03:59

    If you're stuck with a single variable, you'll have to use eval:

    $ show() { i=0; for param; do ((i++)); echo "$i>$param"; done; }
    
    $ show '"single param" separate params'
    1>"single param" separate params
    
    $ eval show '"single param" separate params'
    1>single param
    2>separate
    3>params
    

    Note that the double quotes are eaten by the shell.

    0 讨论(0)
  • 2021-01-07 04:00

    Answering my own question. BIG thanks goes to pzanoni. xargs seems to parse correctly anything you are throwing to it :-) "$@", "$", $@ and $ works good with it. So my code now looks like:

    #!/bin/sh
    
    pass() {
        echo $* | xargs ./sh_param
    }
    
    pass '"single param" separate params'
    

    And result is what I wanted:

    Param: single param
    Param: separate
    Param: params
    
    0 讨论(0)
  • 2021-01-07 04:15

    This might work, although it's hard to tell without knowing how sh_param handles its arguments.

    #!/bin/sh
    
    pass() {
        echo "Result with \"\$@\""
        ./sh_param "$@"
    }
    
    pass "single param" separate params
    
    0 讨论(0)
  • 2021-01-07 04:19

    script

    pass() {
      echo 'Result with "$@"'
      sh_param "$@"
    }
    
    sh_param() {
      for i in "$@"
      do
        echo Param: $i
      done
    }
    
    pass "single param" separate param
    

    result

    Result with "$@"
    Param: single param
    Param: separate
    Param: param
    
    0 讨论(0)
  • 2021-01-07 04:19

    Avoid passing the whole thing as a single argument (as you do with the squotes). It's hard enough to make shell scripts that carefully preserve the number of arguments passed, without needing to make it harder by flattening them into strings and expanding them again.

    If you do need to though, there are some best practices to follow. I had to write a script a while back that serves as a su/sudo wrapper: su takes a single argument that it passes to sh to evaluate; sudo takes any number of arguments that it passes on unmodified to an execv(e).

    You have to be pretty careful about getting it portable and not running into problems in obscure shells. The whole script isn't very useful to post, but the general gist is to write some escaping functions and very perform the quoting to build up a bullet-proof, unreadably-cautiously escaped string that's safe to pass to eval.

    bash_escape() {
      # backtick indirection strictly necessary here: we use it to strip the
      # trailing newline from sed's output, which Solaris/BSD sed *always* output
      # (unlike GNU sed, which outputs "test": printf %s test | sed -e s/dummy//)
      out=`echo "$1" | sed -e s/\\'/\\''\\\\'\\'\\'/g`
      printf \'%s\' "$out"
    }
    append_bash_escape() {
      printf "%s " "$1"
      bash_escape "$2"
    }
    sed_escape() {
      out=`echo "$1" | sed -e 's/[\\/&]/\\\\&/g'`
      printf %s "$out"
    }
    

    These useful functions let you do something like this to portably build command strings:

    COMMAND=
    while [ $# -gt 0 ] ; do
      COMMAND=`append_bash_escape "$COMMAND" "$1"` ; shift
    done
    

    You can then manipulate the command string, for example by running bash_escape on it and using sed_escape to substitute into the string "su - root -c SUB_HERE", or just substitute it directly into a string like "sudo -- SUB_HERE". You then have something you can safely eval without worrying about metacharacters, argument splitting, unescaped globs, and so on.

    Be paranoid, and unittest your script with every nasty input you can think of to make sure your argument splitting and preserving really is correct!

    0 讨论(0)
提交回复
热议问题