By HyderA


2011-11-28 13:46:53 8 Comments

Consider this command:

ls /mydir/*.txt | xargs chown root

The intention is to change owners of all text files in mydir to root

The issue is that if there are no .txt files in mydir then xargs thows an error saying there is no path specified. This is a harmless example because an error is being thrown, but in some cases, like in the script that i need to use here, a blank path is assumed to be the current directory. So if I run that command from /home/tom/ then if there is no result for ls /mydir/*.txt and all files under /home/tom/ have their owners changed to root.

So how can I have xargs ignore an empty result?

6 comments

@arielCo 2013-09-26 21:08:58

Users of non-GNU xargs may take advantage of -L <#lines>, -n <#args>, -i, and -I <string>:

ls /empty_dir/ | xargs -n10 chown root # chown executed every 10 args or fewer
ls /empty_dir/ | xargs -L10 chown root # chown executed every 10 lines or fewer
ls /empty_dir/ | xargs -i cp {} {}.bak # every {} is replaced with the args from one input line
ls /empty_dir/ | xargs -I ARG cp ARG ARG.bak # like -i, with a user-specified placeholder

Keep in mind that xargs splits the line at whitespace but quoting and escaping are available; RTFM for details.

Also, as Doron Behar mentions, this workaround isn't portable so checks may be needed:

$ uname -is
SunOS sun4v
$ xargs -n1 echo blah < /dev/null
$ uname -is
Linux x86_64
$ xargs --version | head -1
xargs (GNU findutils) 4.7.0-git
$ xargs -n1 echo blah < /dev/null
blah

@Nicolas Raoul 2014-07-21 08:24:00

Does not seem to answer the question?

@arielCo 2014-07-21 18:48:23

@Nicolas: it's the way to prevent execution if your version of xargs does not support the --no-run-if-empty switch and attempts to run the command argument without STDIN input (this is the case in Solaris 10). The versions in other unixes may just ignore an empty list (e.g. AIX).

@carlosayam 2017-03-30 02:58:43

Yes! On MAC OSX, echo '' | xargs -n1 echo blah does nothing; whereas echo 'x' | xargs -n1 echo blah prints "blah".

@Luís Bianchin 2018-07-17 11:12:08

For both GNU and BSD/OSX support I end up using something like: ls /mydir/*.txt | xargs -n 1 -I {} chown root {}, as this answer suggests.

@arielCo 2018-07-17 15:41:08

@LuísBianchin, -I and -i already imply -n 1 since they substitute one argument per placeholder. Also your placeholder can be any string (after shell processing); {} is just the default for -i.

@Luís Bianchin 2018-07-25 12:26:16

Great point @arielCo! In that case, one can do: ls /mydir/*.txt | xargs -n 1 chown root {}

@Doron Behar 2019-05-30 15:41:47

It should be noted that echo '' | xargs -n1 echo blah prints blah with GNU's xargs.

@arielCo 2019-05-30 22:56:50

@DoronBehar yikes, so this isn't portable. Thanks - I'll edit accordingly.

@Mirko Steiner 2018-11-28 14:42:47

This is a behaviour of GNU xargs which can be supressed by using -r, --no-run-if-empty.

The *BSD variant of xargs has this behavoir on default, so -r is not needed. Since FreeBSD 7.1 (released in january 2009) an -r argument is accepted (witch does nothing) for compatiblity reasons.

I personally prefer using longopts in scripts but since the *BSD xargs does not uses longopts just use "-r" and xargs will act the same on *BSD an on linux systems

xargs on MacOS (currently MacOS Mojave) sadly don't supports the "-r" argument.

@kenorb 2016-05-13 21:23:02

In terms of xargs, you can use -r as suggested, however it's not supported by BSD xargs.

So as workaround you may pass some extra temporary file, for example:

find /mydir -type f -name "*.txt" -print0 | xargs -0 chown root $(mktemp)

or redirect its stderr into null (2> /dev/null), e.g.

find /mydir -type f -name "*.txt" -print0 | xargs -0 chown root 2> /dev/null || true

Another better approach is to iterate over found files using while loop:

find /mydir -type f -name "*.txt" -print0 | while IFS= read -r -d '' file; do
  chown -v root "$file"
done

See also: Ignore empty results for xargs in Mac OS X


Also please note that your method of changing the permissions isn't great and it's discouraged. Definitely you shouldn't parse output of ls command (see: Why you shouldn't parse the output of ls). Especially when you're running your command by root, because your files can consist special characters which may be interpreted by the shell or imagine the file having a space character around /, then the results can be terrible.

Therefore you should change your approach and use find command instead, e.g.

find /mydir -type f -name "*.txt" -execdir chown root {} ';'

@Adrian 2017-12-27 18:52:46

Sorry, I don't understand how the while IFS= read -r -d '' file is valid. Can you explain?

@rcomblen 2017-05-15 06:39:29

On OSX: Bash reimplementation of xargs dealing with the -r argument, put that e.g. in $HOME/bin and add it to the PATH:

#!/bin/bash
stdin=$(cat <&0)
if [[ $1 == "-r" ]] || [[ $1 == "--no-run-if-empty" ]]
then
    # shift the arguments to get rid of the "-r" that is not valid on OSX
    shift
    # wc -l return some whitespaces, let's get rid of them with tr
    linecount=$(echo $stdin | grep -v "^$" | wc -l | tr -d '[:space:]') 
    if [ "x$linecount" = "x0" ]
    then
      exit 0
    fi
fi

# grep returns an error code for no matching lines, so only activate error checks from here
set -e
set -o pipefail
echo $stdin | /usr/bin/xargs [email protected]

@Sven Marnach 2011-11-28 13:49:10

For GNU xargs, you can use the -r or --no-run-if-empty option:

--no-run-if-empty
-r
If the standard input does not contain any nonblanks, do not run the command. Normally, the command is run once even if there is no input. This option is a GNU extension.

@JonnyJD 2013-06-25 15:55:16

I honestly searched in google first and found this (but wouldn't have asked without reading the manual). I kind of thought this hould be the default behavior, not needing a switch to enable it.

@wedi 2015-06-24 15:50:39

Unfortunately this does not work on a Mac. Neither the short nor the long option.

@edk750 2016-07-20 19:41:11

@wedi - OSX has BSD userspace, so this sort of thing will happen frequently. You can work around it by using homebrew to install the GNU fileutils.

@Mitar 2016-12-04 20:38:30

You mean brew install findutils. findutils, not fileutils.

@Alexander Mills 2017-05-27 00:38:02

so like: foo | xargs --no-run-if-empty bar --baz

@Trejkaz 2018-10-09 04:58:25

On macOS, not providing the flag results in not running the command anyway, so I guess it just isn't needed.

@thiton 2011-11-28 13:48:09

man xargs says --no-run-if-empty.

@jasonleonhard 2017-01-27 23:17:37

If you are on OSX it will not. edk750 has explained this in their comment if you are curious why.

Related Questions

Sponsored Content

20 Answered Questions

[SOLVED] How can I get `find` to ignore .svn directories?

  • 2010-02-22 22:09:17
  • John Kugelman
  • 85725 View
  • 227 Score
  • 20 Answer
  • Tags:   linux find bash grep svn

35 Answered Questions

[SOLVED] How do I parse command line arguments in Bash?

13 Answered Questions

[SOLVED] How to echo shell commands as they are executed

31 Answered Questions

[SOLVED] How do I prompt for Yes/No/Cancel input in a Linux shell script?

14 Answered Questions

[SOLVED] How do I set a variable to the output of a command in Bash?

14 Answered Questions

[SOLVED] How to reload .bash_profile from the command line?

13 Answered Questions

[SOLVED] Make xargs execute the command once for each line of input

  • 2008-10-13 22:31:24
  • Readonly
  • 183401 View
  • 343 Score
  • 13 Answer
  • Tags:   xargs

10 Answered Questions

[SOLVED] Running multiple commands with xargs

  • 2011-08-05 15:22:14
  • Dagang
  • 165043 View
  • 313 Score
  • 10 Answer
  • Tags:   bash xargs

3 Answered Questions

[SOLVED] How to use > in an xargs command?

  • 2009-05-10 18:51:37
  • Jesse Shieh
  • 58060 View
  • 160 Score
  • 3 Answer
  • Tags:   bash redirect xargs

2 Answered Questions

replacement on xargs variable returns empty string

Sponsored Content