Bagaimana cara menggunakan sed untuk memanipulasi output streaming terus menerus?

13

Saya menyusun presentasi untuk audiens non-teknis. Saya memiliki program yang berjalan di bash yang menampilkan aliran nilai yang berkelanjutan, beberapa di antaranya penting. Saya ingin menyoroti hasil penting saat ditampilkan sehingga audiens dapat mengetahui frekuensi mereka. Masalahnya adalah saya tidak bisa sedberoperasi pada aliran yang sedang berjalan. Ini berfungsi dengan baik jika saya meletakkan hasilnya dalam file, seperti pada:

cat output.txt | sed "s/some text/some text bolded/"

Tetapi jika saya mencoba hal yang sama pada output yang berjalan, seperti ini:

command | sed "s/some text/some text bolded/"

sedtidak melakukan apa-apa. Adakah pikiran?

Seperti yang ditunjukkan oleh Lambert cukup membantu, perkataan saya bahwa sedtidak melakukan apa pun tidak jelas. Apa yang terjadi adalah bahwa output program ke stdout(saya cukup yakin itu tidak menulis ke stderr) seperti biasanya, bahkan jika disalurkan melalui sed.

Masalahnya tampaknya bahwa perintah memanggil program kedua, yang kemudian di-output ke stdout. Ada beberapa baris yang dicetak oleh program pertama; ini saya dapat mengedit. Lalu ada aliran nilai yang dicetak oleh program kedua; ini saya tidak bisa mengedit.

Metode Perl dan awk juga tidak berfungsi.

P Jones
sumber
5
Apakah stdbuf -o0 command | sed "s/some text/some text bolded/"bekerja?
FloHimself
3
Dengan 'sed tidak melakukan apa-apa', maksud Anda substitusi tidak dilakukan, atau Anda tidak memiliki output? Perintahnya mungkin menulis ke stderr bukannya stdout? Jika Anda ingin menyorot sesuatu yang mungkin Anda gunakancommand|egrep 'some text|$'
Lambert
Apakah perintah menulis ke stdout (dan bukan ke stderr)?
Anthon
1
Mengingat bahwa teks muncul lebih dari sekali Anda harus menambahkan gsubstitusi "global" yang didapat, jika tidak hanya kemunculan pertama pada baris yang akan diganti:sed "s/old/new/g"
ph0t0nix
@ ph0t0nix Tidak, itu bukan masalahnya, tapi itu akan menjadi masalah di telepon. Terima kasih atas tipnya.
P Jones

Jawaban:

12

Kemungkinannya adalah output perintah buffer. Ketika perintah menulis ke terminal, buffer disiram pada setiap baris baru, sehingga Anda melihatnya muncul pada tingkat yang diharapkan. Ketika perintah menulis ke sebuah pipa, penyangga hanya memerah ketika mencapai beberapa kilobyte, sehingga banyak yang tertinggal. Dengan demikian adalah perilaku default dari perpustakaan input / output standar.

Untuk memaksa perintah untuk tidak merusak outputnya, Anda dapat menggunakan unbuffer(dari yang diharapkan) atau stdbuf(dari GNU coreutils).

unbuffer command | sed …
stdbuf -o0 command | sed …
Gilles 'SANGAT berhenti menjadi jahat'
sumber
stdbuftidak bekerja (disebutkan sebelumnya, BTW), tetapi unbufferberhasil !! Anda tidak tahu betapa bahagianya Anda membuat saya.
P Jones
Dan terima kasih atas penjelasan singkatnya. Sangat membantu.
P Jones
sedsendiri menggunakan buffer seperti itu, (cf posting ChennyStar) sehingga contoh di sini mungkin tidak berfungsi karena sedadalah commanduntuk unbuffer: cat /etc/passwd | unbuffer sedtetapi seditu sendiri memiliki -uopsi, jadi grepmungkin lebih cocok dalam contoh ini. Terima kasih banyak untuk informasi latar belakang Anda! Jawaban bagus!
matematika
4

sed memiliki opsi untuk itu:

-u, --unbuffered

Yang memuat jumlah data minimal dari file input dan menyiram buffer output lebih sering. Lihat man seduntuk lebih jelasnya.

ChennyStar
sumber
1
Hanya GNU sed(bukan BSD sed), dan saya percaya ini masih tidak akan mencegah buffering perintah di awal pipa. Tapi senang menyebutkannya. :)
Wildcard
manual stdbuf menyatakan bahwa "Jika PERINTAH menyesuaikan buffering dari stream standarnya ('tee' tidak misalnya) maka itu akan menimpa perubahan yang sesuai dengan 'stdbuf'." Sepertinya sed mungkin termasuk dalam kategori ini, tetapi bendera '-u' akan membuatnya berguna dalam rantai pipa yang tidak disadap.
MattSmith
2

Saya akan menggunakan awk

  command | awk '/some important stuff/ { printf "%c[31m%s%c[0m\n",27,$0,27 ; next }
  { print ; } '

dimana

  • /some important stuff/ pilih jalur penting, seperti pada sed
  • printf "%c[31m%s%c[0m\n",27,$0,27 ; cetak merah
    • gunakan 32,33 untuk hijau, kuning ...
    • $ 1, $ 2, dapat digunakan untuk memilih bidang tertentu
  • baris lain hanya dicetak 'apa adanya'

titik kuncinya adalah bahwa commandharus menyiram garis, tetapi itu harus terjadi jika Anda memiliki banyak output.

Archemar
sumber
Catatan: Pertanyaannya tidak mengatakan bahwa seluruh baris harus "dicetak tebal", tetapi hanya "beberapa teks". Meskipun dimungkinkan untuk mengimplementasikan juga fitur itu dalam awk(menggunakan sub()atau gsub()), dalam hal penggantian primitif sedini tentunya merupakan alat yang tepat.
Janis
1

Cara perl:

command | perl -pe 's/(stuff)/\x1b[1m$1\x1b[0m/g'

atau dengan output berkelanjutan:

Skrip bash untuk hasilnya cont:

#!/bin/bash
while [ true ]
do
   echo "Some stuff"
done

Uji dengan:

./cont | perl -pe 's/(stuff)/\x1b[1m${1}\x1b[0m/g'
  • \x1b[1m - intensitas tebal atau tebal
  • ${1} - pengaturan kembali
  • \x1b[0m - reset semua atribut

Keluaran:

Beberapa hal
Beberapa hal
Beberapa hal
Beberapa hal

Lebih banyak kode pelarian di sini .

AB
sumber