Игнорировать пустые результаты для xargs в Mac OS X

Код для моего сайта использует этот фрагмент кода для автоматического развертывания на сервере (Ubuntu).

cmd = 'cd ' + checkout_dir + ' && ' + svn_command + " st | awk '{print $2}' | grep -v ^deploy | tac | xargs -r" + svn_command + " revert -R && " + svn_command + ' up -r ' + options.revision

То, что делает эта команда, это cd в каталоге checkout, запускает svn status, а затем извлекает имя файла ($2), удаляет каталог deploy и все его файлы из списка (я не хочу вернуть его). Если аргументов нет, он не запускает команду svn revert, иначе это произойдет.

К сожалению, xargs -r не работает на моей машине (Mac OS X 10.8). Поэтому я застрял здесь, может ли кто-нибудь помочь?

Ответ 1

Действительно, реализация BSD xargs не имеет флага -r (--no-run-if-empty). Версия GNU в Linux имеет его.

Здесь один из способов обойти проблему таким образом, который работает как в Linux, так и в BSD:

... | (grep -v ^deploy || echo :) | xargs svn revert

grep ... || echo : в середине будет генерировать строку с : в ней, если вывод grep пуст. Это немного грязно, потому что xargs все равно выполнит команду svn revert :. Если ваш репозиторий не содержит файл :, тогда это не должно иметь никакого эффекта, поэтому оно может быть приемлемым. : может быть чем угодно, если в вашем репозитории нет такого файла.

Наконец, как отметил @tripleee, grep ... || echo : должен быть заключен в (...), потому что:

|| имеет более высокий приоритет, чем |, и таким образом завершает (первый) конвейер.

Ваш код выглядит как строка Python. Это будет более читаемым следующим образом:

kwargs = {
  'svn': svn_command,
  'dir': checkout_dir,
  'revno': options.revision,
}
cmd = "cd {dir} && {svn} st | awk -v r=1 '$2 ! ~ /deploy/ {{ print $2; r=0 }} END {{ r || print \":\" }}' | xargs {svn} revert && {svn} up -r {revno}".format(**kwargs)

Я внес некоторые изменения в ваш оригинал:

  • Переместил логику grep внутри awk, как предложил @tripleee. Обратите внимание, что поскольку grep hack больше не нужен, больше нет необходимости обертывать внутри (...)
  • Отбросил tac, поскольку я не вижу в нем точки
  • Выпало -r из svn revert, потому что я не думаю, что вам это нужно

Ответ 2

Не очень, но, надеюсь, обходной путь.

cmd = 'cd ' + checkout_dir + ' && ' + 
    'r=$(' svn_command + ' st | ' +
        "awk '$2 !~ /^deploy/{print $2}' | tac) && " +
    'test "$r" && ' +
    svn_command + ' revert -R $r && ' +
    svn_command + ' up -r ' + options.revision

Я не уверен, что tac необходим или полезен. Я реорганизовал первый grep в Awk script для эффективности и эстетических соображений.

Чтобы решить общую проблему "my xargs отсутствует -r", суть решения заключается в преобразовании

stuff | xargs -r cmd

в

var=$(stuff)
test "$var" && cmd $var

Без кавычек $var будет работать только в том случае, если он не содержит имен файлов с пробелами или другими сюрпризами; но тогда голые кости xargs без расширений GNU страдают от одной и той же проблемы.

Ответ 3

-r для xargs не работает в OS X, так как это расширение GNU. Что касается обходного пути, вы должны указать некоторый фиктивный файл, который можно проанализировать с помощью xargs или не вызывать команду, когда нет аргументов (которые можно проверить ранее).

Метод с использованием временного файла (или любого другого заполнителя):

some_cmd ... | xargs svn revert $(mktemp)

Использование условия в оболочке:

files=$(cd checkout_dir && ... | tac)
if [ -n "$files" ]; then
    echo $files | xargs && ...
fi

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

Ответ 4

Bash повторная реализация xargs с аргументом -r:

#!/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]