Игнорировать пустой результат для xargs

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

ls /mydir/*.txt | xargs chown root

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

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

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

Ответ 1

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

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

Ответ 2

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

Ответ 3

Пользователи не-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 для деталей.

Ответ 4

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

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

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

или перенаправить его stderr в null (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 {} ';'

Ответ 5

В 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 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]