In a unix terminal, if I do:
egrep "Stuff" $(ls *.txt)
The result is obvious. But what does
$(ls *.txt) do on its own? I see I can replicate the effect by
egrep "Stuff" *.txt
So what is
Ask Ubuntu is a question and answer site for Ubuntu users and developers. It only takes a minute to sign up.Sign up to join this community
$(ls *.txt) do"
$(ls *.txt) gathers a list of file names and then mangles them. Do not use this.
ls is intended for human-readable output. As one of ls's maintainers wrote in response to why
ls would not offer a
If we were to do this then this is the interface we would use. However
lsis really a tool for direct consumption by a human, and in that case further processing is less useful. For futher processing,
find(1) is more suited. [emphasis added]
In other words, use of
ls for anything other than display to humans is just not supported.
ls maintainers, for example, recently changed the default output format to something they though more human-friendly without warning. So, follow their advice: if you are going to do further processing of file names, use
find instead of
$(...) is command substitution. One consequence of using command substitution is that trailing newline characters are removed. If the last file name in the list happens to contain trailing newlines, they will be removed.
$(...) is not double-quoted, the text it produces will be subject to:
The result of both of these is further mangling of the file names.
not to mention the issues with file names starting with
- because of the missing
-- (for both
egrep as Ubuntu's
egrep being the GNU implementation accepts options even after non-option arguments unless
POSIXLY_CORRECT is in the environment).
also, if any of those
txt files were of type directory,
ls would list their content instead of themselves.
and if there's no
txt files, the output of
ls will be empty (though you'll see an error about a missing
*.txt file) and as
egrep will receive no file argument, it will look for Stuff in its standard input (and seemingly hang).
Let's create 4 files containing
Stuff in our directory:
$ echo Stuff | tee file1 file2 'a b c.txt' 'f* .txt' Stuff $ ls -Q "a b c.txt" "file1" "file2" "f* .txt"
Now, let's run the egrep command:
$ egrep "Stuff" $(ls *.txt) grep: a: No such file or directory grep: b: No such file or directory grep: c.txt: No such file or directory file1:Stuff file2:Stuff f* .txt:Stuff grep: .txt: No such file or directory
Observe that we get 4 error messages about nonexistent files. This is due to word splitting. The result also shows matches with two files,
file2 that should not have been searched because they don't end with
.txt. This is because of _pathname expansion`.
The correctly written command produces two successful matches and no errors:
$ egrep -- "Stuff" *.txt a b c.txt:Stuff f* .txt:Stuff
egrep -- "Stuff" *.txt
grep -E -- "Stuff" *.txt
grep -E -e Stuff -- *.txt
This will work with any file name and has none of the limitations of the
$( ) runs the command
ls *.txt and returns the
STDOUT of the command.
What this particular usage is, is a newbie programming mistake on at least three levels:
egrep 'Stuff' *.txtworks, like you said, except for files named something like
lsoutput for program input is unwise. See reasons
$( ls *.txt)mishandles filenames with spaces and other funny characters.
find . -maxdepth 1 -type f -iname '*.txt' -print0 | xargs -0 egrep 'Stuff'is a more bullet-resistant way.
A contrived example of shell glob expansion/confusion, in response to @8bittree is:
$ /bin/ls -l -b total 36 -rw------- 1 w3 walt 2 Aug 11 13:41 A\ \\012\ File.txt -rw------- 1 w3 walt 2 Aug 11 13:42 A\ \n\ File.txt -rw------- 1 w3 walt 2 Aug 11 13:40 A\ File.txt $ grep "STUFF" $(ls *.txt) grep: A: No such file or directory grep: \012: No such file or directory grep: File.txt: No such file or directory grep: A: No such file or directory grep: File.txt: No such file or directory grep: A: No such file or directory grep: File.txt: No such file or directory $ find . -maxdepth 1 -type f -iname '*.txt' -print0 | xargs -0 egrep 'Stuff' $ find . -maxdepth 1 -type f -iname '*.txt' -print0 | xargs -0 egrep '1' ./A File.txt:1
Wow, for a moment I thought you were wondering about
`$(ls *.txt)` (with
$(..) plus backquotes)
As the question has already been answered, I will just warn you about one thing. Here is a sequence of commands to show you why it can be dangerous:
touch 'rm -f *.txt'
ls *.txtwill be run, then redirect the result to STDOUT. This result,
rm -f *.txtwill be now executed because of the backquotes.
rm -f *.txt
I hope you understand, - as a demonstration just run the previous commands in an empty directory, so you won't break anything.
$( ... )
Is a command substitution, it assigns the output of one or more commands as an input to another command. The old syntax is:
` ... `.
The different is that your commands will get run in different shells:
echo $BASHPID $( echo $BASHPID ) 5718 7138
It actually invokes a subshell:
echo $BASH_SUBSHELL $( echo $BASH_SUBSHELL ) 0 1
This question seems to have little utility in the real world but,
A better way to get a usable result is:
echo $(ls *.txt)
This will list all files with a .txt extension. To find text files that contain a particular string,
egrep -l "Stuff" *.txt
For more information on search tools, do a