I have a basic question about ls
command.
Suppose in a directory I have 4 files named
run
run1
running
run.sh
So, if i do: ls -l|grep run*
then I get no result.
But if i do ls -l|grep run.*
then I get run.sh
as a result.
However I expected grep
to list all of the files in both the cases.
Could you make me understand what is going on behind scenes?
As long as I understand, the "*" is expanded by the shell before executing the command itself, so your grep will try to catch a string with all the file names! On the other hand, grep expects a regular expression, so the "*" is not interpreted as you expect.
The direct solution would be:
$ ls -l run*
Or, if you want to use grep, then scape the "*" and provide a regular expression:
$ ls -l|grep run.\*
$ ls -l|grep 'run.*'
This is because the asterisk is special to the shell and gets expanded. To avoid this, you have to quote the regex for grep to see it unexpanded:
ls -l|grep 'run*'
And note that this is not what you want, because 'run*' as an regexp means 'ru followed by any number of n
'. This will list also files named rubber
and so on. To list files that match a shell glob pattern (which is different from an regexp), why not simply use
ls -l run*
ls -l run.*
and avoid the useless grep process entirely?
Before the shell even runs grep
, it searches through your command for any unquoted file globbing characters, and performs filename expansion on those arguments.
So when you enter this command:
ls -l | grep run*
the shell uses the pattern run*
to search for files in the current directory, and finds run
, run1
, running
and run.sh
. It then rewrites the grep
command with those arguments:
ls -l | grep run run1 running run.sh
which causes grep
to search run1
, running
and run.sh
for the string run
.
As noted, the solution is to quote the argument to grep
so the shell does not try to perform filename expansion on it:
ls -l | grep 'run.*'