Stdout'u bir dosyaya ve stdout + stderr'i bir başkasına yönlendirmek nasıl?


32

Nasıl başarabilirim

cmd >> file1 2>&1 1>>file2

Yani, stdout ve stderr bir dosyaya (dosya1) yönlendirmeli ve sadece stdout (dosya2) bir başkasına yönlendirmeli (her ikisi de ekleme modunda)?

Yanıtlar:


41

Sorun, çıktınızı yeniden yönlendirdiğinizde, bir sonraki yönlendirme için artık kullanılamıyor olmasıdır. Şunları yapabilirsiniz için boru teebir altkabuk ikinci yönlendirme için çıkışını tutmak için:

( cmd | tee -a file2 ) >> file1 2>&1

veya çıkışı terminalde görmek isterseniz:

( cmd | tee -a file2 ) 2>&1 | tee -a file1

İlk komutun stderr'inin eklenmesini önlemek teeiçin file1, komutunuzun stderr'ini bazı dosya tanımlayıcılarına yönlendirmelisiniz (örneğin 3) ve daha sonra bunu tekrar stdout'a eklemelisiniz:

( 2>&3 cmd | tee -a file2 ) >> file1 3>&1
# or
( 2>&3 cmd | tee -a file2 ) 3>&1 | tee -a file1

(teşekkürler @ fra-san)


16

İle zsh:

cmd >& out+err.log > out.log

Ekleme modunda:

cmd >>& out+err.log >> out.log

Içinde zshve mult_iosseçeneği devre dışı bırakılmadığı takdirde, bir dosya tanıtıcısı (burada 1) yazmak için birkaç kez yönlendirildiğinde, kabuk teeçıktıyı tüm hedeflere çoğaltmak için bir yerleşik uygular .


Burada ne anlama geldiğini out+errve ne outanlama geldiğini bulamıyorum. Dosya isimleri? Yönlendirilecek akışlar?
gronostaj

@gronostaj Komutun okuduğunu düşününcmd >& file1 > file2
Isaac

Bu çözüm, çıktının üretildiği sırayı koruyacaktır. Aslında stdout ve stderr'i saklamak için (bu sırayla) farklı bir yaklaşıma ihtiyacınız var.
Isaac

1
@Iacac, stdout çıktısı doğrudan dosyaya giderken stdout çıktısı bir borudan geçeceği için (her dosyaya ileten bir işleme) siparişin mutlaka korunmaması gerekir. Her durumda, OP stderr çıktısının stdout çıktısından sonra gelmesini istedi gibi görünmüyor.
Stéphane Chazelas

3

Yapabilecekleriniz: stdout etiketi (UNBUFFERED sed, yani:) kullanarak sed -u ..., stderr'nin de stdout'a (etiketli, bu etiketleme sed'inden geçmediği için etiketlenmemiş) gitmesi ve sonuçta ortaya çıkan logfile'deki 2'yi ayırt edebilmesi gerekir.

Aşağıdakiler yavaştır ( yavaştır (ciddiyetle optimize edilebilir, bir süre yerine perl betiği kullanarak); yapın ...; her biri için alt kabuklar ve komutlar ortaya çıkacak!), Garip bir ": vs. Ama bu (ben bir yeniden adlandırma stdout'ta ve daha sonra diğer birinde kendisine stderr'e "yoluyla falled" eklemek için 2 {} aşamaları ihtiyaç görünüyor) kavramının kanıtı ", yani tutmaya çalışacağız stdout ve stderr'den mümkün olduğu kadar çıktının siparişi:

#basic principle (some un-necessary "{}" to visually help see the layers):
# { { complex command ;} | sed -e "s/^/TAGstdout/" ;} 2>&1 | read_stdin_and_redispatch

#exemple:
# complex command = a (slowed) ls of several things (some existing, others not)
#  to see if the order of stdout&stderr is kept

#preparation, not needed for the "proof of concept", but needed for our specific exemple setup:
\rm out.file out_AND_err.file unknown unknown2 
touch existing existing2 existing3

#and the (slow, too many execs, etc) "proof of concept":
uniquetag="_stdout_" # change this to something unique, that will NOT appear in all the commands outputs... 
                     # avoid regexp characters ("+" "?" "*" etc) to make it easy to remove with another sed later on.

{
   { for f in existing unknown existing2 unknown2 existing3 ; do ls -l "$f" ; sleep 1; done ;
   } | sed -u -e "s/^/${uniquetag}/" ;
} 2>&1 | while IFS="" read -r line ; do
    case "$line" in
       ${uniquetag}*) printf "%s\n" "$line" | tee -a out_AND_err.file | sed -e "s/^${uniquetag}//" >> out.file ;; 
        *)            printf "%s\n" "$line"       >> out_AND_err.file ;;   
    esac; 
done;

# see the results:
grep "^" out.file out_AND_err.file

Bunu anlamak gerçekten zor. (1) Neden ls unknownstderr üzerine bir şey yazdırmak için bu kadar karmaşık kullanım halini ( ) kullanıyorsunuz ? >&2 echo "error"iyi olurdu. (2) teeaynı anda birden fazla dosyaya ekleyebilir. (3) Neden sadece catdeğil grep "^"? (4) stderr çıkışı başladığında betiğiniz başarısız olur _stdout_. (5) Neden?
pLumo

@RoVo: Yorumlanan ilk 2 satır, konsept örneğinin kanıtından daha basit olan algoritmayı gösterir. 1): bu ls loophem stdout hem de stderr çıktısını (alternatif olarak) kontrollü bir düzende karıştırır, böylece stdout 2 etiketlemesine rağmen bu stderr / stdout sırasını tuttuğumuzu kontrol edebiliriz) düzenli kuyruk (örneğin, aix üzerinde). 3): grep "^", her iki dosya adını da gösterir. 4): bu değişken tarafından değiştirilebilir. 5): kıvrılmış örnek, test ettiğim eski hortumlarda (örneğin, eski aix) çalışır (perl mevcut değildir).
Olivier Dulac

(1) stderr ve stout'a birden fazla eko iyi gelirdi, ama tamam, önemli değil, sadece okunması daha kolay olurdu. (3) katılıyorum, (4) elbette, fakat değişken ne olursa olsun başlasa, başarısız olur. (5) görüyorum.
pLumo

@RoVo Ben senin 1 ile katılıyorum). 4 için), değişken, pb'nin yok olmasını sağlamak için gerektiği kadar karmaşık olabilir (örneğin: uniquetag="banaNa11F453355B28E1158D4E516A2D3EDF96B3450406...)
Olivier Dulac

1
Elbette, pek olası değil, ama yine de daha sonra bir güvenlik sorunu ortaya çıkarabilir. Ve sonra dosyaya yazdırmadan önce bu dizgiyi kaldırmak isteyebilirsiniz ;-)
pLumo

2

Çıktı sırası şöyle olmalıdır: stdout, stderr ; sadece yeniden yönlendirmeyle ilgili bir çözüm yoktur.
Stderr geçici bir dosyaya kaydedilmelidir

cmd 2>>file-err | tee -a file1 >>file2
cat file-err >> file1
rm file-err

Açıklama:

Bir çıktıyı (stdout veya stderr gibi bir fd) iki dosyaya yeniden yönlendirmenin tek yolu onu yeniden oluşturmaktır . Komut tee, bir dosya tanımlayıcı içeriğini yeniden oluşturmak için doğru araçtır. Bu nedenle, iki dosya üzerinde bir çıktının olması için ilk fikir kullanmak:

... |  tee file1 file2

Bu, tee stdinini, tee çıktısını hala kullanılmayan bırakarak her iki dosyaya (1 ve 2) kopyalar. Ancak eklememiz (kullanmamız -a) ve yalnızca bir kopyaya ihtiyacımız var. Bu her iki sorunu da çözer:

... | tee -a file1 >>file2

teeStdout'u (tekrarlanacak olanı) tedarik etmek için stderr'i doğrudan komutun dışında tüketmemiz gerekir. Bir yol, sipariş önemli değilse (çıktı sırası (muhtemelen)) oluşturulduğu gibi korunacak, hangisi önce çıktı saklanacaktır). Ya:

  1. cmd 2>>file1 | tee -a file2 >>file1
  2. cmd 2>>file1 > >( tee -a file2 >>file1 )
  3. ( cmd | tee -a file2 ) >> file1 2>&1

Seçenek 2 yalnızca bazı kabuklarda çalışır. Seçenek 3, ek bir alt kabuk kullanır (daha yavaş) ancak dosya adlarını yalnızca bir kez kullanır.

Ama eğer stdout'u gerekir ilk biz sonunda dosyaya eklemek için mağaza stderr'e gerek (hangisi sipariş çıkış üretilir) (birinci çözüm yayınlanmıştır).


1
Veya hafızada sponge(cmd | tee -a out >> out+err) 2>&1 | sponge >> out+err
saklandığı

1

Çeşitlilik adına:

Sisteminiz destekliyorsa /dev/stderr, o zaman

(cmd | tee -a /dev/stderr) 2>> file1 >> file2

çalışacak. 'Nın standart çıktısı cmd hem boru hattının hem de stdout'una gönderilir. cmdBaypasların standart hatası tee boru hattının stderrini atlar ve çıkar.

Yani

  • Boru hattının stdout, sadece cmd, ve
  • Boru hattının stderr'ı cmd, karıştırılan stdout ve stderr'dir .

Bu, bu akışları doğru dosyalara göndermenin basit bir meselesidir.

Neredeyse böyle bir yaklaşımda olduğu gibi ( Stéphane'nin cevabı dahil ) file1sıra dışı olabilir.

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.