17

In many cases I use command substitution instead of xargs. For example rm $(ls) is the same as ls | xargs rm

What really are the differences between them?

I think one of the differences is that command substitution runs in subshell while xargs runs in current shell but I am not sure.

Please list the differences.

jokerdino
  • 41,320
Sinoosh
  • 2,031

1 Answers1

11

One difference is that when using command substitution instead of a pipe, the size of the data passed is limited by the size of the command buffer, so it is truncated in some cases with no warning. This also means that that the whole command output must be produced, and stored in memory, before it is passed to the next command, so for large outputs you can use much more memory than necessary.

Another issue with the first method is that the output is split on whitespace, so you can't handle filenames with spaces in them. xargs also is affected by the whitespace problem, but it can be fixed by changing the delimiter used. To properly handle filenames by the way, you would need to use the null byte as a delimiter in the second example.

A third issue is that globs are expanded, so if a file has asterisks or question marks in its name, there will be unexpected results.

You can find a nice discussion on the issue here: http://mywiki.wooledge.org/ParsingLs

The correct syntax would be

echo rm *

or if you must use xargs,

find . -maxdepth 1 -print0 | xargs -0 echo rm

Remove echo when the output looks correct.

  • Thank you,is command substitution runs in subshell while xargs runs in current shell but I am not sure true? – Sinoosh Oct 01 '16 at 10:03
  • 1
    @Sinoosh: xargs also runs in a subshell due to the pipe, unless you enable shopt -s lastpipe, in which case it will run in the current shell. I don't think that running in a subshell is a problem though in this case, since you are not changing any variables. – user000001 Oct 01 '16 at 10:04
  • I have two questions about your answer 1-can we say xargs pass argument to command one by one ? 2-for working with space in fie name touch "a b" && ls | xargs -0 rm can not delete the file "a b" what is my problem? – Sinoosh Oct 01 '16 at 10:11
  • for example i have tree files lke 1 ,2, 3 when i run ls | xargs rmit is same as rm 1;rm 2;rm 3 not like rm 1 2 3 – Sinoosh Oct 01 '16 at 10:16
  • 1
    @Sinoosh: To pass arguments one by one you can use the -l flag, like find . -maxdepth 1 -print0 | xargs -0 -l rm. For the second question, you can't use ls with xargs -0 because ls doesn't split the output on the null bute, but with newlines (which are valid in filenames BTW) – user000001 Oct 01 '16 at 10:18
  • @ user000001:Thank you.so in the first paragraph of your answer ,by default xargs should have the same behavior like command substitution.isn't it?they put ls output in front of rm as argument – Sinoosh Oct 01 '16 at 10:26
  • 1
    @Sinoosh: Not the first paragraph, but the second one: xargs without the -0 option suffers from the whitespace issue. – user000001 Oct 01 '16 at 10:28
  • 1
    Read man xargs, use echo for testing, not rm. xargs lets us exceed shell limits (some buffer is limited to 65K, a list of filenames is not). – waltinator Oct 01 '16 at 16:32
  • @waltinator: For the man suggestion, sure, but why specifically? did I say something wrong? Good point about removing rm from the example. I am a bit confused with your last point though, I believe that I covered that with the first paragraph of the answer. Let me know if you believe that something written in this answer is wrong. – user000001 Oct 01 '16 at 16:51
  • @usr0000001: can you give me a reference for first paragraph in your answer? – Sinoosh Oct 01 '16 at 17:09
  • 1
    @Sinoosh: I don't have a good reference handy, but you can type xargs --show-limits and you will see the limit that is set on your system, – user000001 Oct 01 '16 at 17:13
  • touch 'A file';echo rm * – waltinator Oct 09 '16 at 23:43