Ubuntu – Difference in two types of command that uses grep

command linegrep

So, here we have two commands:

cat /var/log/dpkg.log | grep " \install\ "
cat /var/log/dpkg.log | grep install

Why are the outputs of these commands different? And can you please explain the first command a little. Thank you in advance.

Best Answer

The first part of your two commands (cat FILENAME) is always the same and just prints the specified file's content to the STDOUT stream. I will not explain it any further.

The point of our interest is the grep part.

Syntax of grep:

grep [OPTIONS] PATTERN [FILE...]

You can pass grep some options to tweak its behaviour (e.g set the used RegEx flavour or control the output formatting), but those are not used in your case.

The next single argument must be the pattern to match, where a regular expression ("RegEx") or a fixed string (if grep is called with the -F option) is required. In your example, this is the install or "\ install\ " part. I will explain it in the next paragraph.

After that, you specify the source of the data to match. This can either be a file name, or nothing. In the second case, grep will read from the STDIN stream (standard input: normally what you type with the keyboard), where you pipe (|) the output from the previous command.

How to correctly pass the "PATTERN" argument?

The pattern parameter must be a single argument. That means, you can't just pass several words or anything containing spaces or shell special characters here, because spaces are treated as argument separators in Bash and probably every other shell as well, and shell special characters such as ; will break the command.

But you have two options to include spaces into the pattern to match anyway:

  • Put the entire string to match in single ('...') or double ("...") quotes. This way, the shell parses the entire quotes-enclosed string as one argument and passes it on to grep.

  • Escape every space in the pattern with a backslash (\). That means, you write a backslash before every space which you don't want to be seen as argument separator by the shell. But note that if you want to have an actual backslash in the pattern, you must escape it as well, by writing another backslash before it.

If we now analyse the difference of your two grep command examples, we see the difference in what they match:

cat /var/log/dpkg.log | grep "\ install\ "

This matches the pattern install. (Note the leading and trailing space!)

Here we see both methods: double quotes around the entire pattern and backslash-escaping the spaces inside it. It's superfluous to be honest, one would have been enough. Although it doesn't hurt in this case, you should not do this and decide on one method. Usually I would recommend to use quotation marks, as it's easier to read.

cat /var/log/dpkg.log | grep install

This matches the pattern install. (No surrounding spaces.)

Here the pattern only consists of the word install, nothing else. No spaces.

Difference between your commands:

As I said, the first of your examples only matches the word install when it is surrounded by spaces. It would not match if e.g. there's a full stop or any other character directly before or after it instead. It would also not match the word directly at the beginning or end of a line.

The second example does not care about any spaces before or after the word install. It also matches at line beginnings and endings as well as if it is surrounded by any punctuation. It even matches if there is a word containing this letter sequence anywhere, e.g. "uninstall", "reinstall" or "installation" too!

Example with correct/useful backslash escaping:

As in the example you provided the backslashes are superfluous, here the same example without quotes, but with backslash escaping only instead:

cat /var/log/dpkg.log | grep \ install\ 

Or if you want to match the string "I like Ubuntu" in a file /home/you/path with spaces/textfile without using quotes, you would do that like this:

grep I\ like\ Ubuntu /home/you/path\ with\ spaces/textfile

You see that you must escape spaces in path- or filenames as well - or quote them. The line above is equal to the line below:

grep "I like Ubuntu" "/home/you/path with spaces/textfile"
Related Question