Команда для добавления строки к каждой строке?


36

Ищете что-то подобное? Любые идеи?

cmd | prepend "[ERRORS] "

[ERROR] line1 text
[ERROR] line2 text
[ERROR] line3 text
... etc

Есть ли способ установить это для всех команд в функции / скрипте bash?
Александр Миллс

Ответы:


39
cmd | while read line; do echo "[ERROR] $line"; done

имеет преимущество только использования встроенных команд bash, так что будет создаваться / уничтожаться меньше процессов, поэтому он должен быть на ощупь быстрее, чем awk или sed.

@tzrik указывает, что это может также сделать хорошую функцию bash. Определяя это как:

function prepend() { while read line; do echo "${1}${line}"; done; }

позволит использовать его как:

cmd | prepend "[ERROR] "

4
На самом деле это только уменьшает количество процессов на единицу. (Но это может быть быстрее, потому что не используются regexps ( sed) или даже разбиение строк ( awk).)
grawity

Кстати, мне было интересно узнать производительность, и вот результаты моего простого теста с использованием bash, sed и awk. Выкладываем около 1000 строк текста (вывод dmesg) в файл FIFO и затем читаем их следующим образом: pastebin.ca/1606844 Похоже, awk - победитель. Есть идеи почему?
Илья Закреевский

1
будьте осторожны при проведении таких временных тестов - попробуйте выполнить их во всех 6 различных порядках, а затем усредните результаты. Различные порядки для смягчения эффектов блочного кэша и усреднения для смягчения фоновых эффектов прерывания / планирования.
pjz

Этот вопрос помечен как "shell", а не как "bash".
Пятница,

1
Достаточно просто, чтобы обернуть его в функцию:function prepend() { while read line; do echo "${1}${line}"; done; }
tzrlk

46

Попробуй это:

cmd | awk '{print "[ERROR] " $0}'

ура


1
Это имеет тот недостаток, что «[ОШИБКА]» не может быть переменной, потому что все выражение должно быть в одинарных кавычках.
user1071136

4
awk -vT="[ERROR] " '{ print T $0 }'илиawk -vT="[ERROR]" '{ print T " " $0 }'
Тино

2
T="[ERROR] " awk '{ print ENVIRON["T"] $0 }'илиT="[ERROR]" awk '{ print ENVIRON["T"] " " $0 }'
Тино

Вы можете просто выйти из области видимости кавычек, чтобы разыменовать переменную: cmd | awk '{print "['$V]' " $0}'- это должно быть оценено один раз в начале, так что никаких потерь производительности не будет.
Роберт

13

С уважением к @grawity, я представляю его комментарий в качестве ответа, так как он кажется мне лучшим ответом.

sed 's/^/[ERROR] /' cmd

Почему это предпочтительнее решения bash?
user14645

1
Я полагаю, это зависит от вашей цели. Если ваша цель состоит в простом добавлении каждой строки в файле, это достигается с помощью очень небольшого количества символов с использованием очень знакомого инструмента. Я предпочитаю это сценарию с 10 строками. awkОдин-лайнер достаточно хорош, но я думаю , что все больше людей знакомы с sedчем awk. Сценарий bash хорош для того, что он делает, но кажется, что он отвечает на вопрос, который не был задан.
Эрик Уилсон

Ответ, который представил pjz, также является хорошим однострочником. Это не дополнительные программы, процессы и может работать немного быстрее.
user14645

3
sed X cmdчитает cmdи не выполняет его. Либо cmd | sed 's/^/[ERROR] /'или, sed 's/^/[ERROR] /' <(cmd)либо cmd > >(sed 's/^/[ERROR] /'). Но остерегайтесь последнего. Даже то , что это позволяет получить доступ к возвращаемому значению cmdв sedпрогонах в фоновом режиме, так что, скорее всего , вы увидите результат после ЦМДА готового. Хорошо для входа в файл, хотя. И обратите внимание, что, awkвероятно, быстрее, чем sed.
Тино

Ницца. Эта команда легко совмещается. alias lpad="sed 's/^/ /'", вместо ОШИБКИ я вставляю 4 пробела. Теперь для магического трюка: ls | lpad | pbcopyперед выводом ls будет добавлено 4 пробела, которые помечают его как Markdown для кода , что означает, что вы вставляете буфер обмена ( pbcopy захватывает его на macs) непосредственно в StackOverflow или любой другой контекст уценки. Не удалось получить ответ aliasот awk (с 1-й попытки), поэтому этот выигрывает. Решение while read также может создавать псевдонимы, но я нахожу это sed более выразительным.
JL Peyret

8

Я создал GitHub-репозиторий для тестирования скорости.

Результат:

  • В общем случае awkсамый быстрый. sedнемного медленнее и perlне намного медленнее, чем sed. По-видимому, все это высоко оптимизированные языки для обработки текста.
  • В особых случаях, когда доминируют вилки, запуск вашего скрипта как скомпилированного kshскрипта ( shcomp) может сэкономить еще больше времени на обработку. Напротив, он bashочень медленный по сравнению со скомпилированными kshскриптами.
  • awkКажется, создание статически связанного двоичного файла не стоит этих усилий.

Напротив, он pythonочень медленный, но я не проверял скомпилированный случай, потому что это обычно не то, что вы бы сделали в таком случае сценариев.

Тестируются следующие варианты:

while read line; do echo "[TEST] $line"; done
while read -r line; do echo "[TEST] $line"; done
while read -r line; do echo "[TEST]" $line; done
while read -r line; do echo "[TEST]" "$line"; done
sed 's/^/[TEST] /'
awk '{ print "[TEST] " $0 }'
awk -vT="[TEST] " '{ print T $0 }'
awk -vT="[TEST]" '{ print T " " $0 }'
awk -vT="[TEST]" 'BEGIN { T=T " "; } { print T $0 }'
T="[TEST] " awk '{ print ENVIRON["T"] $0 }'
T="[TEST]" awk '{ print ENVIRON["T"] " " $0 }'
T="[TEST]" awk 'BEGIN { T=ENVIRON["T"] " " } { print T $0 }'
perl -ne 'print "[TEST] $_"'

Два бинарных варианта одного из моих инструментов (хотя он не оптимизирован по скорости):

./unbuffered.dynamic -cp'[TEST] ' -q ''
./unbuffered.static -cp'[TEST] ' -q ''

Python буферизован:

python -uSc 'import sys
for line in sys.stdin: print "[TEST]",line,'

И Python небуферизованный:

python -uSc 'import sys
while 1:
 line = sys.stdin.readline()
 if not line: break
 print "[TEST]",line,'

awk -v T="[TEST %Y%m%d-%H%M%S] " '{ print strftime(T) $0 }'вывести метку времени
Tino


3

Я хотел решение, которое обрабатывало бы stdout и stderr, поэтому я написал prepend.shи поставил его на моем пути:

#!/bin/bash

prepend_lines(){
  local prepended=$1
  while read line; do
    echo "$prepended" "$line"
  done
}

tag=$1

shift

"$@" > >(prepend_lines "$tag") 2> >(prepend_lines "$tag" 1>&2)

Теперь я могу просто запустить prepend.sh "[ERROR]" cmd ..., чтобы добавить «[ERROR]» к выводу cmd, и при этом разделить stderr и stdout.


Я попробовал этот подход, но с этими >(оболочками происходило что-то, что я не мог решить. Казалось, что сценарий завершается, и вывод возвращается на терминал после возвращения приглашения, что было немного грязно. В конце концов я пришел с ответом здесь stackoverflow.com/a/25948606/409638
Роберт
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.