What is the difference between
quotes wrap around only the option value
eg:
grep --file="grep pattern file.txt" *
vs
quotes wrap around the option name and option value
eg:
grep "--file=grep pattern file.txt" *
?
They produce the same result.
What is the difference between
quotes wrap around only the option value
eg: grep --file="grep pattern file.txt" *
vs
quotes wrap around the option name and option value
eg: grep "--file=grep pattern file.txt" *
?
They produce the same result.
Quotes and backslash in shells are used to remove the specialness of some characters so they be treated as ordinary characters.
Double quotes are special in that they still allow expansions to take place within. Or in other words, within them $
, \
, and `
are still special. They also affect how those expansions are performed.
In that line, the only characters that are special to the shell are:
*
, which is a glob pattern operator, and the presence of such a character, when not quoted in a simple command line, triggers a mechanism called filename generation or globbing or path name expansion (the POSIX wording).The other characters have no special significance in the shell syntax.
Here, what we want to do is for the shell to execute the /usr/bin/grep
file with these arguments:
grep
--file=grep pattern file.txt
So we do want:
*
to be treated as a glob operator and be expanded to the non hidden files in the current working directory.So those characters above must not be quoted.
However, there are two of those spaces that we want to be included in the second argument passed to grep
, so those must be quoted. So at the very least, we need:
grep --file=grep' 'pattern' 'file.txt *
Or:
grep --file=grep" "pattern\ file.txt *
(to show different quoting operators)
That is where we only quote the 2 characters that are special to the shell and that we don't want be treated as such. But we could also do:
'grep' '--file=grep pattern file.txt' *
And quote all the characters except those we want the shell to treat specially.
Quoting those non-special characters make no difference to the shell¹.
'g'r"ep" \-\-"file="'grep p'atte'\r\n\ file.txt *
Here alternating different forms of quotes would work the same.
Given that command and option² names rarely contain shell special characters, it is customary not to quote them. You rarely see people doing 'ls' '-l'
instead of ls -l
, so that grep --file="the value"
is a common sighting even if it makes no difference compared to grep "--file=the value"
or grep --file=the" value"
.
See How to use a special character as a normal one in Unix shells? for more details as to what characters are special and ways to quote them in various shells.
Now that still leaves a few problems with that command:
*
starts with with -
, it will be treated as an option*
expands all files regardless of their type. That includes directories, symlinks, fifos, devices. Chances are you only want to look in files of type regular (or may symlink to regular files)--file
is a GNUism. The standard equivalent is with -f
.*
expands to only one file, grep
will not include the file name along with the matching lines.*
doesn't match any file, in a few shells including bash (by default), a literal *
argument will be passed to grep
(and it will likely complain that it can't open a file by that name).So, here, you'd like want to use the zsh shell for instance, and write:
grep -f 'grep pattern file.txt' -- /dev/null *(-.)
Where:
-f
is used in place of --file
.--
marks the end of option so that no other argument after it be treated as one even if it starts with -
./dev/null
so grep
be passed at least 2 files, guaranteeing that it will always print the file name.*(-.)
so grep
only looks in regular files. If that doesn't match any, zsh
will abort with a no match error and not run grep.Since, we're passing /dev/null
to grep
, we could also add the N
glob qualifier (*(N-.)
) which would cause the glob to expand to nothing when there's no match instead of reporting an error, and grep
would only look inside /dev/null
(and silently fail).
¹ Beware quoting keywords in the shell syntax such as while
, do
, if
, time
even in part has an influence though as it stops the shell from recognising them as such; similarly, 'v'ar=value
would stop the shell from considering as a variable assignment as 'v'ar
is not a valid variable name (and quote handling is performed after parsing the syntax). Or your foo
alias won't be expanded if you write them \foo
or f'oo'
unless you also have aliases for those quoted forms (which few shells let you do)
² To the notable exception of -?
sometimes found on utilities inspired by Microsoft ones.
³ In the case of GNU grep
, that also applies to further arguments, not just the first as GNU grep
(and nowadays a few other GNU-like implementations) accepts options even after non-option arguments.
zsh
, try info zsh quoting
.
– Stéphane Chazelas
Dec 10 '23 at 18:10
ls -options
may be substituted as an alias (perhaps with additional options like --color=auto
), but 'ls' -options
will run /bin/ls
without considering any alias.
– Paul_Pedant
Dec 10 '23 at 21:59
var="foo bar"
vs"var=foo bar"
(is the latter even valid)? – Nor.Z Dec 10 '23 at 17:01yeah I tried with
// var1=foo bar // echo $var1 // // var2="foo bar" // echo $var2 // // "var3=foo bar" // echo $var3 // // var4=foo" "bar // echo $var4
and got
bash: var3=foo bar: command not found
– Nor.Z Dec 11 '23 at 18:51var="foo bar"
here is referring to variable assignment in bash script, a different topic than--var="foo bar"
.) – Nor.Z Dec 11 '23 at 18:54