These work as advertised:
grep -ir 'hello world' . grep -ir hello\ world .
These don't:
argumentString1="-ir 'hello world'" argumentString2="-ir hello\\ world" grep $argumentString1 . grep $argumentString2 .
Despite 'hello world'
being enclosed by quotes in the second example, grep interprets 'hello
(and hello\
) as one argument and world'
(and world
) as another, which means that, in this case, 'hello
will be the search pattern and world'
will be the search path.
Again, this only happens when the arguments are expanded from the argumentString
variables. grep properly interprets 'hello world'
(and hello\ world
) as a single argument in the first example.
Can anyone explain why this is? Is there a proper way to expand a string variable that will preserve the syntax of each character such that it is correctly interpreted by shell commands?
When you want to pass all the arguments to another script, or function, use "$@" (without escaping your quotes). See this answer: Difference between single and double quotes in Bash.
To quote a generic string with double quotes, perform the following actions: Add leading and trailing double quotes: aaa ==> "aaa" Escape with a backslash every double quote character and every backslash character: " ==> \", \ ==> \\
In short, quote everything where you do not require the shell to perform word splitting and wildcard expansion. Single quotes protect the text between them verbatim. It is the proper tool when you need to ensure that the shell does not touch the string at all.
Double quotes are similar to single quotes except that it allows the shell to interpret dollar sign ($), backtick(`), backslash(\) and exclamation mark(!). The characters have special meaning when used with double quotes, and before display, they are evaluated.
When the string is expanded, it is split into words, but it is not re-evaluated to find special characters such as quotes or dollar signs or ... This is the way the shell has 'always' behaved, since the Bourne shell back in 1978 or thereabouts.
In bash
, use an array to hold the arguments:
argumentArray=(-ir 'hello world') grep "${argumentArray[@]}" .
Or, if brave/foolhardy, use eval
:
argumentString="-ir 'hello world'" eval "grep $argumentString ."
On the other hand, discretion is often the better part of valour, and working with eval
is a place where discretion is better than bravery. If you are not completely in control of the string that is eval
'd (if there's any user input in the command string that has not been rigorously validated), then you are opening yourself to potentially serious problems.
Note that the sequence of expansions for Bash is described in Shell Expansions in the GNU Bash manual. Note in particular sections 3.5.3 Shell Parameter Expansion, 3.5.7 Word Splitting, and 3.5.9 Quote Removal.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With