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 have the following files in a directory:

-rw-r--r-- 1 smsc sys  46 Apr 22 12:09 bills.50.1.3G.MO.X.20120422120453.Z
-rw-r--r-- 1 smsc sys  28 Apr 22 12:15 bills.50.1.3G.MO.X.20120422120953.Z
-rw-r--r-- 1 smsc sys  46 Apr 22 12:20 bills.50.1.3G.MO.X.20120422121453.Z
-rw-r--r-- 1 smsc sys  46 Apr 22 12:25 bills.50.1.3G.MO.X.20120422121953.Z

Where the fifth column is the file's size. I wish to delete all files which size is 46. In order to filter out these files I used the following command:

ls -ltr | awk '$5 ~ /46/ {print $0}'

Which works fine. But now I want to delete all files which were filtered out, so I add the following to the above command:

ls -ltr | awk '$5 ~ /46/ {print $0}' | xargs rm

However it gives me the following error:

rm: invalid option -- w

It seems that I have to use find over ls so I will get the output in the below format:

./bills.50.1.3G.MO.X.20120421050453.Z
./bills.50.1.3G.MO.X.20120421154953.Z
./bills.50.1.3G.MO.X.20120419133452.Z

But then I have no way to filter the files by its parameters. How this task could be done?

share|improve this question
3  
Btw, find could also do this, i.e. something like find . -maxdepth 1 -size 46c -delete (at least GNU find can) –  sr_ Apr 22 '12 at 9:51
    
@sr_ That's an interesting option. I will keep that in mind. Thanks! –  Eugene S Apr 22 '12 at 9:55
3  
Please don't parse ls output. @sr_ You should post that as an answer. –  jw013 Apr 22 '12 at 9:55
    
@jw013 Thanks for the link! I'm familiar with this issue, however I couldn't figure out how to avoid using ls here. –  Eugene S Apr 22 '12 at 9:58
    
@EugeneS ... except sr_ has already shown you exactly how to do this safely with find. Why don't you just use the suggested command? It is never necessary to use ls for manipulating files (at least I have yet to run into such a situation) since find and shell globbing cover nearly all cases. –  jw013 Apr 22 '12 at 10:05

3 Answers 3

up vote 6 down vote accepted

You have two bugs:

  1. You are comparing for a size that contains 46; you want it to be equal to 46.

  2. You are printing the entire line, when you want only the filename.

And an additional issue: what is the point of -ltr to sort the ls output when you aren't using the sort order?

You want to do something like

ls -l | awk '$5 == "46" {print $9}' | xargs rm

Except you don't want to do that, because while it might be safe at the moment, parsing ls output is unreliable. Use an appropriate tool such as

find . -maxdepth 1 -size 46c -delete # requires GNU find

(Doing this portably is more annoying, since POSIX find doesn't have -maxdepth or -size that operates in units other than blocks. Better to write a script in a Perl/Python/Ruby/etc. that can use a proper directory scan that won't get in trouble with special characters in filenames.)

share|improve this answer
    
Thank you for your answer and mentioning ALL the bugs! You're absolutely right, it works fine now. Regarding the -ltr, I guess it's just a bad habit. –  Eugene S Apr 22 '12 at 9:53
2  
Also note the comment above about parsing ls output, which I let pass; it is really not a good idea, due to the possibility of spaces and other special characters in filenames. –  geekosaur Apr 22 '12 at 9:56
    
I know that but I couldn't think about a way to avoid this. If you can edit your answer and describe a way to perform this task without using ls I will be grateful. Thanks again! –  Eugene S Apr 22 '12 at 10:01
    
I would advise using $NF (always referring to the last field) instead of $9. –  jippie Apr 22 '12 at 10:02
1  
@jippie, that's still not going to work if there's whitespace in the name. –  geekosaur Apr 22 '12 at 10:03

In zsh, to remove all files of size 26 in the current directory (first line) or the current directory and its subdirectories (second line), use the L glob qualifier:

rm *(L26)
rm **/*(L26)

With GNU or FreeBSD/NetBSD/OSX find:

find . -name . -o -type d -prune -o -type f -size 26c -exec rm {} +
find . -type f -size 26c -exec rm {} +

Portably:

find . -name . -o -type d -prune -o -type f -exec sh -c '[ $(wc -c <"$0") -eq 26 ] && rm -- "$0"' {} \;
find . -type f -exec sh -c '[ $(wc -c <"$0") -eq 26 ] && rm -- "$0"' {} \;
share|improve this answer

find is the best option here. you can use stat to find the file size without using ls

for file in *; do
  (( $(stat -c %s "$file") == 46 )) && rm "$file"
done
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.