0

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.

muru
  • 72,889
Nor.Z
  • 101
  • (couldnt find any post online about this, they all talks about another topic like "$var") – Nor.Z Dec 10 '23 at 16:59
  • also, what is the difference between var="foo bar" vs "var=foo bar" (is the latter even valid)? – Nor.Z Dec 10 '23 at 17:01
  • 1
    They are identical if they are arguments to a command. The first one is a valid variable assignment, but the second one is not. The second one would be valid as the name of a command, but we usually don't use spaces in command names. – Kusalananda Dec 11 '23 at 07:47
  • @Kusalananda for >"The second one would be valid as the name of a command"
    yeah 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:51
  • @Kusalananda for >"They are identical if they are arguments to a command." I dont understand, could you give an example? \ (Note, to clarify: the var="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
  • 1
    If you referred to a variable assignment, then they are not identical. The case that I thought of first is when they are used as an argument to a command. – Kusalananda Dec 11 '23 at 20:51

1 Answers1

3

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:

  • space, which in the syntax of the shell (like in many languages) is used to delimit words in the syntax, and specifically for that line, arguments in simple commands (which is one of several and the main construct that the shell knows about).
  • the newline character at the end which is used (among other things) to delimit commands
  • *, 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:

  1. grep
  2. --file=grep pattern file.txt
  3. and following: the list of files in the current working directory.

So we do want:

  • space to be treated as a word delimiter in between those
  • newline to delimit that command
  • * 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:

  • if the first³ filename expanded by * 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.
  • If * expands to only one file, grep will not include the file name along with the matching lines.
  • If * 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 -.
  • we add /dev/null so grep be passed at least 2 files, guaranteeing that it will always print the file name.
  • We use *(-.) 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.

  • (The explaination is very detailed and talks about the intrinsic behaviour of quotes.
    (I didnt know the idea of "escaping the space".)) \ Is there any official documentation about this?
    – Nor.Z Dec 10 '23 at 18:07
  • @Nor.Z, check you shell's manual. For instance, with zsh, try info zsh quoting. – Stéphane Chazelas Dec 10 '23 at 18:10
  • (command not found: info, but anyways.) (the doc I found online https://www.gnu.org/software/bash/manual/html_node/Quoting.html https://info2html.sourceforge.net/cgi-bin/info2html-demo/info2html?(zsh)Quoting are very brief.) (anyways, thanks for the ex & explain.) – Nor.Z Dec 10 '23 at 18:19
  • Notably, quoting the first word in a command line (whether with , ' or ") will prevent Bash (and probably other shells) from treating the command as an alias. That is, 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
  • @Paul_Pedant, thanks. I've added that to the ¹ footnote. – Stéphane Chazelas Dec 11 '23 at 06:17