Як застосувати команду оболонки до кожного рядка команди команди?


192

Припустимо, у мене є деякий вихід з команди (наприклад ls -1):

a
b
c
d
e
...

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

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

Який найпростіший спосіб зробити це в баші?


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
@Dennis, не схоже на це: ls | xargs -L2 echoі ls -1 | xargs -L2 echoдай два різні виходи. Перші - всі на одній лінії.
Олексій Будовський

2
@ Алекс: Я отримую такий же вихід.
Призупинено до подальшого повідомлення.

6
xargsможе запускати лише виконувані файли, а не функції оболонки або вбудовані команди оболонки. Для першого найкращим рішенням є, мабуть, рішення з readциклом.
pabouk

12
Я хотів би, щоб ця відповідь включала пояснення того -L1, для чого.
Вік

164

Ви можете використовувати основні операції з поданням у кожному рядку:

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схоже, що це прийняття eecho - це команда sed чи щось таке.
Олексій Будовський

+1 для whileциклу. cmd1 | while read line; do cmd2 $line; done. Або, while read line; do cmd2 $line; done < <(cmd1)що не створює підшару. Це спрощена версія вашої sedкоманди:sed 's/.*/echo &/'
Призупинено до подальшого повідомлення.

1
@Alex: змінити подвійні лапки на одиничні.
Призупинено до подальшого повідомлення.

5
Цитуйте "$line"цикл у той час, щоб уникнути розбиття слів.
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

Ви можете використовувати цикл :

для файлу в *; робити
   відлуння "$ файл"
зроблено

Зауважте, що якщо відповідна команда приймає декілька аргументів, то використання 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


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,55s проти 0,066s). Але для інших команд введення, таких як пошук, пошук, читання з файлу ( tr \\n \\0 <file) або подібного, вам доведеться працювати з xargsподібним чином.


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)}'

проте втеча персонажів, що втечуть, може стати трохи волохатим.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.