Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I'm a total noob when it comes to unix/linux commands and I decided to read a book. 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? :(

share|improve this question
    
It would help if you also said what errors you get. –  EightBitTony Jul 30 '11 at 20:08
    
Apologies, added. –  Nassosdim Jul 30 '11 at 20:53
    
Turns out that ls with a directory as an argument results in file names and not the actual paths. Plus as @Caleb pointed out, parsing the output of ls is bad. Thank you for your answers! –  Nassosdim Jul 30 '11 at 21:41

4 Answers 4

up vote 6 down vote accepted

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 {} +
share|improve this answer

Parsing the results of ls can be a risky business as the output format varies. You'd be better doing something like this:

file /path/to/*.zip

Try a simpler command to demonstrate how process substitution works:

ls $(echo /usr/bin) 
share|improve this answer

Please try ls /usr/bin/* | grep zip to see what arguments you are feeding to file. I was baffled by the result a bit. On my Ubuntu system, the result is this:

/usr/bin/funzip
/usr/bin/gpg-zip
/usr/bin/mzip
/usr/bin/preunzip
/usr/bin/prezip
/usr/bin/prezip-bin
/usr/bin/unzip
/usr/bin/unzipsfx
/usr/bin/zip
/usr/bin/zipcloak
/usr/bin/zipgrep
/usr/bin/zipinfo
/usr/bin/zipnote
/usr/bin/zipsplit
funzip
gpg-zip
mzip
preunzip
prezip
prezip-bin
unzip
unzipsfx
zip
zipcloak
zipgrep
zipinfo
zipnote
zipsplit

Using ls /usr/bin/* | less we can see why we get that result: after all the files in /usr/bin, ls also lists the contents of the /usr/bin/X11 directory, and it lists them without the directory names prepended.

And when you feed these file names without a directory name to file, file cannot find these files and you get the "No such file or directory" error messages (as a side note: if you get error messages, please don't just say "I got errors", but post the error messages).

Like Caleb already posted, just use file /usr/bin/*zip*.

share|improve this answer
1  
I apologize about the formatting of my question, this is the first time that I'm posting and realized that I expressed it vaguely so I did the necessary editing. –  Nassosdim Jul 30 '11 at 20:31
    
@Nassosdim: thank you for making the question clearer (and a screenshot really isn't necessary). –  elmicha Jul 30 '11 at 20:44

The shell expands the * and turns the command into file $(ls /usr/bin/a /usr/bin/b /usr/bin/whatever | grep zip). You don't need to specify a asterisk in UNIX unless you want the shell to expand it to include everything before it passes it to the ls.

i.e. your shell replaces the * with every matching file before it runs the command, which is not how it works on Windows if you're used to that.

Read section 3.4.9 here.

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

is the right way to do it (right in the sense that you don't need the *, but see Caleb's answer for other issues and options). But note, that actually runs,

file /usr/bin/a /usr/bin/b /usr/bin/whatever

not

file /usr/bin/a
file /usr/bin/b
file /usr/bin/whatever

again, it doesn't do it the way windows does.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.