Как передать вывод команды в качестве нескольких аргументов другой команде
Я хочу передать каждый вывод команды как множественный аргумент второй команде, например:
grep "pattern" input
возвращает:
file1
file2
file3
и я хочу скопировать эти выводы, например:
cp file1 file1.bac
cp file2 file2.bac
cp file3 file3.bac
Как я могу сделать это за один раз? Что-то вроде:
grep "pattern" input | cp $1 $1.bac
4 ответа
Ты можешь использовать xargs
:
grep 'pattern' input | xargs -I% cp "%" "%.bac"
Ты можешь использовать $()
интерполировать вывод команды. Итак, вы могли бы использовать kill -9 $(grep -hP '^\d+$' $(ls -lad /dir/*/pid | grep -P '/dir/\d+/pid' | awk '{ print $9 }'))
если бы ты хотел.
Для полноты я также упомяну подстановку команд и объясню, почему это не рекомендуется:
cp $(grep -l "pattern" input) directory/
(Синтаксис обратного удара cp `grep -l "pattern" input` directory/
примерно эквивалентен, но он устарел и громоздок; не используйте это.)
Это не удастся, если выход из grep
создает имя файла, которое содержит пробел или метасимвол оболочки.
Конечно, это хорошо, если вы точно знаете, какие имена файлов grep
может произвести и проверил, что ни один из них не является проблематичным. Но для производственного сценария не используйте это.
В любом случае, для сценария ОП, где вам нужно обратиться к каждому совпадению отдельно и добавить к нему расширение, xargs
или же while read
альтернативы превосходят в любом случае.
В худшем случае (имеется в виду проблемные или неуказанные имена файлов) передайте совпадения в подоболочку через xargs
:
grep -l "pattern" input |
xargs -r sh -c 'for f; do cp "$f" "$f.bac"; done' _
... где, очевидно, сценарий внутри for
Цикл может быть сколь угодно сложным.
В идеальном случае команда, которую вы хотите выполнить, достаточно проста (или универсальна), чтобы вы могли просто передать ей произвольно длинный список имен файлов. Например, GNU cp
имеет -t
возможность облегчить это использование xargs
(-t
опция позволяет вам поместить каталог назначения первым в командной строке, чтобы вы могли поместить столько файлов, сколько вам нужно в конце команды):
grep -l "pattern" input | xargs cp -t destdir
который будет расширяться в
cp -t destdir file1 file2 file3 file4 ...
на столько матчей, сколько xargs
может поместиться в командной строке cp
повторяется столько раз, сколько нужно, чтобы передать все файлы cp
, (К сожалению, это не соответствует сценарию OP; если вам нужно переименовать каждый файл во время копирования, вам нужно передать только два аргумента в cp
вызов: имя исходного файла и имя файла назначения, в который оно будет скопировано.)
Другими словами, если вы используете синтаксис подстановки команд и grep
создает действительно длинный список совпадений, вы рискуете наткнуться ARG_MAX
и ошибки "Список аргументов слишком длинный"; но xargs
специально избегать этого, вместо этого копируя столько аргументов, сколько он может безопасно передать cp
в то время, и работает cp
несколько раз, если это необходимо.
В дополнение к Крису Джестеру-Юнгу хороший ответ, я бы сказал, что xargs
также является хорошим решением для этих ситуаций:
grep ... `ls -lad ... | awk '{ print $9 }'` | xargs kill -9
сделаю это. Все вместе:
grep -hP '^\d+$' `ls -lad /dir/*/pid | grep -P '/dir/\d+/pid' | awk '{ print $9 }'` | xargs kill -9
#!/bin/bash
for f in files; do
if grep -q PATTERN "$f"; then
echo cp -v "$f" "${f}.bac"
fi
done
файлы могут быть *.txt или *.text, что в основном означает файлы, оканчивающиеся на *.txt или *text, или заменять чем-то, что вам нужно / нужно, конечно, замените PATTERN своим. Удалите эхо, если результат вас устраивает. Для рекурсивного решения взгляните на параметр оболочки bash globstar