Sadece STDERR'yi bir filtreden geçirin


82

Bash'de STDOUT ile birleştirmeden önce STDERR'ı bir filtreden geçirmenin herhangi bir yolu var mı? Yani istiyorum

STDOUT ────────────────┐
                       ├─────> terminal/file/whatever
STDERR ── [ filter ] ──┘

ziyade

STDOUT ────┐
           ├────[ filter ]───> terminal/file/whatever
STDERR ────┘

Yanıtlar:


64

İşte bash'ta dosya tanımlayıcılarının nasıl değiştirileceğinden sonra modellenmiş bir örnek . A.out'un çıktısı, 'STDXXX:' öneki olmadan aşağıdaki gibidir.

STDERR: stderr output
STDOUT: more regular

./a.out 3>&1 1>&2 2>&3 3>&- | sed 's/e/E/g'
more regular
stdErr output

Yukarıdaki bağlantıdan alıntı yapmak:

  1. İlk önce standart çıkışı & 3 olarak kaydedin (& 1, 3'e kopyalanır)
  2. Sonra stdout'u stderr'e gönder (& 2, 1'e kopyalandı)
  3. Stderr'i & 3'e gönder (stdout) (& 3, 2'ye kopyalandı)
  4. kapat & 3 (& - 3'e kopyalanır)

1
Benim için çalışmıyor. Sonunda onunla çalışmasını sağlıyorum 3>&2 2>&1 1>&3-.
aleung

3
Dikkat : Bu, FD 3'ün kullanımda olmadığını varsayar ve 1 ve 2 dosya tanımlayıcılarının değiş tokuşunu geri almaz, bu nedenle bunu başka bir komuta yönlendirmeye devam edemezsiniz. Daha fazla ayrıntı ve çözüm için bu yanıta bakın . {Ba, z} sh için çok daha net bir sözdizimi için bu yanıta bakın .
Tom Hale

25

TL; DR:

$ cmd 2> >(stderr-filter >&2)

Misal:

% cat /non-existant 2> >(tr o X >&2)
cat: /nXn-existant: NX such file Xr directXry
%

Bu hem bash hem de zsh'da çalışacaktır. Bash bugünlerde hemen hemen her yerde bulunuyor, ancak, POSIX için gerçekten (gerçekten karmaşık) bir çözüme ihtiyacınız varsa sh, buraya bakın .


Açıklama

Şimdiye kadar, bunu yapmanın en kolay yolu, STDERR'yi işlem ikamesi yoluyla yeniden yönlendirmektir :

İşlem ikamesi, bir işlemin girdisinin veya çıktısının bir dosya adı kullanılarak başvurulmasına izin verir. Şeklini alır

>(list)

İşlem listesi eşzamansız olarak çalıştırılır ve girdisi veya çıktısı bir dosya adı olarak görünür.

Yani süreç ikamesi ile elde ettiğiniz şey bir dosya adıdır.

Tıpkı senin yapabileceğin gibi:

$ cmd 2> filename

yapabilirsin

$ cmd 2> >(filter >&2)

Yeniden >&2yönlendirmenin filterSTDOUT'u orijinal STDERR'ye geri döndü.


1
Bu yanıtı devam ettiren bir uyarı var: bash , değiştirilen sürecin tamamlanmasını beklemiyor , FD ise 1 ve 2'yi değiştirmeye çalışıyor. Bu önemli olabilir. exec 2> >(while .. read .. echo)Bir systemd hizmeti olarak çalışan bir betikte bunu yaparak yandım. journald, hizmetin fd2'sini yakalar ve bir '<N>' önekinden günlük düzeyini çıkarır: <2> çıktı satırlarının başına eklenir ve günlük kayıtlarına atanmış ERROR düzeyini alırsınız. Ancak bazen sistem, son, en önemli mesajı günlüğe kaydetmeden önce ana gruptan çıktıktan sonra tüm süreç grubunu öldürmek için daha hızlıydı!
kkm

Güzel basit çözüm. Uyarı: Bunu bir cron işinin parçası olarak kullanırken dikkatli olun; cron tarafından kullanılan bazı kabuklarda işlem ikamesi mevcut değildir.
Quinn Comendant

24

Süreç ikamesinin saf bir kullanımı şunlardan stderrayrı olarak filtrelemeye izin veriyor gibi görünüyor stdout:

:; ( echo out ; echo err >&2 ) 2> >( sed s/^/e:/ >&2 )
out
e:err

Not stderrçıkar, stderrve stdoutüzerinde stdoutbaşka bir alt kabukta şeyi sarma ve dosyalara yönlendirerek görebileceğiniz, ovee

( ( echo out ; echo err >&2 ) 2> >( sed s/^/e:/ >&2 ) ) 1>o 2>e

neden:; başlangıçta? Sihirli çizgiyi onsuz denedim ve bir fark yaratmıyor gibi görünüyor.
Koshmaar

1
Bazı insanlar $komut istemi olarak kullanır . Sonra da sıklıkla gibi kabuk örnekleri yazma: $ cat /var/log/syslog | fgrep .... Ancak bu satır $,. :;bir komut istemi gibi görünür, ancak temelde işlemsiz bir kabuktur; böylece tüm satırı güvenle seçip yapıştırabilirsiniz.
solidsnack

23
Tamam, ancak şunları atlayabilirsiniz:; ve satır da kopyalanabilir olacaktır :) Bana göre:; bir bilgi istemi gibi görünmüyor, örneği daha net değil (komutları çıktıdan ayırmak) ama kafa karıştırıcıydı. Bakış açınızı anlasam da sözdizimi / kuralları tartışmayalım.
Koshmaar

15

TL; DR: (bash ve zsh)

$ cmd 2> >(stderr-filter >&2)

Misal:

% cat /non-existant 2> >(tr o X >&2)
cat: /nXn-existant: NX such file Xr directXry
%

StackExchange ağındaki birçok yanıt şu biçime sahiptir:

cat /non-existant 3>&1 1>&2 2>&3 3>&- | sed 's/e/E/g'

Bunun yerleşik bir varsayımı vardır: bu dosya tanımlayıcısı 3 başka bir şey için kullanılmıyor.

Bunun yerine, adlandırılmış bir dosya tanımlayıcı kullanın ve bir {ba,z}shsonraki kullanılabilir dosya tanımlayıcısını> = 10 ayırın:

cat /non-existant {tmp}>&1 1>&2 2>&$tmp {tmp}>&- | sed 's/e/E/g'

Adlandırılmış dosya tanımlayıcılarının POSIX tarafından desteklenmediğini unutmayın sh.

Yukarıdakilerle ilgili diğer sorun, komutun STDOUT ve STDERR'ı orijinal değerlerine geri döndürmeden başka komutlara aktarılamamasıdır.

POSIX'te ileriye doğru borulamaya izin vermek için sh(ve yine de FD 3'ün kullanılmadığını varsayarsak) karmaşıklaşır :

(cmd 2>&1 >&3 3>&- | stderr-filter >&2 3>&-) 3>&1

Öyleyse, bunun varsayımı ve karmaşık sözdizimi göz önüne alındığında, yukarıda TL; DR'de gösterilen ve burada açıklanan daha basit bash/ zshsözdizimini kullanmanız daha iyi olacaktır .


8

Başlangıçtaki amacı neredeyse kelimesi kelimesine yansıttığı için bash işlem ikamesinin kullanımını hatırlamayı ve kullanmayı daha kolay buluyorum. Örneğin:

$ cat ./p
echo stdout
echo stderr >&2
$ ./p 2> >(sed -e 's/s/S/') | sed 's/t/T/'
sTdout
STderr

ilk sed komutunu yalnızca stderr üzerinde bir filtre olarak ve ikinci sed komutunu birleştirilen çıktıyı değiştirmek için kullanır.

Komutun doğru şekilde ayrıştırılması için 2> 'den sonraki beyaz boşluğun zorunlu olduğuna dikkat edin.


5

Advanced Bash Scripting Guide'ın bu sayfasının son kısmı "yalnızca stderr'i bir boruya yeniden yönlendirmektir".

# Yalnızca stderr'i bir boruya yönlendirmek.

exec 3>&1                              # Save current "value" of stdout.
ls -l 2>&1 >&3 3>&- | grep bad 3>&-    # Close fd 3 for 'grep' (but not 'ls').
#              ^^^^   ^^^^
exec 3>&-                              # Now close it for the remainder of the script.

# Teşekkürler SC

İstediğin bu olabilir. Değilse, ABSG'nin başka bir kısmı size yardımcı olabilir, bu mükemmel.


Farklılıkları açıkça belirtmeden belgeleri, reçeteleri ve görüşü karıştırdığı için ABSG'yi referans olarak önermekte biraz tereddüt ediyoruz. Bağlandığınız bölüm iyi görünse de bazı bölümler şüpheli içeriğe sahiptir.
üçlü

3

Adlandırılmış borulara bir göz atın:

$ mkfifo err
$ cmd1 2>err |cat - err |cmd2

olmaz cat - errstdout ve stderr interspersing kırmak?
Martin DeMello 01

@Martin - duruma göre değişir. Cmd1, cat veya cmd2 tampon çıktıları çıktıysa, çıktının sıra dışı olduğunu görebilirsiniz.
mob
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.