9

xargs and basename work together as I would expect:

$ printf '%s\n' foo/index.js bar/index.js baz/index.js | xargs basename
index.js
index.js
index.js

xargs and dirname, though, appear not to work together:

$ printf '%s\n' foo/index.js bar/index.js baz/index.js | xargs dirname
usage: dirname path

I would expect

foo
bar
baz

as output. What am I missing?

I'm on Darwin 18.2.0 (macOS 10.14.3).

Jeff Schaller
  • 66,199
  • 35
  • 114
  • 250
davidchambers
  • 193
  • 1
  • 5
  • See also: [Stack Overflow: Why using dirname in find command gives dots for each match?](https://stackoverflow.com/a/11158279/4561887) – Gabriel Staples Jul 25 '23 at 02:54

2 Answers2

11

dirname on macOS only takes a single pathname, whereas basename is able to work with multiple pathnames. It is however safest to call basename with a single pathname so that it does not accidentally try to remove the the second pathname from the end of the first, as in

$ basename some/file e
fil

When calling these utilities from xargs you may ask xargs to run the utility with a single newline-delimited string at a time:

printf '%s\n' some arguments | xargs -I {} basename {}

or,

printf '%s\n' some arguments | xargs -I {} dirname {}

You could also use xargs -L 1 utility rather than xargs -I {} utility {}.

Kusalananda
  • 320,670
  • 36
  • 633
  • 936
  • 4
    I fundamentally misunderstood `xargs`. I was under the impression that its default behaviour was to execute the utility once *for each* input line. Thanks for correcting my understanding. – davidchambers Mar 02 '19 at 14:12
  • @davidchambers: If you call `xargs -n1`, you might get the behavior you were expecting. You can compare the output of `ls | xargs echo "TEST"` to `ls | xargs -n1 echo "TEST"` as a test. – Eric Duminil Mar 15 '21 at 07:53
  • 1
    @EricDuminil Note that using `-n` and `-I` divides the input into arguments in _very_ different ways. With `-n 1`, you'll get one argument per whitespace-delimited word (possibly several such words per line of input), while with `-I {}` you'll get one argument per _line_ of input. Test `xargs -n 1 echo hello` and `xargs -I {} echo hello {}` and give it a single line saying `1 2 3`. – Kusalananda Mar 15 '21 at 07:56
  • @Kusalananda: Excellent, thanks. `echo "1 2 3\n4 5 6"` is a good test input to see the difference between `-n1` and `-I` – Eric Duminil Mar 15 '21 at 08:22
4

dirname only takes 1 argument; while the GNU/coreutils version of dirname can take more than 1 argument, that is a non-standard extension:

SYNOPSIS

    dirname string