it-swarm-id.com

Bagaimana cara grep stream kesalahan standar (stderr)?

Saya menggunakan ffmpeg untuk mendapatkan info meta dari klip audio. Tapi saya tidak bisa menerimanya.

    $ ffmpeg -i 01-Daemon.mp3  |grep -i Duration
    FFmpeg version SVN-r15261, Copyright (c) 2000-2008 Fabrice Bellard, et al.
      configuration: --prefix=/usr --bindir=/usr/bin 
      --datadir=/usr/share/ffmpeg --incdir=/usr/include/ffmpeg --libdir=/usr/lib
      --mandir=/usr/share/man --Arch=i386 --extra-cflags=-O2 
      ...

Saya memeriksa, output ffmpeg ini diarahkan ke stderr.

$ ffmpeg -i 01-Daemon.mp3 2> /dev/null

Jadi saya pikir grep tidak dapat membaca aliran kesalahan untuk menangkap garis yang cocok. Bagaimana kita bisa mengaktifkan grep untuk membaca aliran kesalahan?

Menggunakan tautan nixCraft , saya mengarahkan aliran kesalahan standar ke aliran keluaran standar, lalu grep bekerja.

$ ffmpeg -i 01-Daemon.mp3 2>&1 | grep -i Duration
  Duration: 01:15:12.33, start: 0.000000, bitrate: 64 kb/s

Tetapi bagaimana jika kita tidak ingin mengarahkan stderr ke stdout?

90
Andrew-Dufresne

Jika Anda menggunakan bash mengapa tidak menggunakan pipa anonim, intinya singkatan untuk apa yang dikatakan phunehehe:

ffmpeg -i 01-Daemon.mp3 2> >(grep -i Duration)

66
Jé Queue

Tak satu pun dari kerang yang biasa (bahkan zsh) mengizinkan pipa selain dari stdout ke stdin. Tetapi semua shell Bourne-style mendukung penugasan kembali deskriptor file (seperti pada 1>&2). Jadi, Anda dapat sementara mengalihkan stdout ke fd 3 dan stderr ke stdout, dan kemudian mengembalikan fd 3 ke stdout. Jika stuff menghasilkan beberapa output pada stdout dan beberapa output pada stderr, dan Anda ingin menerapkan filter pada output kesalahan membiarkan output standar tidak tersentuh, Anda dapat menggunakan { stuff 2>&1 1>&3 | filter 1>&2; } 3>&1.

$ stuff () {
  echo standard output
  echo more output
  echo standard error 1>&2
  echo more error 1>&2
}
$ filter () {
  grep a
}
$ { stuff 2>&1 1>&3 | filter 1>&2; } 3>&1
standard output
more output
standard error

Ini mirip dengan "temp file trick" phunehehe, tetapi menggunakan pipa bernama sebagai gantinya, memungkinkan Anda untuk mendapatkan hasil yang sedikit lebih dekat dengan ketika mereka dihasilkan, yang dapat berguna untuk perintah yang sudah berjalan lama:

$ mkfifo mypipe
$ command 2> mypipe | grep "pattern" mypipe

Dalam konstruksi ini, stderr akan diarahkan ke pipa bernama "mypipe". Karena grep telah dipanggil dengan argumen file, ia tidak akan mencari input dari STDIN. Sayangnya, Anda masih harus membersihkan pipa bernama itu setelah Anda selesai.

Jika Anda menggunakan Bash 4, ada sintaks pintasan untuk command1 2>&1 | command2, yang mana command1 |& command2. Namun, saya percaya bahwa ini murni cara pintas sintaksis, Anda masih mengarahkan ulang STDERR ke STDOUT.

22
Steven D

Jawaban Gilles dan Stefan Lasiewski sama-sama baik, tetapi cara ini lebih sederhana:

ffmpeg -i 01-Daemon.mp3 2>&1 >/dev/null | grep "pattern"

Saya berasumsi Anda tidak mau ffmpeg's stdout dicetak.

Bagaimana itu bekerja:

  • pipa dulu
    • ffmpeg dan grep dimulai, dengan stfout ffmpeg akan menjadi stdin grep
  • pengalihan berikutnya, kiri ke kanan
    • stderr ffmpeg diatur ke apa pun stdout-nya (saat ini pipa)
    • stdout ffmpeg diatur ke/dev/null
11
Mikel

Lihat di bawah untuk skrip yang digunakan dalam tes ini.

Grep hanya dapat beroperasi pada stdin, jadi karena itu Anda harus mengubah aliran stderr dalam bentuk yang dapat diurai Grep.

Biasanya, stdout dan stderr dicetak ke layar Anda:

$ ./stdout-stderr.sh
./stdout-stderr.sh: Printing to stdout
./stdout-stderr.sh: Printing to stderr

Untuk menyembunyikan stdout, tetapi tetap cetak stderr lakukan ini:

$ ./stdout-stderr.sh >/dev/null
./stdout-stderr.sh: Printing to stderr

Tetapi grep tidak akan beroperasi pada stderr! Anda akan mengharapkan perintah berikut untuk menekan baris yang mengandung 'err', tetapi tidak.

$ ./stdout-stderr.sh >/dev/null |grep --invert-match err
./stdout-stderr.sh: Printing to stderr

Inilah solusinya.

Sintaks Bash berikut akan menyembunyikan output ke stdout, tetapi masih akan menampilkan stderr. Pertama kita mem-pipe stdout ke/dev/null, lalu kita mengonversi stderr ke stdout, karena pipa Unix hanya akan beroperasi di stdout. Anda masih dapat menerima teks.

$ ./stdout-stderr.sh 2>&1 >/dev/null | grep err
./stdout-stderr.sh: Printing to stderr

(Perhatikan bahwa perintah di atas adalah berbeda lalu ./command >/dev/null 2>&1, yang merupakan perintah yang sangat umum).

Berikut skrip yang digunakan untuk pengujian. Ini mencetak satu baris ke stdout dan satu baris ke stderr:

#!/bin/sh

# Print a message to stdout
echo "$0: Printing to stdout"
# Print a message to stderr
echo "$0: Printing to stderr" >&2

exit 0
8
Stefan Lasiewski

Anda dapat menukar streaming. Ini akan memungkinkan Anda untuk grep aliran kesalahan standar asli sambil tetap mendapatkan output yang awalnya pergi ke output standar di terminal:

somecommand 3>&2 2>&1 1>&3- | grep 'pattern'

Ini berfungsi dengan terlebih dahulu membuat deskriptor file baru (3) terbuka untuk output dan mengaturnya ke aliran kesalahan standar (3>&2). Kemudian kami mengarahkan kesalahan standar ke output standar (2>&1). Akhirnya keluaran standar dialihkan ke kesalahan standar asli, dan deskriptor file baru ditutup (1>&3-).

Dalam kasus Anda:

ffmpeg -i 01-Daemon.mp3 3>&2 2>&1 1>&3- | grep -i Duration

Mengujinya:

$ ( echo "error" >&2; echo "output" ) 3>&2 2>&1 1>&3- | grep "error"
output
error

$ ( echo "error" >&2; echo "output" ) 3>&2 2>&1 1>&3- | grep -v "error"
output
3
Kusalananda

Ketika Anda menyalurkan output dari satu perintah ke perintah lainnya (menggunakan |), Anda hanya mengarahkan keluaran standar. Jadi itu harus menjelaskan alasannya

ffmpeg -i 01-Daemon.mp3 | grep -i Duration

tidak menampilkan apa yang Anda inginkan (itu berhasil, meskipun).

Jika Anda tidak ingin mengarahkan output kesalahan ke output standar, Anda dapat mengarahkan output kesalahan ke file, lalu ambil nanti

ffmpeg -i 01-Daemon.mp3 2> /tmp/ffmpeg-error
grep -i Duration /tmp/ffmpeg-error
3
phunehehe

bash dapat mengarahkan stdout ke aliran stdin melalui pipa biasa - | Ini juga dapat mengalihkan stdout dan stderr ke stdin oleh |&

1
0x00

Saya suka menggunakan shell rc dalam kasus ini.

Pertama instal paket (kurang dari 1MB).

Ini adalah contoh bagaimana Anda akan membuang stdout dan pipa stderr untuk menerima rc:

find /proc/ >[1] /dev/null |[2] grep task

Anda dapat melakukannya tanpa meninggalkan Bash:

rc -c 'find /proc/ >[1] /dev/null |[2] grep task'

Seperti yang mungkin Anda perhatikan, sintaksinya langsung, yang menjadikan ini solusi pilihan saya.

Anda bisa menentukan deskriptor file mana yang ingin Anda perpipkan di dalam tanda kurung, tepat setelah karakter pipa.

Deskriptor file standar diberi nomor seperti itu:

  • 0: Input standar
  • 1: Ouptut standar
  • 2: Kesalahan standar
1
Rolf

Variasi pada contoh subproses bash:

gema dua baris ke stderr dan tee stderr ke file, ambil tee dan pipa kembali ke stdout

(>&2 echo -e 'asdf\nfff\n') 2> >(tee some.load.errors | grep 'fff' >&1)

stdout:

fff

some.load.errors (misalnya stderr):

asdf
fff
0
jmunsch