it-swarm-tr.com

Standart hata akışı (stderr) nasıl grep?

Bir ses klibinin meta bilgilerini almak için ffmpeg kullanıyorum. Ama ben bunu yapamıyorum.

    $ 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 
      ...

Kontrol ettim, bu ffmpeg çıkışı stderr'a yönlendirildi.

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

Bu yüzden grep'in eşleşen satırları yakalamak için hata akışını okuyamayacağını düşünüyorum. Grep'in hata akışını okumasını nasıl sağlayabiliriz?

nixCraft bağlantısını kullanarak, standart hata akışını standart çıkış akışına yönlendirdim, sonra grep çalıştı.

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

Peki ya stderr'ı stdout'a yönlendirmek istemiyorsak?

90
Andrew-Dufresne

bash kullanıyorsanız, neden phunehehe'nin söylediklerine kısaca anonim kanallar kullanmıyorsunuz:

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

66
Jé Queue

Normal mermilerin hiçbiri (hatta zsh) stdout'tan stdin'e kadar borulara izin vermez. Ancak tüm Bourne tarzı mermiler dosya tanımlayıcısının yeniden atanmasını destekler (1>&2). Böylece stdout'u geçici olarak fd 3'e ve stderr'i stdout'a aktarabilir ve daha sonra fd 3'ü stdout'a geri koyabilirsiniz. stuff, stdout'ta bir miktar çıktı ve stderr'de bir çıktı üretirse ve standart çıktıya dokunulmadan bırakılan hata çıktısına filter uygulamak istiyorsanız, { 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

Bu, phunehehe'nin "geçici dosya numarası" na benzer, ancak bunun yerine adlandırılmış bir boru kullanır ve sonuçları çıktıklarında biraz daha yakınlaştırmanıza olanak tanır, bu da uzun süren komutlar için kullanışlı olabilir:

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

Bu yapımda, stderr "mypipe" adlı boruya yönlendirilecektir. grep bir dosya argümanı ile çağrıldığından, girişi için STDIN'e bakmaz. Ne yazık ki, işiniz bittiğinde bu adlandırılmış boruyu temizlemeniz gerekecek.

Bash 4 kullanıyorsanız, command1 2>&1 | command2 İçin command1 |& command2 Kısayol sözdizimi vardır. Ancak, bunun tamamen bir sözdizimi kısayolu olduğuna inanıyorum, hala STDERR'u STDOUT'a yönlendiriyorsunuz.

22
Steven D

Gilles ve Stefan Lasiewski'nin cevapları hem iyi, hem de bu şekilde daha basit:

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

İstemediğini varsayıyorum ffmpeg's stdout yazdırıldı.

Nasıl çalışır:

  • önce borular
    • ffmpeg ve grep başlatıldı, ffmpeg'in stdout'u grep'in stdinine gidiyor
  • sonraki yönlendirmeler, soldan sağa
    • ffmpeg's stderr, stdout'u ne olursa olsun ayarlanır (şu anda boru)
    • ffmpeg'nin stdout'u/dev/null olarak ayarlandı
11
Mikel

Bu testlerde kullanılan komut dosyası için aşağıya bakın.

Grep yalnızca stdin üzerinde çalışabilir, bu nedenle stderr akışını Grep'in ayrıştırabileceği bir biçimde dönüştürmelisiniz.

Normalde, stdout ve stderr'nin her ikisi de ekranınıza yazdırılır:

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

Stdout'u gizlemek, ancak yine de stderr'ı yazdırmak için şunu yapın:

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

Ama grep stderr'da çalışmaz! Aşağıdaki komutun 'err' içeren satırları bastırmasını beklersiniz, ancak yapmaz.

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

İşte çözüm.

Aşağıdaki Bash sözdizimi çıktıyı stdout'a gizleyecek, ancak yine de stderr gösterecektir. İlk önce stdout'u/dev/null'a borlarız, sonra stderr'ı stdout'a dönüştürürüz, çünkü Unix boruları sadece stdout'ta çalışacaktır. Yine de metni grep edebilirsiniz.

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

(Yukarıdaki komutun farklı sonra ./command >/dev/null 2>&1, bu çok yaygın bir komuttur).

İşte test için kullanılan komut dosyası. Bu, bir satırı stdout'a ve bir satırı stderr'a yazdırır:

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

Akarsuları değiştirebilirsiniz. Bu, başlangıçta terminaldeki standart çıkışa giden çıkışı alırken orijinal standart hata akışını grep yapmanızı sağlar:

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

Bu, önce çıktı için açık yeni bir dosya tanımlayıcı (3) oluşturarak ve bunu standart hata akışına (3>&2). Ardından standart hatayı standart çıktıya yönlendiriyoruz (2>&1). Son olarak standart çıktı, orijinal standart hataya yönlendirilir ve yeni dosya tanımlayıcı kapatılır (1>&3-).

Senin durumunda:

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

Test ediliyor:

$ ( 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

Bir komutun çıktısını diğerine ilettiğinizde (|), yalnızca standart çıktıyı yeniden yönlendiriyorsunuzdur. Bu yüzden nedenini açıklamalı

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

istediğini çıkarmaz (yine de işe yarar).

Hata çıkışını standart çıkışa yönlendirmek istemiyorsanız, hata çıkışını bir dosyaya yönlendirebilir ve daha sonra grep yapabilirsiniz

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

bash stdout'u normal kanal aracılığıyla stdin akışına yönlendirebilir - | Ayrıca stdout ve stderr'i stdin'e |&

1
0x00

Bu durumda rc Kabuğu kullanmayı seviyorum.

İlk olarak paketi yükleyin (1 MB'den az).

Bu, stdout ve stderr kanallarını rc içinde nasıl atacağınıza bir örnektir:

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

Bash'den ayrılmadan yapabilirsiniz:

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

Fark etmiş olabileceğiniz gibi, sözdizimi basittir, bu da bunu tercih edilen çözümüm yapar.

Köşeli parantez içinde, boru karakterinden hemen sonra hangi dosya tanımlayıcısının borulanmasını istediğinizi belirleyebilirsiniz.

Standart dosya tanımlayıcıları aşağıdaki gibi numaralandırılmıştır:

  • 0: Standart giriş
  • 1: Standart çıkış
  • 2: Standart hata
1
Rolf

Bash alt işlem örneğindeki bir varyasyon:

stderr ve tee stderr için bir dosyaya iki satır yankı, tee grep ve stdout için geri boru

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

stdout'u:

fff

some.load.errors (örneğin stderr):

asdf
fff
0
jmunsch