bash: cetak stderr dalam warna merah

123

Apakah ada cara untuk membuat bash menampilkan pesan stderr dalam warna merah?

kolypto
sumber
4
Saya kira bash tidak akan pernah mewarnai keluarannya: beberapa program mungkin ingin menguraikan sesuatu, dan pewarnaan akan merusak data dengan urutan yang lolos. Aplikasi GUI harus menangani warna, saya kira.
kolypto
Menggabungkan Balázs Pozsár dan killdash9 memberikan hasil yang bagus: function color { "$@" 2> >(sed $'s,.*,\e[31m&\e[m,') } Bekerja untuk bash dan zsh. Tidak dapat menambahkan ini sebagai jawaban b / c reputasi.
Heinrich Hartmann
1
Saya menunggu jawaban yang memodifikasi bash untuk melakukan ini. Solusi di bawah ini benar - benar mengubah stderr dan bahkan mungkin mengurutkannya kembali stdout yang memecah hal-hal ketika urutan byte yang tepat dari stderr harus dipertahankan misalnya ketika perpipaan.
masterxilo

Jawaban:

98
command 2> >(while read line; do echo -e "\e[01;31m$line\e[0m" >&2; done)
Balázs Pozsár
sumber
6
Bagus! Tapi saya heran jika ada cara untuk membuatnya :) permanen
kolypto
9
Tip yang bagus! Saran: Dengan menambahkan >&2tepat sebelumnya ; done), output yang dimaksudkan untuk stderr sebenarnya ditulis ke stderr. Itu membantu jika Anda ingin menangkap output normal dari program.
henko
8
Penggunaan berikut tput, dan sedikit lebih mudah dibaca menurut saya:command 2> >(while read line; do echo -e "$(tput setaf 1)$line$(tput sgr0)" >&2; done)
Stefan Lasiewski
2
Saya pikir menjalankan 2 proses tput untuk setiap jalur output tidak elegan sama sekali. Mungkin jika Anda akan menyimpan output dari perintah tput dalam variabel dan menggunakannya untuk setiap gema. Tetapi sekali lagi, keterbacaan tidak benar-benar lebih baik.
Balázs Pozsár
1
Solusi ini tidak mempertahankan spasi putih tapi saya suka karena singkatnya. IFS= read -r lineharus membantu tetapi tidak. Tidak yakin kenapa.
Max Murphy
89

Metode 1: Gunakan substitusi proses:

command 2> >(sed $'s,.*,\e[31m&\e[m,'>&2)

Metode 2: Buat fungsi dalam skrip bash:

color()(set -o pipefail;"$@" 2>&1>&3|sed $'s,.*,\e[31m&\e[m,'>&2)3>&1

Gunakan seperti ini:

$ color command

Kedua metode akan menampilkan perintah stderrberwarna merah.

Baca terus untuk penjelasan tentang cara kerja metode 2. Ada beberapa fitur menarik yang ditunjukkan oleh perintah ini.

  • color()... - Membuat fungsi bash yang disebut warna.
  • set -o pipefail- Ini adalah opsi shell yang mempertahankan kode pengembalian kesalahan dari suatu perintah yang outputnya disalurkan ke perintah lain. Ini dilakukan dalam subkulit, yang dibuat oleh tanda kurung, agar tidak mengubah opsi pipefail di kulit terluar.
  • "$@"- Menjalankan argumen ke fungsi sebagai perintah baru. "$@"setara dengan"$1" "$2" ...
  • 2>&1- Pengalihan yang stderrperintah untuk stdoutsehingga menjadi sed's stdin.
  • >&3- Singkatan dari 1>&3, ini mengalihkan stdoutke deskriptor file sementara yang baru 3. 3akan dialihkan kembali ke stdoutnanti.
  • sed ...- Karena pengalihan atas, sed's stdinadalah stderrdari perintah dieksekusi. Fungsinya untuk mengelilingi setiap baris dengan kode warna.
  • $'...' Konstruksi bash yang menyebabkannya memahami karakter backslash-escape
  • .* - Cocok dengan seluruh baris.
  • \e[31m - Urutan escape ANSI yang menyebabkan karakter berikut menjadi merah
  • &- sedKarakter ganti yang mengembang ke seluruh string yang cocok (seluruh baris dalam kasus ini).
  • \e[m - Urutan escape ANSI yang mengatur ulang warna.
  • >&2- Singkatan untuk 1>&2, ini pengalihan sed's stdoutuntuk stderr.
  • 3>&1- Mengarahkan deskriptor file sementara 3kembali ke stdout.
killdash9
sumber
9
+1 jawaban terbaik! absolutly diremehkan!
muhqu
3
Jawaban yang bagus dan penjelasan yang lebih baik
Daniel Serodio
Mengapa Anda perlu melakukan semua pengalihan tambahan? sepertinya berlebihan
qodeninja
1
Apakah ada cara untuk membuatnya bekerja zsh?
Eyal Levin
1
ZSH tidak mengenali bentuk pengalihan steno. Hanya perlu dua lagi 1, yaitu:zsh: color()(set -o pipefail;"$@" 2>&1 1>&3|sed $'s,.*,\e[31m&\e[m,'1>&2)3>&1
Rekin
26

Anda juga dapat melihat stderred: https://github.com/sickill/stderred

sickill
sumber
Wow, utilitas ini luar biasa, satu-satunya hal yang diperlukan adalah memiliki repositori yang tepat yang menginstalnya untuk semua pengguna, dengan satu baris, tidak harus melakukan lebih banyak pekerjaan untuk mengaktifkannya.
sorin
Tampaknya berfungsi dengan baik ketika saya mengujinya dengan skrip build di terminal yang terpisah, tapi saya ragu untuk menggunakannya secara global .bashrc. Terimakasih Meskipun!
Joel Purra
2
Di OS X El Capitan, cara kerjanya (DYLD_INSERT_LIBRARY) "rusak" dalam biner sistem karena dilindungi oleh SIP. Jadi mungkin lebih baik menggunakan opsi bash yang diberikan dalam jawaban lain.
hmijail
1
@hmijail untuk MacOS silakan ikuti github.com/sickill/stderred/issues/60 sehingga kita dapat menemukan solusinya, sebagian sudah ada tetapi sedikit buggy.
Sorin
15

Cara bash membuat stderr merah secara permanen menggunakan 'exec' untuk mengarahkan aliran. Tambahkan yang berikut ke bashrc Anda:

exec 9>&2
exec 8> >(
    while IFS='' read -r line || [ -n "$line" ]; do
       echo -e "\033[31m${line}\033[0m"
    done
)
function undirect(){ exec 2>&9; }
function redirect(){ exec 2>&8; }
trap "redirect;" DEBUG
PROMPT_COMMAND='undirect;'

Saya telah memposting ini sebelumnya: Cara mengatur warna font untuk STDOUT dan STDERR

Injil
sumber
1
Ini adalah jawaban terbaik sejauh ini; mudah diimplementasikan tanpa instalasi / memerlukan hak sudo, dan dapat digeneralisasi untuk semua perintah.
Luke Davis
1
Sayangnya ini tidak berjalan dengan baik dengan perintah chaining (command && nextCommand || errorHandlerCommand). Output kesalahan berjalan setelah output errorHandlerCommand.
carlin.scott
1
Demikian pula, jika saya source ~/.bashrcdua kali dengan ini, terminal saya pada dasarnya terkunci.
Dolph
@ Wolf: Di bashrc saya, saya dengan mudah menjaga ini dengan pernyataan if untuk mencegah kode ini dari reload. Kalau tidak, masalahnya adalah pengalihan 'exec 9> & 2' setelah pengalihan sudah terjadi. Mungkin mengubahnya ke konstanta jika Anda tahu di mana> 2 menunjuk pada awalnya.
gospes
7

Saya telah membuat skrip pembungkus yang mengimplementasikan jawaban Balázs Pozsár di bash murni. Simpan di $ PATH dan perintah awalan untuk mewarnai hasilnya.

    #! / bin / bash

    if [$ 1 == "--help"]; kemudian
        echo "Menjalankan perintah dan mewarnai semua kesalahan yang terjadi"
        echo "Contoh:` basename $ {0} `wget ..."
        echo "(c) o_O Tync, ICQ # 1227-700, Selamat Menikmati!"
        keluar 0
        fi

    # Temp file untuk menangkap semua kesalahan
    TMP_ERRS = $ (mktemp)

    # Jalankan perintah
    "$ @" 2>> (saat membaca baris; lakukan gema -e "\ e [01; 31m $ baris \ e [0m" | tee --append $ TMP_ERRS; selesai)
    EXIT_CODE = $?

    # Tampilkan semua kesalahan lagi
    if [-s "$ TMP_ERRS"]; kemudian
        echo -e "\ n \ n \ n \ e [01; 31m === KESALAHAN === \ e [0m"
        cat $ TMP_ERRS
        fi
    rm -f $ TMP_ERRS

    # Selesai
    keluar $ EXIT_CODE

kolypto
sumber
2
Ini bisa dibuat lebih efisien jika "tee ..." diletakkan setelah "selesai".
Juliano
3

Anda dapat menggunakan fungsi seperti ini


 #!/bin/sh

color() {
      printf '\033[%sm%s\033[m\n' "$@"
      # usage color "31;5" "string"
      # 0 default
      # 5 blink, 1 strong, 4 underlined
      # fg: 31 red,  32 green, 33 yellow, 34 blue, 35 purple, 36 cyan, 37 white
      # bg: 40 black, 41 red, 44 blue, 45 purple
      }
string="Hello world!"
color '31;1' "$string" >&2

Saya tambahkan> & 2 untuk mencetak ke stderr

Ali Mezgani
sumber
4
Tidak mengatasi masalah. Anda belum memberikan cara untuk memisahkan stderr dari stdout, yang merupakan tujuan OP.
Jeremy Visser
1

Saya memiliki skrip O_o Tync versi yang sedikit dimodifikasi. Saya perlu membuat mod ini untuk OS X Lion dan itu tidak sempurna karena skrip terkadang selesai sebelum perintah yang dibungkus melakukannya. Saya sudah menambahkan tidur tapi saya yakin ada cara yang lebih baik.

#!/bin/bash

   if [ $1 == "--help" ] ; then
       echo "Executes a command and colorizes all errors occured"
       echo "Example: `basename ${0}` wget ..."
       echo "(c) o_O Tync, ICQ# 1227-700, Enjoy!"
       exit 0
       fi

   # Temp file to catch all errors
   TMP_ERRS=`mktemp /tmp/temperr.XXXXXX` || exit 1

   # Execute command
   "$@" 2> >(while read line; do echo -e "$(tput setaf 1)$line\n" | tee -a $TMP_ERRS; done)
   EXIT_CODE=$?

   sleep 1
   # Display all errors again
   if [ -s "$TMP_ERRS" ] ; then
       echo -e "\n\n\n$(tput setaf 1) === ERRORS === "
       cat $TMP_ERRS
   else
       echo "No errors collected in $TMP_ERRS"
   fi
   rm -f $TMP_ERRS

   # Finish
   exit $EXIT_CODE
Jurang
sumber
1

Solusi ini bekerja untuk saya: https://superuser.com/questions/28869/immediately-tell-which-output-was-sent-to-stderr

Saya telah menempatkan fungsi ini di .bashrcatau .zshrc:

# Red STDERR
# rse <command string>
function rse()
{
    # We need to wrap each phrase of the command in quotes to preserve arguments that contain whitespace
    # Execute the command, swap STDOUT and STDERR, colour STDOUT, swap back
    ((eval $(for phrase in "$@"; do echo -n "'$phrase' "; done)) 3>&1 1>&2 2>&3 | sed -e "s/^\(.*\)$/$(echo -en \\033)[31;1m\1$(echo -en \\033)[0m/") 3>&1 1>&2 2>&3
}

Lalu misalnya:

$ rse cat non_existing_file.txt

akan memberi saya output merah.

Eyal Levin
sumber
Anda dapat menambahkan set -o pipefail;sebelumnya (evaluntuk kode keluar redirect
kvaps
juga tambahkan "ke eval untuk menghemat spasi dalam argumen
kvaps
1

menggunakan xargs dan printf:

command 2> >(xargs -0 printf "\e[31m%s\e[m" >&2)
Carlos Barcellos
sumber
0

versi menggunakan fifos

mkfifo errs
stdbuf -o0 -e0 -i0 grep . foo | while read line; do echo -e "\e[01;31m$line  \e[0m" >&2; done &
stdbuf -o0 -e0 -i0 sh $script 2>errs
belum dibuka
sumber