Perintah untuk menambahkan string ke setiap baris?

36

Mencari yang seperti ini? Ada ide?

cmd | prepend "[ERRORS] "

[ERROR] line1 text
[ERROR] line2 text
[ERROR] line3 text
... etc
pengguna14645
sumber
Apakah ada cara untuk mengatur ini untuk semua perintah dalam fungsi / skrip bash?
Alexander Mills

Jawaban:

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

memiliki keunggulan hanya menggunakan bash builtin sehingga lebih sedikit proses akan dibuat / dihancurkan sehingga harus menjadi sentuhan lebih cepat daripada awk atau sed.

@ tzrik menunjukkan bahwa itu mungkin juga membuat fungsi bash yang bagus. Mendefinisikannya seperti:

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

akan memungkinkannya digunakan seperti:

cmd | prepend "[ERROR] "
pjz
sumber
4
Ini sebenarnya hanya mengurangi jumlah proses per satu. (Tapi itu mungkin akan lebih cepat karena tidak ada regexps ( sed) atau bahkan string yang membelah ( awk) digunakan.)
grawity
BTW, saya ingin tahu tentang kinerja dan di sini adalah hasil dari tolok ukur sederhana saya menggunakan bash, sed dan awk. Mendorong sekitar 1000 baris teks (dmesg output) ke file FIFO dan kemudian membacanya seperti ini: pastebin.ca/1606844 Sepertinya awk adalah pemenangnya. Ada ide mengapa?
Ilya Zakreuski
1
hati-hati menjalankan tes waktu seperti itu - coba jalankan di semua 6 pesanan yang berbeda dan kemudian rata-rata hasilnya. Perintah berbeda untuk mengurangi efek cache dan rata-rata untuk mengurangi efek gangguan / penjadwalan latar belakang.
pjz
Pertanyaan ini ditandai "shell", bukan "bash".
fiatjaf
1
Cukup mudah untuk membungkusnya dalam suatu fungsi juga:function prepend() { while read line; do echo "${1}${line}"; done; }
tzrlk
46

Coba ini:

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

Tepuk tangan

Ilya Zakreuski
sumber
1
Ini memiliki kekurangan bahwa "[KESALAHAN]" tidak bisa menjadi variabel, karena seluruh ekspresi harus dalam tanda kutip tunggal.
user1071136
4
awk -vT="[ERROR] " '{ print T $0 }'atauawk -vT="[ERROR]" '{ print T " " $0 }'
Tino
2
T="[ERROR] " awk '{ print ENVIRON["T"] $0 }'atauT="[ERROR]" awk '{ print ENVIRON["T"] " " $0 }'
Tino
Anda bisa keluar dari ruang lingkup tanda kutip untuk referensi variabel: cmd | awk '{print "['$V]' " $0}'- ini harus dievaluasi sekali pada awal, jadi tidak ada overhead kinerja.
robert
13

Dengan segala penghargaan kepada @grawity, saya mengirimkan komentarnya sebagai jawaban, karena sepertinya ini jawaban terbaik bagi saya.

sed 's/^/[ERROR] /' cmd
Eric Wilson
sumber
Mengapa ini lebih disukai daripada solusi bash?
user14645
1
Saya kira itu tergantung pada tujuan Anda. Jika tujuan Anda adalah dengan hanya menambahkan ke setiap baris dalam file, ini mencapai tujuan itu dengan sangat sedikit karakter, menggunakan alat yang sangat akrab. Saya jauh lebih suka itu untuk skrip bash 10 baris. The awksatu-kapal ini cukup bagus, tapi saya pikir bahwa lebih banyak orang yang akrab dengan seddari awk. Skrip bash baik untuk fungsinya, tetapi tampaknya ia menjawab pertanyaan yang tidak ditanyakan.
Eric Wilson
Jawaban yang diajukan pjz juga bagus sekali. Itu bukan program tambahan, proses dan mungkin berjalan sedikit lebih cepat.
user14645
3
sed X cmdmembaca cmddan tidak menjalankannya. Entah cmd | sed 's/^/[ERROR] /'atau sed 's/^/[ERROR] /' <(cmd)atau cmd > >(sed 's/^/[ERROR] /'). Namun waspadalah terhadap yang terakhir. Bahkan bahwa ini memungkinkan Anda untuk mengakses nilai kembali dari cmdyang sedberjalan di latar belakang, sehingga kemungkinan Anda melihat output setelah cmd selesai. Bagus untuk masuk ke file. Dan perhatikan bahwa awkmungkin lebih cepat dari sed.
Tino
Bagus. Perintah ini mudah alias. alias lpad="sed 's/^/ /'". bukannya ERROR saya memasukkan 4 spasi utama. Sekarang, untuk trik sulap: ls | lpad | pbcopyakan menambahkan ls output dengan 4 spasi yang menandainya sebagai Penurunan harga untuk kode , yang berarti bahwa Anda menempelkan clipboard ( pbcopy ambil, pada mac) langsung ke StackOverflow atau konteks penurunan harga lainnya. Tidak bisa aliasdengan awk jawaban (pada tanggal 1 try) jadi ini salah satu menang. Solusi sambil membaca juga alias-mampu, tetapi saya menemukan sed ini lebih ekspresif.
JL Peyret
8

Saya membuat repositori GitHub untuk melakukan beberapa tes kecepatan.

Hasilnya adalah:

  • Dalam kasus umum, awkini tercepat. sedsedikit lebih lambat dan perltidak lebih lambat dari sed. Rupanya, semua itu adalah bahasa yang sangat optimal untuk pemrosesan teks.
  • Dalam situasi yang sangat khusus, di mana garpu mendominasi, menjalankan skrip Anda sebagai skrip yang dikompilasi ksh( shcomp) dapat menghemat waktu pemrosesan lebih banyak. Sebaliknya, bashmati lambat dibandingkan dengan kshskrip yang dikompilasi .
  • Membuat biner yang terhubung secara statis untuk dikalahkan awktampaknya tidak sepadan dengan usaha.

Sebaliknya pythonsangat lambat, tetapi saya belum menguji case yang dikompilasi, karena biasanya bukan apa yang akan Anda lakukan dalam case scripting seperti itu.

Varian berikut diuji:

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] $_"'

Dua varian biner dari salah satu alat saya (meskipun tidak dioptimalkan untuk kecepatan):

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

Python buffered:

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

Dan Python tidak ditemukan:

python -uSc 'import sys
while 1:
 line = sys.stdin.readline()
 if not line: break
 print "[TEST]",line,'
Tino
sumber
awk -v T="[TEST %Y%m%d-%H%M%S] " '{ print strftime(T) $0 }'untuk mengeluarkan stempel waktu
Tino
5
cmd | sed 's/.*/[ERROR] &/'
Dijeda sampai pemberitahuan lebih lanjut.
sumber
16
sed 's/^/[ERROR] /'
grawity
3

Saya menginginkan solusi yang menangani stdout dan stderr, jadi saya menulis prepend.shdan meletakkannya di jalur saya:

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

Sekarang saya bisa menjalankan prepend.sh "[ERROR]" cmd ..., untuk menambahkan "[KESALAHAN]" ke output cmd, dan masih memiliki stderr dan stdout terpisah.

Aprotim
sumber
Saya mencoba pendekatan ini tetapi ada sesuatu yang terjadi dengan >(subkulit yang tidak bisa saya selesaikan. Tampaknya skrip itu selesai, dan hasilnya tiba ke terminal setelah prompt kembali yang agak berantakan. Saya akhirnya datang dengan jawaban di sini stackoverflow.com/a/25948606/409638
robert