1

I want to keep the escaping of quotes in a bash function:

foo () { echo $@; }

The function above will print the arguments passed to it:

$ foo -a bar
-a bar

This is fine, but when I pass quotes I want to keep them in one argument:

$ foo -l 'bar' -m "asd asd" 
-l bar -m asd asd

I expected:

-l bar -m "asd asd"

How can I do that?

I tried to put $@ between quotes: "$@", also I tried to pass the $* variable, none of them working on my expectation.

Ionică Bizău
  • 109,027
  • 88
  • 289
  • 474
  • Your `echo` command isn't telling you what you think it is. Note that `-l 'bar' -m "asd asd"` prints `-l bar -m asd asd`, exactly as your `foo` function does. So `echo` *shouldn't* print the quotes! (And if it did print the quotes, that'd actually be an indication that something was terribly wrong.) Try defining the function `printargs() { echo "Got $# arguments:"; if [ $# -gt 0 ]; then printf " '%s'\n" "$@"; fi; }`, and use that instead of `echo`. – Gordon Davisson Jan 25 '15 at 21:29

2 Answers2

2

I think that what you want to achieve is not possible. Inside the function foo there is no way to see whether the passed argument has been quoted. The function only sees the array of arguments.

Can you live with all arguments quoted? It's ok to have unneeded double quotes when constructing a new command.

$ foo () { declare arg; for arg in "$@"; do echo -n ' "'$arg'"' ; done; echo; }
$ foo -l 'bar' -m "asd asd" 
 "-l" "bar" "-m" "asd asd"
BarneySchmale
  • 658
  • 1
  • 4
  • 10
  • 1
    To expand on this: quote removal is performed before the arguments are passed to the function (/command). Thus, `foo -l 'bar' -m "asd asd"`, `foo '-l' bar '-m' 'asd asd'`, `foo -l bar -m asd" "asd`, `foo -l bar -m asd\ asd`, and many other variants all pass *exactly* the same arguments, and the function (/command) cannot tell which was used. What it can tell is how they were broken into arguments (i.e. that `asd asd` was a single argument with a space, rather than two separate arguments), and "$@" preserves that. – Gordon Davisson Jan 25 '15 at 21:38
1

You're missing a certain understanding about how arguments in bash work.

The output from foo() is correct, but your implementation is not. The quotes aren't required to see the multiple words as a single argument; what is needed is to tell bash to use and pass the arguments correctly. Observe:

$ foo () { bar $@; }
$ bar () { echo $@; echo $#; }
$ foo -l 'bar' -m "asd asd" 
-l bar -m asd asd
5
$ foo () { bar "$@"; }
$ foo -l 'bar' -m "asd asd" 
-l bar -m asd asd
4
$ bar () { echo "$@"; echo $#; }
$ foo -l 'bar' -m "asd asd" 
-l bar -m asd asd
4

Quoting the $@ (as "$@") directs bash to not perform word splitting of the arguments passed to the script or function, although tools such as echo that naively display the arguments show an "incorrect" result.

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358