Как применить команду оболочки к каждой строке вывода команды?

192

Предположим, у меня есть вывод команды (например, ls -1):

a
b
c
d
e
...

Я хочу применить команду (скажем echo) к каждому, по очереди. Например

echo a
echo b
echo c
echo d
echo e
...

Какой самый простой способ сделать это в bash?

Алекс Будовски
источник
3
ls -1может быть примером здесь, но важно помнить, что не очень хорошо разбирать вывод ls. См .: mywiki.wooledge.org/ParsingLs
codeforester

Ответы:

217

Это, вероятно, самый простой в использовании xargs. В твоем случае:

ls -1 | xargs -L1 echo

-LФлаг обеспечивает вход правильно читать. Со страницы руководства xargs:

-L number
    Call utility for every number non-empty lines read. 
    A line ending with a space continues to the next non-empty line. [...]
Майкл Мрозек
источник
24
lsавтоматически делает -1в трубе.
Приостановлено до дальнейшего уведомления.
5
@ Денис, не похоже на это: ls | xargs -L2 echoи ls -1 | xargs -L2 echoдает два разных выхода. Бывшее все на одной линии.
Алекс Будовски
2
@ Алекс: Я получаю тот же вывод.
Приостановлено до дальнейшего уведомления.
6
xargsможет запускать только исполняемые файлы, а не функции оболочки или встроенные команды оболочки. Для первых лучшим решением, вероятно, является решение с readпетлей.
Пабук
12
Я хотел бы, чтобы этот ответ включал объяснение того, для чего -L1.
Wyck
164

Вы можете использовать базовую операцию prepend в каждой строке:

ls -1 | while read line ; do echo $line ; done

Или вы можете направить вывод в sed для более сложных операций:

ls -1 | sed 's/^\(.*\)$/echo \1/'
Трей Ханнер
источник
1
Команда sed, похоже, не работает: sh: cho: not found a sh: cho: not foundпохоже, она принимает eв echo команду sed или что-то в этом роде.
Алекс Будовски
+1 за whileцикл. cmd1 | while read line; do cmd2 $line; done, Или while read line; do cmd2 $line; done < <(cmd1)который не создает подоболочку. Это упрощенная версия вашей sedкоманды:sed 's/.*/echo &/'
Приостановлено до дальнейшего уведомления.
1
@ Алекс: изменить двойные кавычки на одинарные.
Приостановлено до дальнейшего уведомления.
5
Цитируйте "$line"цикл while, чтобы избежать разделения слов.
ignis
3
Попробуйте использовать, read -r lineчтобы не readиспортить экранированные символы. Например echo '"a \"nested\" quote"' | while read line; do echo "$line"; doneдает "a "nested" quote", который потерял свое вытекание. Если мы это сделаем, echo '"a \"nested\" quote"' | while read -r line; do echo "$line"; doneмы получим, "a \"nested\" quote"как ожидалось. См. Wiki.bash-hackers.org/commands/builtin/read
Warbo
9

Вы можете использовать цикл for :

для файла в *; делать
   echo "$ file"
сделано

Обратите внимание, что если рассматриваемая команда принимает несколько аргументов, то использование xargs почти всегда более эффективно, поскольку она вызывает только одну утилиту вместо нескольких раз.

Майкл Аарон Сафян
источник
1
Стоит описать правильное / безопасное использование xargs, т.е. printf '%s\0' * | xargs -0 ...- иначе, это довольно опасно с именами файлов с пробелами, кавычками и т. д.
Чарльз Даффи
8

Вы действительно можете использовать sed для этого, при условии, что это GNU sed.

... | sed 's/match/command \0/e'

Как это устроено:

  1. Заменяющее совпадение с командным совпадением
  2. При замене выполните команду
  3. Замените замещенную строку выводом команды.
Лукаш Данилюк
источник
Фантастика. Я забыл поставить команду e в конце, но сделал это, увидев ваш ответ, и это сработало. Я пытался добавить случайный идентификатор между 1000 и 15000, когда SED соответствует строке. cat /logs/lfa/Modified.trace.log.20150904.pw | sed -r 's/^(.*)(\|006\|00032\|)(.*)$/echo "\1\2\3 - ID `shuf -i 999-14999 -n 1`"/e'
sgsi
6
for s in `cmd`; do echo $s; done

Если cmd имеет большой вывод:

cmd | xargs -L1 echo
Марсело Кантос
источник
7
Если cmdв выводе есть пробелы, первый потерпит неудачу.
Приостановлено до дальнейшего уведомления.
mywiki.wooledge.org/DontReadLinesWithFor , mywiki.wooledge.org/BashPitfalls запись № 1
Чарльз Даффи,
2

Xargs не с обратными слешами, цитаты. Это должно быть что-то вроде

ls -1 |tr \\n \\0 |xargs -0 -iTHIS echo "THIS is a file."

опция xargs -0:

-0, --null
          Input  items are terminated by a null character instead of by whitespace, and the quotes and backslash are
          not special (every character is taken literally).  Disables the end of file string, which is treated  like
          any  other argument.  Useful when input items might contain white space, quote marks, or backslashes.  The
          GNU find -print0 option produces input suitable for this mode.

ls -1завершает элементы символами новой строки, поэтому trпереводит их в нулевые символы.

Этот подход примерно в 50 раз медленнее, чем итерация вручную for ...(см. Ответ Майкла Аарона Сафяна) (3,55 с против 0,066 с). Но для других команд ввода, таких как locate, find, чтение из файла ( tr \\n \\0 <file) или аналогичных, вы должны работать с xargsэтим.

phil294
источник
1

Лучший результат для меня:

ls -1 | xargs -L1 -d "\n" CMD
Андрей Пандович
источник
Лучше, но не идеально. find . -mindepth 1 -maxdepth 1 -print0 | xargs -0 commandбудет обрабатывать случаи, когда вывод ls -1неоднозначен; используйте, -printf '%P\0'а не -print0если вы не хотите ведущий ./на каждом.
Чарльз Даффи
1

мне нравится использовать gawk для запуска нескольких команд в списке, например

ls -l | gawk '{system("/path/to/cmd.sh "$1)}'

однако побег от убегающих персонажей может стать немного волосатым.

Крис
источник