Как игнорировать команды xargs, если ввод stdin пуст? - PullRequest
157 голосов
/ 28 ноября 2011

Рассмотрим эту команду:

ls /mydir/*.txt | xargs chown root

Цель состоит в том, чтобы сменить владельцев всех текстовых файлов в mydir на root

Проблема заключается в том, что если .txt файлов нетв mydir тогда xargs выдает ошибку, говорящую, что путь не указан.Это безвредный пример, потому что выдается ошибка, но в некоторых случаях, например, в сценарии, который мне нужно здесь использовать, в качестве текущего каталога предполагается пустой путь.Поэтому, если я запускаю эту команду с /home/tom/, то если нет результата для ls /mydir/*.txt и все файлы в /home/tom/ меняют своих владельцев на root.

Так как я могу заставить xargs игнорировать пустой результат

Ответы [ 6 ]

257 голосов
/ 28 ноября 2011

Для GNU xargs вы можете использовать опцию -r или --no-run-if-empty:

--no-run-if-empty
-r
Если стандартный ввод не содержит небланков, не запускайте команду. Обычно команда запускается один раз, даже если нет ввода. Эта опция является расширением GNU.

12 голосов
/ 27 сентября 2013

Пользователи не-GNU xargs могут использовать -L <#lines>, -n <#args>, -i и -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

Имейте в виду, что xargs разбивает строку в пробелах, но доступны цитирование и экранирование; RTFM для деталей.

Кроме того, как упоминает Дорон Бехар, этот обходной путь не переносим, ​​поэтому могут потребоваться проверки:

$ 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
9 голосов
/ 28 ноября 2011

man xargs говорит --no-run-if-empty.

8 голосов
/ 14 мая 2016

С точки зрения xargs, вы можете использовать -r, как предлагается, однако это не поддерживается BSD xargs.

Так что в качестве обходного пути вы можете передать некоторый дополнительный временный файл, например:

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

или перенаправить его stderr в ноль (2> /dev/null), например,

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

Другой лучший подход - перебирать найденные файлы, используя цикл while:

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

См. Также: Игнорировать пустые результаты для xargs в Mac OS X


Также обратите внимание, что ваш способ изменения разрешений не очень хорош и не рекомендуется. Определенно, вы не должны анализировать вывод команды ls (см .: Почему вы не должны анализировать вывод команды ls ). Особенно, когда вы запускаете команду от имени пользователя root, поскольку ваши файлы могут состоять из специальных символов, которые могут интерпретироваться оболочкой или представлять файл с пробелом около /, тогда результаты могут быть ужасными.

Поэтому вы должны изменить свой подход и использовать вместо него команду find, например,

find /mydir -type f -name "*.txt" -execdir chown root {} ';'
2 голосов
/ 15 мая 2017

В OSX: переопределение Bash xargs, связанное с аргументом -r, поместите его, например, в $HOME/bin и добавьте его к 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 $@
0 голосов
/ 28 ноября 2018

Это поведение GNU xargs, которое можно подавить с помощью -r, --no-run-if-empty.

Вариант * BSD для * BSD имеет такое поведение по умолчанию, поэтому -r не требуется. Начиная с FreeBSD 7.1 (выпущена в январе 2009 г.), аргумент -r принимается (ведьма ничего не делает) по соображениям совместимости.

Лично я предпочитаю использовать longopts в скриптах, но поскольку * BSD xargs не использует longopts, просто используйте "-r", и xargs будет действовать так же на * BSD и в системах linux

xargs в MacOS (в настоящее время MacOS Mojave), к сожалению, не поддерживает аргумент "-r".

...