Apakah ini salah ketik di bagian redirection manual Bash?

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.

Sekarang, bagian terakhir itu membingungkan saya. Dalam hal ini, kesalahan standar apa pun akan dicetak ke terminal dan STDOUT akan pergi ke file dirlist. Itulah yang akan terjadi, tetapi bukan itu yang saya pahami manualnya.

Sepertinya harus mengatakan "karena kesalahan standar digandakan dari output standar SETELAH output standar dialihkan ke dirlist". Jika STDERR dikirim ke STDOUT sebelum STDOUT diarahkan ke file, maka bukankah file tersebut berisi STDOUT AND STDERR?

Dapatkah seseorang tolong jelaskan ini untuk saya? Apakah pemahaman bacaan saya buruk? Penggunaan kata duplikasi agak aneh bagi saya dalam konteks ini. Mungkin itu melempar saya.

Gregg Leventhal
sumber
1
Kasus klasik mencampur operasi yang "berdasarkan nilai" vs "dengan referensi". Ketika Anda menduplikasi file descriptor itu adalah operasi dengan nilai . Dalam pemrograman, setelah a = 1; b = a; a = 2Anda berharap a == 2 && b == 1untuk menjadi kenyataan. Pengalihan 2>&1serupa dengan b = apenugasan - itu berdasarkan nilai, bukan oleh referensi. 2>&1tidak menikahi deskriptor 2 untuk mengajukan deskriptor 1 untuk semua keabadian - mereka masih 2 deskriptor file yang berbeda, yang kebetulan mengarah ke file yang sama.
jw013

Jawaban:

23

Duplikasi adalah bagian yang sangat penting di sini.

Mari kita lihat di mana deskriptor file akan pergi sebelum pengalihan. Ini biasanya terminal saat ini, misalnya:

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

Sekarang, jika kita memanggil ls -ltanpa pengalihan, pesan output dan kesalahan pergi ke terminal saya di bawah /dev/pts/1.

Jika pertama kali kami mengarahkan ulang STDOUTke file ( ls -l > dirlist), tampilannya seperti ini:

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

Ketika kita kemudian mengarahkan STDERRke duplikat dari STDOUT's file descriptor ( ls -l > dirlist 2>&1), STDERRpergi ke duplikat dari /home/bon/dirlist:

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

Jika kita pertama - tama akan mengarahkan ulang STDERRke duplikat STDOUTdeskriptor file ( ls -l 2>&1):

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

dan kemudian STDOUT ke file ( ls -l 2>&1 > dirlist), kita akan mendapatkan ini:

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

Di sini, STDERRmasih menuju terminal.

Anda lihat, urutan di halaman manual sudah benar.


Menguji Pengalihan

Sekarang, Anda bisa mengujinya sendiri. Dengan menggunakan ls -l /proc/$$/fd/, Anda melihat ke mana STDOUT(dengan fd 1) dan STDERR(dengan fd 2), untuk proses saat ini:

$ 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

Mari kita buat skrip shell kecil yang menunjukkan di mana file penjelas Anda diarahkan. Dengan cara ini, kami selalu mendapatkan status saat memanggil ls, termasuk pengalihan dari shell panggilan.

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

(Dengan CtrlD, Anda mengirim file akhir dan menghentikan catperintah membaca dari STDIN.)

Sekarang, panggil skrip ini dengan berbagai kombinasi pengalihan:

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

Anda dapat melihat, bahwa file deskriptor 1 (untuk STDOUT) dan 2 (untuk STDERR) bervariasi. Untuk bersenang-senang, Anda juga bisa mengarahkan STDINdan melihat hasilnya:

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

(Pertanyaan tersisa untuk pembaca: Di mana file deskriptor 255 poin? ;-))

Dubu
sumber
+1 - jawaban yang sangat bagus. Contoh yang ditulis dengan sangat bagus dan luar biasa. Terima kasih!!!
slm
Saya mengerti, saya pikir kesalahpahaman saya adalah bahwa pengalihan akan tetap untuk semua perintah berikut, sehingga STDERR untuk sisa baris akan pergi ke STDOUT.
Gregg Leventhal
2

Tidak, manualnya benar.

Jika pada awalnya 1 menunjuk ke terminal, dan 2 juga ke terminal, maka:

command  2>&1   1>somewhere

evaluatino pengalihan akan terjadi dari kiri ke kanan.

Jadi ini akan mengevaluasi PERTAMA 2>&1, dan dengan demikian PERTAMA menyalin apa yang 1digunakan untuk menunjuk ke fd (yaitu, deskriptor file the terminal, biasanya / dev / tty) ke fd 2.

Jadi pada saat itu fd 2sekarang menunjuk ke tempat fd 1dulu menunjuk ke ( the terminal)

Dan MAKA mengevaluasi 1>somewherebagian, dan dengan demikian akan menyalin deskriptor file somewheredalam fd 1(jadi pada saat itu, fd 1sekarang menunjuk ke somewhere, dan fd 2masih menunjuk ke the terminal)

Jadi ia memang mencetak 1 menjadi "suatu tempat" dan 2 ke dalam terminal, karena 2 telah diduplikasi dari 1 SEBELUM 1 telah diubah.

Pesanan lainnya:

command  1>somewhere 2>&1

pertama-tama akan mengarahkan ulang fd 1ke somewhere, dan kemudian menyalin referensi yang sama ke fd 2, jadi pada akhir 2 juga menunjuk ke somewhere. Tetapi mereka tidak "terhubung" mulai sekarang. Masing-masing masih dapat dialihkan secara terpisah.

ex:

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

Pada akhir yang itu, fd 1menunjuk ke somewhere, dan fd 2diarahkan ke/dev/null

Nama biasa untuk fd 1adalah STDOUT (output standar), dan nama yang biasa untuk fd 2adalah STDERR (kesalahan standar, karena ini biasanya digunakan untuk menampilkan kesalahan tanpa mengganggu STDOUT)

Olivier Dulac
sumber
@ Michael-mrozek: terima kasih atas hasil editnya, tetapi saya bersikeras mengatakan "salin" daripada "duplikat" sebagai "duplikat" dapat membuat orang percaya bahwa mulai sekarang keduanya adalah "hal yang sama", yang tidak benar. mis:: cmd 1>somewhere 2>&1 ; exec 2>/dev/nullsetelah exec, hanya 2 yang telah dialihkan ke / dev / null (1 masih akan "di suatu tempat"). Saya perlu bantuan untuk menemukan cara untuk mengatakan "apa 1 menunjuk ke" bukan "fd 1", namun ... karena itu juga membingungkan ...
Olivier Dulac
1
Saya tidak yakin apa yang Anda maksudkan; kaulah yang mengubahnya dari "salin" ke "duplikat". Yang saya lakukan adalah menggunakan huruf besar dan memformat sesuatu, saya tidak mengubah sepatah kata pun
Michael Mrozek
doh ... ^^ maaf. Dan saya mengedit lagi untuk merumuskan kembali untuk membuat lebih tepat apa yang disalin ke dalam apa ^^
Olivier Dulac
1

Saya pikir bagian yang membingungkan di sini adalah kesalahpahaman bahwa mengarahkan stderr ke stdout sebenarnya menghubungkan dua aliran.

Gagasan yang sangat masuk akal, tetapi apa yang terjadi ketika Anda menulis 2>&1adalah stderr membutuhkan waktu lama untuk mengetahui apa yang sedang ditulis oleh stdout dan menulis ke tempat yang sama. Oleh karena itu jika Anda kemudian memberitahu stdout untuk pergi menulis di tempat lain itu tidak berpengaruh pada tujuan stderr yang sudah dipindahkan.

Saya pikir ini sedikit berlawanan dengan intuisi saya sendiri tetapi itulah cara kerjanya. Atur di mana Anda ingin menulis terlebih dahulu kemudian beri tahu semua orang "salin saya". Harapan yang menjelaskan ...

jrichemont
sumber
0

DUPLIKASI...

itu penting, tetapi lebih dalam arti bahwa itu adalah sumber dari banyak kebingungan . Ini sangat sederhana. Jawaban ini hanyalah ilustrasi "radikal".

Jawaban yang diterima baik, tetapi terlalu panjang dan menekankan "duplikasi".

Q dengan bijak berakhir dengan:

The penggunaan duplikasi kata tampaknya sedikit aneh bagi saya dalam konteks ini. Mungkin itu melempar saya.

Saya menggunakan notasi bash dan mendefinisikan variabel "satu" dan "dua" sebagai filehandles "1" dan "2". Operator redirection (output) >adalah tugas =. &dan $berarti "nilai" dari.

Contoh man bash (dengan default "1" ditambahkan)

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

menjadi:

one=dirlist  two=$one

dan

two=$one   one=dirlist

Dan bahkan ini tidak otomatis bagi saya, dan beberapa yang lain saya kira. Baris pertama memberi Anda $onedan $twokeduanya berisi "dirlist". Tentu saja.

Baris kedua dimulai dengan tugas yang tidak berguna. Keduanya mulai dengan definisi dengan "TTY" (sedikit simbolik) sebagai arah mereka ; tidak ada nilai yang diubah oleh penugasan ini, dan dengan variabel seperti menangani file, tidak ada yang terkait secara ajaib. Variabel twotidak terpengaruh oleh yang berikut ini one=dirlist. Tentu saja tidak.

Sombody di sini (6 tahun yang lalu) menyarankan "arahkan ke" alih-alih "salin" atau "duplikat", dan kemudian menyadari: itu akan membingungkan juga.

Duplikasi atau pointer semantik ini bahkan tidak diperlukan. Mungkin ampersand yang perlu lebih diperhatikan. "Nilai" operator / token / apa pun.

Jika - dan hanya jika - Anda mencari cara untuk mendapatkan nomor pekerjaan yang mengejutkan di konsol Anda , maka pesan "selesai" plus sebagai bonus file bernama "2", maka Anda pergi:

ls 1>2& 2>/dev/null

Bunyinya secara alami sebagai " copy" / "duplikat" 1 ke 2, dan kemudian keduanya bersama-sama menjadi nol . Tapi idenya salah, dan juga sintaksisnya. (tetapi tidak ada kesalahan sintaks, ini valid)

Cara yang tepat untuk merencanakannya adalah mengarahkan salah satu dari keduanya ke nol, dan kemudian mengarahkan LAINNYA ke tempat yang SAMA:

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

("1" terkemuka dapat ditinggalkan)

(OK acc. A tidak terlalu panjang, tetapi terlalu banyak daftar - atau: visualisasi yang sangat baik, penjelasan tidak begitu baik)

rastafile
sumber