What is the difference between grep apple file and grep "apple" file?

Solution 1:

The quotation marks have an effect on what characters your shell considers special and to have a syntactic meaning. In your example this doesn't make a difference because apple contains no such characters.

But consider another example: grep apple tree file will search for the word apple in the files tree and file, whereas grep "apple tree" file will search the word apple tree in the file file. The quotation marks tell bash, that the word space in "apple tree" does not start a new parameter but shall be part of the current parameter. grep apple\ tree file would produce the same result, because \ tells bash to disregard the special meaning of the following character and treat it literally.

Solution 2:

Double quotes allow evaluation, single quotes prevent evaluaton, no quotes allow wildcard expansion, when used on command line. As contrived examples:

[user@work test]$ ls .
A.txt B.txt C.txt D.cpp

# The following is the same as writing echo 'A.txt B.txt C.txt D.cpp'
[user@work test]$ echo *
A.txt B.txt C.txt D.cpp

[user@work test]$ echo "*"
*

[user@work test]$ echo '*'
*

# The following is the same as writing echo 'A.txt B.txt C.txt'
[user@work test]$ echo *.txt
A.txt B.txt C.txt

[user@work test]$ echo "*.txt"
*.txt

[user@work test]$ echo '*.txt'
*.txt

[user@work test]$ myname=is Fred; echo $myname
bash: Fred: command not found

[user@work test]$ myname=is\ Fred; echo $myname
is Fred

[user@work test]$ myname="is Fred"; echo $myname
is Fred

[user@work test]$ myname='is Fred'; echo $myname
is Fred

Understanding the way quotes work is pivotal to understanding Bash. For example:

# for will operate on each file name separately (like an array), looping 3 times.
[user@work test]$ for f in $(echo *txt); do echo "$f"; done;
A.txt
B.txt
C.txt

# for will see only the string, 'A.txt B.txt C.txt' and loop just once.
[user@work test]$ for f in "$(echo *txt)"; do echo "$f"; done;
A.txt B.txt C.txt

# this just returns the string - it can't be evaluated in single quotes.
[user@work test]$ for f in '$(echo *txt)'; do echo "$f"; done;
$(echo *txt)

You can use single quotes to pass a command via a variable. Single quotes will prevent evaluation. Double quotes will evaluate.

# This returns three distinct elements, like an array.
[user@work test]$ echo='echo *.txt'; echo $($echo)
A.txt B.txt C.txt

# This returns what looks like three elements, but it is actually a single string.
[user@work test]$ echo='echo *.txt'; echo "$($echo)"
A.txt B.txt C.txt

# This cannot be evaluated, so it returns whatever is between quotes, literally.
[user@work test]$ echo='echo *.txt'; echo '$($echo)'
$($echo)

You can use single quotes inside double quotes and you can use double quotes inside double quotes, but double quotes inside single quotes should not be done (without escaping them) will not be evaluated, they will be interpreted literally. Single quotes within single quotes should not be done (without escaping them).

You'll need to gain a thorough understanding of quotes to use Bash effectively. Very important!

As a general rule, I don't use quotes if I want Bash to expand something into elements (like an array), I use single quotes for literal strings that are not to be altered, and I make liberal use of double quotes for variables that are likely to return any type of string. This is to ensure that spaces and special characters will be preserved.