Bu Bash kılavuzunun yeniden yönlendirme bölümündeki bir yazım hatası mı?


13
Note that the order of redirections is significant.  For example, the command

          ls > dirlist 2>&1

   directs both standard output and standard error to the file dirlist, 
   while the command

          ls 2>&1 > dirlist

   directs  only  the  standard  output  to  file  dirlist,  because the 
   standard error was duplicated from the standard output before the standard
   output was redirected to dirlist.

Şimdi, bu son kısım benim için kafa karıştırıcı. Bu durumda, herhangi bir standart hata terminale yazdırılır ve herhangi bir STDOUT dirlist dosyasına gider. Böyle bir şey olurdu, ama kılavuzu bu şekilde anlamıyorum.

"Standart hata standart listeye yeniden yönlendirildikten SONRA standart çıktıdan standart hata kopyalandığından" gibi görünmelidir. STDEROUT bir dosyaya yönlendirilmeden önce STDERR STDOUT'a gönderildiyse, dosya STDOUT VE STDERR içermez mi?

Birisi bunu benim için temizleyebilir mi? Benim açımdan sadece okuduğunu anlama kötü mü? Çoğaltma kelimesinin kullanımı bu bağlamda bana biraz garip geliyor. Belki de beni atar.



1
"Değerle" ve "referansla" olan karıştırma işlemlerinin klasik bir örneği. Bir dosya tanımlayıcısını çoğalttığınızda, bu bir by-value işlemidir. Programlamada, gerçek a = 1; b = a; a = 2olmasını bekledikten sonra a == 2 && b == 1. Yeniden yönlendirme atamaya 2>&1benzer b = a- referansla değil değere göre. 2>&1dosya tanımlayıcı 2'yi sonsuzluğun tümü için dosya tanımlayıcı 1'e bağlamaz - yine de aynı dosyayı işaret eden 2 ayrı dosya tanımlayıcıdır.
jw013

Yanıtlar:


23

Çoğaltma burada gerçekten önemli bir parçadır.

Yeniden yönlendirmeden önce dosya tanımlayıcılarının nereye gideceğini görelim. Bu normalde akım terminalidir, örneğin:

STDOUT ---> /dev/pts/1
STDERR ---> /dev/pts/1

Şimdi, ls -lyönlendirme olmadan ararsak, çıkış ve hata mesajları aşağıdaki terminalime gider /dev/pts/1.

İlk STDOUTolarak bir dosyaya ( ls -l > dirlist) yönlendirirsek, şöyle görünür:

STDOUT ---> /home/bon/dirlist
STDERR ---> /dev/pts/1

Biz ne zaman sonra yönlendirmek STDERRa yinelenen bir STDOUTbireyin dosya tanımlayıcı ( ls -l > dirlist 2>&1), STDERRbir kopyası gider /home/bon/dirlist:

STDOUT ---> /home/bon/dirlist
STDERR ---> /home/bon/dirlist

Biz istiyorsanız ilk yönlendirmek STDERRkopyası için STDOUTbireyin dosya tanımlayıcı ( ls -l 2>&1):

STDOUT ---> /dev/pts/1
STDERR ---> /dev/pts/1

ve sonra STDOUT bir dosyaya ( ls -l 2>&1 > dirlist), şunu elde ederiz:

STDOUT ---> /home/bon/dirlist
STDERR ---> /dev/pts/1

Burada STDERRhala terminale gidiyor.

Gördüğünüz gibi, man sayfasındaki sipariş doğrudur.


Yeniden Yönlendirmeyi Test Etme

Şimdi, bunu kendiniz test edebilirsiniz. Kullanarak ls -l /proc/$$/fd/, STDOUT(fd 1 ile) ve STDERR(fd 2 ile), geçerli işlemin nereye gittiğini görürsünüz :

$ ls -l /proc/$$/fd/
total 0
lrwx------ 1 bon bon 64 Jul 24 18:19 0 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 18:19 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 07:41 2 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 18:19 255 -> /dev/pts/1

Dosya tanımlayıcılarınızın nereye yönlendirildiğini gösteren küçük bir kabuk betiği oluşturalım. Bu şekilde, arama sırasında her zaman ls, arama kabuğundan herhangi bir yönlendirme de dahil olmak üzere durumu alırız .

$ cat > lookfd.sh
#!/bin/sh
ls -l /proc/$$/fd/
^D
$ chmod +x lookfd.sh

(İle CtrlDbir dosya sonu gönderirsiniz ve böylece catkomutu okumayı durdurursunuz STDIN.)

Şimdi, bu komut dosyasını farklı yönlendirme kombinasyonlarıyla çağırın:

$ ./lookfd.sh 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:08 0 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:08 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:08 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:08 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh > foo.out
$ cat foo.out 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:10 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:10 1 -> /home/bon/foo.out
lrwx------ 1 bon bon 64 Jul 24 19:10 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:10 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh 2>&1 > foo.out
$ cat foo.out 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:10 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:10 1 -> /home/bon/foo.out
lrwx------ 1 bon bon 64 Jul 24 19:10 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:10 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh > foo.out 2>&1
$ cat foo.out 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:11 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:11 1 -> /home/bon/foo.out
l-wx------ 1 bon bon 64 Jul 24 19:11 2 -> /home/bon/foo.out
lr-x------ 1 bon bon 64 Jul 24 19:11 255 -> /home/bon/lookfd.sh

1 (for STDOUT) ve 2 (for STDERR) dosya tanımlayıcılarının değiştiğini görebilirsiniz. Eğlenmek STDINiçin, sonucu yeniden yönlendirebilir ve görebilirsiniz:

$ ./lookfd.sh < /dev/zero
total 0
lr-x------ 1 bon bon 64 Jul 24 19:18 0 -> /dev/zero
lrwx------ 1 bon bon 64 Jul 24 19:18 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:18 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:18 255 -> /home/bon/lookfd.sh

(Soru okuyucuya bırakıldı: Dosya tanımlayıcı 255 nerede gösteriyor? ;-))


+1 - mükemmel cevap. Son derece iyi yazılmış ve müthiş örnekler. Teşekkür ederim!!!
slm

Görüyorum ki, yanlış anlaşılmam, yönlendirmenin aşağıdaki tüm komutlar için kalıcı olacağıydı, böylece hattın geri kalanı için herhangi bir STDERR, STDOUT'a gidecekti.
Gregg Leventhal

2

Hayır, kılavuz haklı.

İlk başta terminali ve 2 terminali de işaret ediyorsa, o zaman:

command  2>&1   1>somewhere

yönlendirme değerlendirme soldan sağa doğru olacaktır.

Bu nedenle, ilk olarak 2>&1fd'yi 1işaret etmek için kullanılan İLK değerlendirilir ve böylece İLK kopyalanır (yani the terminal, genellikle / dev / tty dosya tanımlayıcısı) fd'ye kopyalanır 2.

Yani o noktada fd 2şimdi fd'nin 1( the terminal)

Ve sonra 1>somewhereparçayı değerlendirir ve böylece somewherefd içindeki dosya tanımlayıcısını kopyalar 1(bu noktada fd 1şimdi işaret eder somewhereve fd 2hala işaret eder the terminal)

Böylece 1 değişti ÖNCE 2 1'den çoğaltılır gibi gerçekten 1 "bir yere" ve 2 terminaline yazdırır.

Diğer sipariş:

command  1>somewhere 2>&1

ilk yönlendirme fd olacak 1kadar somewhere, ve daha sonra bu aynı zamanda noktalarını uca 2 de, fd 2 içine aynı başvuru kopya somewhere. Ancak bundan böyle "bağlantılı" değiller. Her biri ayrı ayrı yönlendirilebilir.

örn:

command  1>somewhere 2>&1
exec 2>/dev/null

Bu birinin sonunda, fd 1noktaları somewhereve fd 2yöneliktir/dev/null

Fd için normal adlar 1STDOUT (standart çıktı) ve fd için genel ad 2STDERR'dir (standart hata, genellikle STDOUT'a müdahale etmeden hataları görüntülemek için kullanılır)


@ Michael-mrozek: düzenleme için teşekkürler, ancak "yinelenen" yerine "yinelenen" yerine "kopya" demede ısrar ediyorum, bundan sonra her ikisinin de "aynı şey" olduğuna inanmasına yol açabilir, ki bu doğru değil. örnek:: cmd 1>somewhere 2>&1 ; exec 2>/dev/nullexec'den sonra sadece 2 / dev / null konumuna yönlendirildi (1 hala "bir yere" gidiyor). Ben "fd 1" yerine "ne 1 puan" demek için bir yol bulmak için yardıma ihtiyacım var, ancak ... bu da kafa karıştırıcı olduğu gibi ...
Olivier Dulac

1
Neyi kastettiğinden emin değilim; "kopya" dan "kopya" olarak değiştiren sizsiniz . Tek yaptığım şeyleri büyük harfle biçimlendirmek ve biçimlendirmekti, bir kelime değiştirmedim
Michael Mrozek

doh ... ^^ üzgünüm. Ve tekrar neyin kopyalandığını daha kesin hale getirmek için yeniden düzenlemek için düzenledim ^^
Olivier Dulac

1

Bence buradaki kafa karıştırıcı kısım stderr'ın stdout'a yönlendirilmesinin aslında iki akışı birbirine bağladığı yanlış anlama.

Mükemmel mantıklı bir fikir ama yazdığınızda olan 2>&1şey stderr, stdout'un ne yazdığını bir peekaboo alır ve aynı yere yazar. Bu nedenle, daha sonra stdout'a başka bir yere yazmasını söylerseniz, zaten taşınmış olan stderr hedefi üzerinde hiçbir etkisi yoktur.

Bence bu biraz mantıksız bir şey ama işte böyle çalışıyor. Önce yazmak istediğiniz yeri ayarlayın, sonra herkese "kopyala" deyin. Umarım açıklar ...


0

DUPLICATION ...

önemlidir, daha çok karışıklığın kaynağı olması anlamındadır . Bu gerçekten oldukça basit. Bu cevap sadece "radikal" bir örnek.

Kabul edilen cevap iyidir, ancak çok uzundur ve "tekrarlama" yı vurgular.

Q akıllıca:

Kelime çoğaltma kullanımı bu bağlamda bana biraz garip geliyor. Belki de beni atar.

Bash gösterimini kullanıyorum ve "1" ve "2" dosya tutamaçları olarak "one" ve "two" değişkenlerini tanımlıyorum. (Çıktı) yönlendirme operatörü >bir ödevdir =. &ve $ortalama değeri.

Man bash örnekleri (varsayılan olarak "1" eklenmiştir)

ls 1>dirlist 2>&1      # both to dirlist
ls 2>&1 1>dirlist      # 1 to dirlist, 2 stays on tty/screen 

olmak:

one=dirlist  two=$one

ve

two=$one   one=dirlist

Ve bu bile benim için otomatik değil ve bazıları sanırım. İlk satır sizi terk eder $oneve $twoher ikisi de "dirlist" içerir. Elbette.

İkinci satır işe yaramaz bir ödevle başlar. Her ikisi de tanım olarak "TTY" (biraz sembolik) yönü olarak başlar ; bu atama tarafından hiçbir değer değiştirilmez ve dosya tanıtıcılarındaki gibi değişkenlerle sihirli bir şekilde hiçbir şey bağlantılı değildir. Değişken twoaşağıdakilerden etkilenmez one=dirlist. Tabii ki değil.

Buradaki biri (6 yıl önce), "kopya" veya "kopya" yerine "noktaya" önerdi ve sonra fark etti: bu da kafa karıştırıcı olurdu.

Bu çoğaltma veya işaret semantiği bile gerekli değildir. Belki de daha fazla dikkat gerektiren ve işareti. "/" Operatörü / belirteci / değeri

Konsolunuzda şaşırtıcı bir iş numarası almanın bir yolunu arıyorsanız - ve sadece eğer, "tamam " mesajı artı bonus olarak "2" adlı bir dosya arıyorsanız, o zaman devam edersiniz:

ls 1>2& 2>/dev/null

Doğal olarak " kopya" / "yinelenen" 1'den 2'ye ve sonra her ikisini birlikte null değerine okur . Ancak fikir yanlıştır ve sözdizimi de vardır. (ancak sözdizimi hatası yok, geçerli)

Planlamanın doğru yolu, ikisinden herhangi birini null değerine yönlendirmek ve ardından DİĞER'İ AYNI yere yönlendirmektir:

ls 1>/dev/null 2>&1
# or 
ls 2>/dev/null 1>&2

(önde gelen "1" bırakılabilir)

(Tamam acc. A çok uzun değil, ama çok fazla bir liste - veya: çok iyi görselleştirme, çok iyi bir açıklama değil)

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.