Thursday, September 23, 2010

Avoid that grep command in grep output

Hello everyone,

I'm planning to send everyday at least a message to the list with something interesting about Linux. This is the first one.

One week ago I bought the book "Official Ubuntu Server Book, The (2nd Edition)". Being a command line fan I went directly to the section "Cool tips and tricks". I started reading and I found out that the explanation of the first trick is in a certain way wrong. 

The idea behind the trick is to avoid the appearance of grep in resulting list for a command like:

janeiros@harlie:~$ ps ax | grep bash
13235 tty1     S      0:00 -bash
13256 tty1     S+     0:00 /bin/bash /usr/bin/startx
16911 pts/0    Ss+    0:00 -bash
17005 pts/1    Ss     0:00 -bash
17152 pts/1    S+     0:00 grep --color=auto bash

The authors suggest a "trick" running the command in this form:

janeiros@harlie:~$ ps ax | grep [b]ash
 How come when I try the trick is OK the first time and wrong at the second one?

janeiros@harlie:~$ ps ax | grep [b]ash
13235 tty1     S      0:00 -bash
13256 tty1     S+     0:00 /bin/bash /usr/bin/startx
16911 pts/0    Ss+    0:00 -bash
17005 pts/1    Ss     0:00 -bash

You can see grep is gone.

And now is back:

janeiros@harlie:~$ ps ax | grep [b]ash
13235 tty1     S      0:00 -bash
13256 tty1     S+     0:00 /bin/bash /usr/bin/startx
16911 pts/0    Ss+    0:00 -bash
17005 pts/1    Ss     0:00 -bash
17185 pts/1    S+     0:00 grep --color=auto bash

Of course, I did something in between that provoked the change! What was it? Let see if somebody find out what was. The authors' explanation for the trick is not exact!

The explanation is right there at the command line!

If nobody finds out I'll tell you later today.

By the way there is an alternative to the trick but it's longer.

The outputs of the commands weren't edited at all.

And by the way, something the author didn't tell: In Linux we can avoid the whole grep filtering with a simple ps -C

janeiros@harlie:~$ ps -C bash
  PID TTY          TIME CMD
13235 tty1     00:00:00 bash
17798 pts/0    00:00:00 bash

Of course, the output is a little bit different than the one from ps ax.

Hello,

The cause that is causing the trick to fail the second time is the existence in the current directory of a file with the name bash.

You can test it by going to a directory where you have write permissions, run the command the first time (ps -ef | grep [b]ash), then run the command touch bash and finally run ps -ef | grep [b]ash to see it fail.

The reason the command fails is due to something call "file expansion"at the shell level.

In the book, the authors say, and I quote:

"This works because of the power of regular expressions. When I surround the first character with brackets, I’m telling grep to search within a character class."

That's partially true 'cause they are forgetting that the shell is in the middle doing some "massage" to the argument, that massage is called "file name expansion." When the shell sees an argument with one of the special characters * or ? or [ it considers it as a pattern and replaces it with an alphabetical sorted list of filenames. If there is no filename that fits the pattern then the argument is left without change (depending of the shell option nullglob).

So, if you try to use the trick but in the directory exists a file that fits the pattern then the shell is gonna do the substitution and feed the command (grep) with the filename, bash in this case, instead of [b]ash. One solution to avoid the pattern scanning is using double quotes around the argument.

Below is a small script that let you test the whole thing:

#!/bin/bash

echo "$1"       # Don't forget to type the double quotes!

##END##

You can run the script with the [b]ash without the file bash in the directory and with the file in it and see the argument that the script is receiving from the shell.

Have a good night.
-- 
J. E. Aneiros
GNU/Linux User #190716 en http://counter.li.org
perl -e '$_=pack(c5,0105,0107,0123,0132,(1<<3)+2);y[A-Z][N-ZA-M];print;'
PK fingerprint: 5179 917E 5B34 F073 E11A  AFB3 4CB3 5301 4A80 F674

No comments: