Shell – file $(ls /usr/bin/* | grep zip) command gives me errors. What’s wrong

command-substitutionlsshellwildcards

I decided to read a book about Linux/Unix.
I've reached a chapter where they try to explain how to pass the output of commands as expansions to the shell.

The following command

file $(ls /usr/bin/* | grep zip)

gives me the error "cannot open "file name (No such file or directory)".
On the contrary, when I do

file $(ls | grep zip)

everything appears normally, no matter the folder. Why? Is it some kind of argument restriction?

If I do this

file $(ls Pictures/ | grep jpg)

I get the following:

1234.jpg: ERROR: cannot open 1234.jpg (No such file or directory)

while moving right in the directory that I want to list

nassosdim@ubuntu:~$ cd Pictures/

nassosdim@ubuntu:~/Pictures$ file $(ls | grep jpg)

prints out the result of file

1234.jpg: JPEG image data, JFIF standard 1.01

Can someone explain to me what's going on? 🙁

Best Answer

The error is coming because bash is trying to expand the * in your argument to ls using glob pattern matching. It will always through such an error when the glob doesn't match the pattern. The * then gets passed to ls as a real asterisk, which doesn't match a file either so ls will error!

There are several bits about what you are doing that are redundant. I know you are trying to learn a construct, but let's break down some of the bits of what you just accomplished and how it could have been done.

  1. ls /usr/bin is sufficient to list files in the bin directory, you don't need the star at all.
  2. You should never parse the output of ls at all, it is ambiguous and potentially dangerous.
  3. You could get the same effect without grep by doing ls /usr/bin/*zip*, but the ls is still redundant, you can just pass the glob expantion to file: file /usr/bin/*zip*

Lastly, find is usually a better tool for finding files. You could do what you are after like this:

$ find /usr/bin -iname '*zip*' -exec file {} +
Related Question