54

Suppose that I want to delete all files in a folder that are greater than 1 MB.

$ find . -size +1M | xargs -0 rm

This will not delete files that have space in their names. So I want it to quote all arguments it sends to rm. If find gives it Some report.docx it should pass "Some report.docx" to rm.

How can I do that?

Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
Kshitiz Sharma
  • 8,585
  • 21
  • 59
  • 75
  • 3
    You should read this http://mywiki.wooledge.org/UsingFind?highlight=%28xargs%29 before doing anything with `xargs`. Also as the wiki suggests, don't use `xargs` without passing the `-print0` to `find`. – Valentin Bajrami Dec 10 '14 at 08:38
  • 1
    More generally, see [Why does my shell script choke on whitespace or other special characters?](http://unix.stackexchange.com/questions/131766/why-does-my-shell-script-choke-on-whitespace-or-other-special-characters) – Gilles 'SO- stop being evil' Dec 10 '14 at 20:52
  • 3
    Use `xargs -d$'\n'` to limit the delimiter to only new lines (and not spaces; this wouldn't [process quotes etc. specially](https://unix.stackexchange.com/q/38148/4319) -- I've checked on a GNU system) -- the answer given in http://stackoverflow.com/a/33528111/94687 – imz -- Ivan Zakharyaschev May 11 '17 at 12:31
  • Related: [Why is looping over find's output bad practice?](//unix.stackexchange.com/q/321697) – Stéphane Chazelas Feb 20 '21 at 07:10
  • See [Search for text files where two different words exist (any order, any line)](//unix.stackexchange.com/a/67820) for the length you have to go to transform file paths into the format expected by `xargs` without the `-0` option. – Stéphane Chazelas Feb 20 '21 at 07:19

5 Answers5

48

I had a similar requirement and ended up using the -I switch to have a placeholder and I was able to quote it.

find . -size +1M | xargs -I {} rm "{}"
dee-see
  • 589
  • 4
  • 4
  • With `xargs -I`, you'll still have problems with filenames that contain single quotes, double quotes, backslashes or newlines (leading blanks are also a problem, but with the output of `find .`, that can only occur for filenames that contain ``). – Stéphane Chazelas Feb 20 '21 at 07:28
  • 8
    Quoting that second `{}` makes absolutely no difference here. That's just quoting for the shell, and that `{}` doesn't need quoting in most shells. As far as `xargs` is concerned, it receives an argument containing `{}` (the two characters `{` and `}`) for both the `{}` after `-I` and `"{}"` after `rm`. It is exactly the same as `xargs -I {} rm {}` or `xargs -I "{}" rm '{}'` – Stéphane Chazelas Feb 20 '21 at 07:43
  • Just escape the quotes like this: `find . -size +1M | xargs -I {} rm \"{}\"` – lots0logs Aug 08 '22 at 22:37
  • It simply don't work. It gets only the first element (tested with `tar`). See https://unix.stackexchange.com/a/635479/41011 instead – Marco Sulla Mar 02 '23 at 08:50
25

As you're already using that non-standard 1M, chances are your find implementation also supports -delete. So, simply use:

find . -type f -size +1M -delete

Where supported, that's by far the safest and most efficient.

If you insist on using xargs and rm with find, just add -print0 in your command:

find . -type f -size +1M -print0 | xargs -r0 rm -f --

(-print0 and -0 are non-standard, but pretty common. -r (to avoid running rm at all if find doesn't find anything) is less common, but if your xargs doesn't support it, you can just omit it, as rm with -f won't complain if called without argument).

The standard syntax would be:

find . -type f -size +1048576c -exec rm -f -- {} +

Other way:

find . -type f -size +1M -execdir rm -f -- {} +

(that's safer than -exec/xargs -0 and would work with very deep directory trees (where full file paths would end up larger than PATH_MAX), but that's also non-standard, and runs at least one rm for each directory that contains at least one big file, so would be less efficient).

From man find on a GNU system:

-print0

True; print the full file name on the standard output, followed by a null character (instead of the newline character that -print uses). This allows file names that contain newlines or other types of white space to be correctly interpreted by programs that process the find output. This option corresponds to the -0 option of xargs.

Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
αғsнιη
  • 40,939
  • 15
  • 71
  • 114
  • 6
    This solves the problem but actually quoting the arguments would be useful in other cases. So I'll hold on for other answers. +1 for a simple solution though. – Kshitiz Sharma Dec 10 '14 at 08:33
  • 3
    @KshitizSharma No, you do not want to pass `"Some report.docx"` to `rm`, unless the file name contains the quotes. What you want is to pass `Some report.docx` unmolested to `rm`. KasiyA's answer (now) shows the general way to do that with `find`. [KasiyA: sorry for the wrong ping earlier.] – Gilles 'SO- stop being evil' Dec 10 '14 at 21:22
  • @Gilles Correct. In bash I generally quote the string to pass it unmolested. So by quoting what I meant was to send filename as a single argument to rm instead of being split up into `$0` and `$1` etc. – Kshitiz Sharma Dec 11 '14 at 06:36
  • On OSX I do `xargs -0` instead of `xargs -r0`. – Rolf Sep 13 '18 at 15:56
14

Option -0 of xargs means that output from pipe is interpreted as null terminated items. In such case you also need to create input for the pipe with find ... -print0.

jimmij
  • 46,064
  • 19
  • 123
  • 136
5

Here is a simple way to do it that does not rely on special flags in find. It works for any input into xargs. Just use sed to wrap each line in quotes.

find . -size +1M | sed 's/.*/"&"/' | xargs ls -l

You can replace ls -l with rm after you are satisfied it works.

Asim Jalis
  • 151
  • 1
  • 4
  • With `sed 's/.*/"&"/'` quoting, you'll still have problems with filenames that contain `"` or newline characters or sequences of bytes that don't form valid characters in the locale. – Stéphane Chazelas Feb 20 '21 at 07:08
  • Stéphane: Good catch regarding the `-delete` and double quotes in file name. I have removed the `-delete`. Regarding escaping quotes it needs a more robust sed expression. Do you have any suggestions on how to fix this? I’ll think about it. – Asim Jalis Feb 21 '21 at 14:50
  • See for instance: [Search for text files where two different words exist (any order, any line)](//unix.stackexchange.com/a/67820) – Stéphane Chazelas Feb 21 '21 at 14:51
2

Actually just reiterating an existing comment to the question so that it appears under answers, in my case I was using locate to find filenames containing a certain string:

Ivan Zakharyaschev wrote: Use xargs -d$'\n' to limit the delimiter to only new lines (and not spaces; this wouldn't process quotes etc. specially -- I've checked on a GNU system) -- the answer given in stackoverflow.com/a/33528111/94687

So for example using Ivan's suggestion,

$ locate -i example_string | xargs -d$'\n' md5sum
Andrew Richards
  • 213
  • 2
  • 5