19

From my understanding, the following should mean exactly the same:

ls -1 | xargs file {}
ls -1 | xargs -I{} file {}

if -I option is not specified, it is default to -I{}.

I want to list all files in the current directory and run file command on each of them. Some have spaces in their names. However, I noticed the difference. See below:

$ ls -1
Hello World
$ ls -1 | xargs file {}
{}:    ERROR: cannot open `{}' (No such file or directory)
Hello: ERROR: cannot open `Hello' (No such file or directory)
World: ERROR: cannot open `World' (No such file or directory)
$ ls -1 | xargs -I{} file {}
Hello World: directory

With -I{} explicitly specified, blanks in file names are treated as expected.

foresightyj
  • 315
  • 1
  • 3
  • 7
  • 4
    "if -I option is not specified, it is default to -I{}" -- this is incorrect, at least with GNU xargs. – jjlin Oct 07 '13 at 01:18
  • 1
    I understand my mistake. I should specify either `xargs file` or `xargs -I{} file {}`. Shouldn't be `xargs file {}`. I guess when explicitly specifying -I{}, the bash will treat it as `file "Hello World"`. Without -I{}, it is treated as `file Hello World`. – foresightyj Oct 07 '13 at 04:01
  • 1
    (1) This discussion has little to do with the shell (bash).  (2) It’s good that you know the `-1` option to `ls`, but it’s on by default when the output of `ls` is a file or a pipe, so you don’t need it here.  (3) You’re confusing `-I` with the deprecated `-i` (lower case `I`) option.  `-ifoo` is equivalent to `-Ifoo`, but plain `-i` is equivalent to `-I{}`.  But use `-I{}`.  (4) If you really want to do what you say you want to do, why not just say `file *`? – Scott - Слава Україні Oct 08 '13 at 00:36
  • @Scott My original intent was to write ` find . -name "*.mov" | xargs -I{} file {}` to find all mov files recursively and run `file` on them. But I came up with a simpler example using `ls`. You are right. For that, `file *` is the best. – foresightyj Oct 08 '13 at 02:23
  • 1
    **find** has `-exec`. – Cristian Ciupitu Oct 22 '13 at 11:03
  • @CristianCiupitu I knew that. But I always forget the {} \ after -exec so I always prefer to go with xargs. – foresightyj Oct 23 '13 at 00:46
  • @CristianCiupitu Actually I started using -exec these days. It is handy sometimes and I don't find \; to end the -exec part confusing anymore. Thanks for mentioning that. – foresightyj Apr 29 '14 at 00:50

1 Answers1

26

The -I requires a defined placeholder. The -i option will assume {} is the placeholder. This was where I found any assumption of {} in man xargs at least on Cygwin and CentOS.

xargs called without either option does not need the placeholder and it just appends the STDIN to the end of the arguments.

Just add echo to your examples to see what xargs is doing:

$ ls -1
Hello World/

Your example mistakenly uses {}:

$ ls -1 | xargs echo file {}
file {} Hello World/

so the file cmd sees args of {} Hello World and errors.

If you want to explicitly use {} in your xargs call:

$ ls -1 | xargs -I{} echo file {}
file Hello World/

Or with No placeholder:

$ ls -1 | xargs echo file
file Hello World/

xargs as called above does not need the {}. It appends the STDIN to the end of the command without a placeholder. The use of {} usually means you want the STDIN somewhere in the middle of the cmd to execute, as in:

$ ls -1 | xargs -i mv {} /path/to/someplace/.
johnnyB
  • 1,290
  • 10
  • 8