开发者

How to preserve double quotes in $@ in a shell script?

Let's say I have a really simple shell script 'foo':

  #!/bin/sh
  echo $@

If I invoke it like so:

  foo 1 2 3

It happily prints:

  1 2 3

However, let's say one of my arguments is double-quote enclosed and contains whitespace:

  foo 1 "this arg has whitespace" 3

foo happily prints:

  1 this arg has whitespace 3

The double-quotes have been stripped! I know shell thinks its doing me a favor开发者_高级运维, but... I would like to get at the original version of the arguments, unmolested by shell's interpretation. Is there any way to do so?


First, you probably want quoted version of $@, i.e. "$@". To feel the difference, try putting more than one space inside the string.

Second, quotes are element of shell's syntax -- it doesn't do you a favor. To preserve them, you need to escape them. Examples:

foo 1 "\"this arg has whitespace\"" 3

foo 1 '"this arg has whitespace"' 3


Double quote $@:

#!/bin/sh
for ARG in "$@"
do
    echo $ARG
done

Then:

foo 1 "this arg has whitespace" 3

will give you:

1
this arg has whitespace
3


What i'd do is to quote all the arguments received with spaces that might help your case.

for x in "${@}" ; do
    # try to figure out if quoting was required for the $x
    if [[ "$x" != "${x%[[:space:]]*}" ]]; then
        x="\""$x"\""
    fi
    echo $x
    _args=$_args" "$x
done

echo "All Cmd Args are: $_args"


You need to quote the quotes:

foo 1 "\"this arg has whitespace\"" 3

or (more simply)

foo 1 '"this arg has whitespace"' 3

You need to quote the double quotes to make sure that the shell doesn't remove them when parsing word arguments.


Let's suppose you are in a more rigid set-up and you CANNOT change your command line, and make it more "friendly" by escaping the double quotes. For example:

example_script.sh argument_without_quotes "argument with quotes i cannot escape"

First consider that inside your script you can't tell if an argument is passed with or without quotes, because the shell strips them.

So what you can possibly do is rebuilding double quotes for arguments containing whitespaces

This example rebuilds the whole command line, double-quoting arguments that have white spaces

#!/bin/sh
#initialize the variable that will contain the whole argument string
argList=""
#iterate on each argument
for arg in "$@"
do
  #if an argument contains a white space, enclose it in double quotes and append to the list
  #otherwise simply append the argument to the list
  if echo $arg | grep -q " "; then
   argList="$argList \"$arg\""
  else
   argList="$argList $arg"
  fi
done

#remove a possible trailing space at the beginning of the list
argList=$(echo $argList | sed 's/^ *//')

#pass your argument list WITH QUOTES
echo "my_executable" $argList
#my_executable $argList

Note this limitation. If you run this example

example_script.sh "argument with spaces" argument_without_spaces "argument_doublequoted_but_without_spaces"

you will get this output

my_executable "argument with spaces" argument_without_spaces argument_doublequoted_but_without_spaces

Note the last argument: since it had no spaces, it has not been enclosed again in double quotes, but this shouldn't be an issue.


The most reliable method that I've found to do this is to leverage the logic that's built into the shell for "xtrace". This is a subsystem that's turned on via set -x which will print every command that's run by the shell. If we turn on xtrace then run a command that does nothing (true) with the same arguments, the shell (most shells...) will quote the arguments for us.

show_quoted() {
  (
    # use > as the xtrace prefix (this is the default)
    PS4='+'
    exec 2>&1  # send the xtrace (on stderr, 2) to stdout (1) for processing
    # turn on xtrace -- all commands are printed, with $PS4 as a prefix
    set -x
    # `true` is the command that does nothing, successfully
    true "$@"
  ) |
    sed '
      # remove the xtrace prefix -- any number of + characters
      s/^+*//
      # hide the "true" command
      s/^true //
    '
}

Same thing, but with a normal amount of comments:

show_quoted() {
  (
    PS4='+'  # reset to default
    exec 2>&1  # send xtrace to stdout
    set -x
    true "$@"
  ) | sed 's/^+*true //'  # remove the xtrace prefix
}

Here's my test results: (these vary a bit but are all valid)

$ sh show_quoted.sh 1 '2 3' 'he said: "hi"' "she said: 'bye'"
1 '2 3' 'he said: "hi"' 'she said: '\''bye'\'''

$ bash show_quoted.sh 1 '2 3' 'he said: "hi"' "she said: 'bye'"
1 '2 3' 'he said: "hi"' 'she said: '\''bye'\'''

$ zsh show_quoted.sh 1 '2 3' 'he said: "hi"' "she said: 'bye'"
1 '2 3' 'he said: "hi"' 'she said: '\''bye'\'

$ busybox sh show_quoted.sh 1 '2 3' 'he said: "hi"' "she said: 'bye'"
1 '2 3' 'he said: "hi"' 'she said: '"'"'bye'"'"

These shells (below) weren't up to the task however. To be fair, the toybox shell is "80% done".

$ dash show_quoted.sh 1 '2 3' 'he said: "hi"' "she said: 'bye'"
1 2 3 he said: "hi" she said: 'bye'

$ ./toybox sh show_quoted.sh 1 '2 3' 'he said: "hi"' "she said: 'bye'"
"$@"
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜