Saya mencoba mengalihkan keduanya stdout
dan stderr
ke file hari ini, dan saya menemukan ini:
<command> > file.txt 2>&1
Ini tampaknya dialihkan stderr
ke stdout
pertama, dan kemudian hasilnya stdout
dialihkan ke file.txt
.
Namun, mengapa tidak memesan <command> 2>&1 > file.txt
? Orang tentu akan membaca ini sebagai (dengan asumsi eksekusi dari kiri ke kanan) perintah yang dieksekusi pertama, stderr
yang diarahkan ke stdout
dan kemudian, hasilnya stdout
ditulis untuk file.txt
. Tetapi di atas hanya mengalihkan stderr
ke layar.
Bagaimana shell menginterpretasikan kedua perintah?
command-line
bash
redirect
Latih Heartnet
sumber
sumber
bash
manual . Omong-omong, pengalihan bukan perintah.execv
-sistem panggilan keluarga dipanggil untuk benar-benar menyerahkan subproses ke perintah yang dimulai, shell kemudian keluar dari loop (tidak ada lagi kode yang dieksekusi dalam proses itu) ) dan tidak memiliki cara yang mungkin untuk mengendalikan apa yang terjadi sejak saat itu; Dengan demikian, semua pengalihan harus dilakukan sebelum eksekusi dimulai, sementara shell memiliki salinan dirinya yang sedang berjalan dalam prosesfork()
untuk menjalankan perintah Anda.Jawaban:
Ketika Anda menjalankan
<command> 2>&1 > file.txt
stderr diarahkan2>&1
ke ke mana stdout saat ini pergi, terminal Anda. Setelah itu, stdout diarahkan ke file oleh>
, tetapi stderr tidak diarahkan dengan itu, jadi tetap sebagai output terminal.Dengan
<command> > file.txt 2>&1
stdout pertama-tama diarahkan ke file oleh>
, kemudian2>&1
mengarahkan stderr ke tempat stdout pergi, yang merupakan file.Mungkin tampaknya kontra intuitif untuk memulai, tetapi ketika Anda memikirkan pengalihan dengan cara ini, dan ingat bahwa mereka diproses dari kiri ke kanan, itu jauh lebih masuk akal.
sumber
Mungkin masuk akal jika Anda melacaknya.
Pada awalnya, stderr dan stdout pergi ke hal yang sama (biasanya terminal, yang saya sebut di sini
pts
):Saya mengacu pada stdin, stdout dan stderr dengan nomor deskriptor file mereka di sini: mereka adalah file deskriptor 0, 1 dan 2 masing-masing.
Sekarang, di set pengalihan pertama, kami memiliki
> file.txt
dan2>&1
.Begitu:
> file.txt
:fd/1
sekarang pergi kefile.txt
. Dengan>
,1
adalah deskriptor file tersirat ketika tidak ada yang ditentukan, jadi ini adalah1>file.txt
:2>&1
:fd/2
sekarang pergi ke mana punfd/1
saat ini pergi:Di sisi lain, dengan
2>&1 > file.txt
, urutan dibalik:2>&1
:fd/2
sekarang pergi ke mana punfd/1
saat ini, yang berarti tidak ada yang berubah:> file.txt
:fd/1
sekarang menuju kefile.txt
:Poin penting adalah bahwa pengalihan tidak berarti bahwa deskriptor file yang dialihkan akan mengikuti semua perubahan di masa depan untuk deskriptor file target; itu hanya akan mengambil kondisi saat ini .
sumber
2.
bagian kedua;fd/1 -> file.txt
dan tidakfd/2 -> file.txt
.Saya pikir itu membantu untuk berpikir bahwa shell akan mengatur pengalihan di sebelah kiri pertama, dan menyelesaikannya sebelum mengatur pengalihan berikutnya.
Baris Perintah Linux oleh William Shotts mengatakan
ini masuk akal, tapi kemudian
tetapi sebenarnya, kita dapat mengarahkan stdout ke stderr setelah mengarahkan stderr ke file dengan efek yang sama
Jadi, in
command > file 2>&1
, shell mengirim stdout ke file, kemudian mengirim stderr ke stdout (yang sedang dikirim ke file). Padahal, padacommand 2>&1 > file
shell pertama-tama redirect stderr ke stdout (yaitu menampilkannya di terminal tempat stdout biasanya berjalan) dan kemudian setelah itu, mengarahkan ulang stdout ke file. TLCL menyesatkan dalam mengatakan bahwa kita harus mengarahkan ulang stdout pertama: karena kita dapat mengarahkan stderr ke file terlebih dahulu dan kemudian mengirim stdout ke sana. Apa yang tidak bisa kita lakukan adalah mengarahkan ulang stdout ke stderr atau sebaliknya sebelum diarahkan ke suatu file. Contoh lainKita mungkin berpikir ini akan membuang stdout ke tempat yang sama dengan stderr, tetapi tidak, itu mengarahkan ulang stdout ke stderr (layar) terlebih dahulu, dan kemudian hanya mengarahkan ulang stderr, seperti ketika kita mencobanya sebaliknya ...
Saya harap ini membawa sedikit cahaya ...
sumber
Anda sudah mendapatkan beberapa jawaban yang sangat bagus. Biar saya tekankan bahwa ada dua konsep berbeda yang terlibat di sini, pemahamannya sangat membantu:
Latar Belakang: Deskriptor file vs. tabel file
Deskriptor file Anda hanya angka 0 ... n, yang merupakan indeks dalam tabel deskriptor file dalam proses Anda. Dengan konvensi, STDIN = 0, STDOUT = 1, STDERR = 2 (perhatikan bahwa istilah
STDIN
dll. Di sini hanya simbol / makro yang digunakan oleh konvensi dalam beberapa bahasa pemrograman dan halaman manual, tidak ada "objek" sebenarnya yang disebut STDIN; untuk tujuan diskusi ini, STDIN adalah 0, dll.).Tabel deskriptor file itu sendiri tidak mengandung informasi apa pun tentang file sebenarnya. Sebaliknya, ini berisi pointer ke tabel file yang berbeda; yang terakhir berisi informasi tentang file fisik aktual (atau perangkat blok, atau pipa, atau apa pun yang dapat ditangani oleh Linux melalui mekanisme file) dan informasi lebih lanjut (yaitu, apakah itu untuk membaca atau menulis).
Jadi, ketika Anda menggunakan
>
atau<
di shell Anda, Anda cukup mengganti pointer dari masing-masing file descriptor untuk menunjuk ke hal lain. Sintaks2>&1
hanya menunjuk deskriptor 2 ke manapun 1 poin.> file.txt
cukup terbukafile.txt
untuk menulis dan membiarkan STDOUT (file decsriptor 1) menunjukkan itu.Ada barang lain, misalnya
2>(xxx)
(yaitu: membuat proses baru berjalanxxx
, membuat pipa, menghubungkan file descriptor 0 dari proses baru ke ujung pembacaan pipa dan menghubungkan file deskriptor 2 dari proses asli ke akhir penulisan pipa).Ini juga merupakan dasar untuk "file handle magic" pada perangkat lunak selain shell Anda. Misalnya, Anda bisa, dalam skrip Perl Anda,
dup
melisensikan deskriptor file STDOUT menjadi yang lain (sementara), lalu membuka kembali STDOUT ke file sementara yang baru dibuat. Mulai saat ini, semua output STDOUT dari skrip Perl Anda sendiri dan semuasystem()
panggilan skrip itu akan berakhir di file sementara itu. Setelah selesai, Anda dapatdup
mengirim kembali STDOUT Anda ke deskriptor sementara tempat Anda menyimpannya, dan presto, semuanya seperti sebelumnya. Anda bahkan dapat menulis ke deskriptor sementara itu, jadi sementara output STDOUT Anda yang sebenarnya pergi ke file sementara, Anda masih dapat benar-benar menampilkan barang-barang ke STDOUT nyata (umumnya, pengguna).Menjawab
Untuk menerapkan informasi latar belakang yang diberikan di atas untuk pertanyaan Anda:
Kiri ke kanan.
fork
dari proses baru.file.txt
dan simpan penunjuknya di file descriptor 1 (STDOUT).file.txt
tentu saja).exec
itu<command>
Ini masuk akal jika hanya ada satu meja, tetapi seperti yang dijelaskan di atas ada dua. File deskriptor tidak menunjuk satu sama lain secara rekursif, tidak masuk akal untuk berpikir "redirect STDERR ke STDOUT". Pikiran yang benar adalah "arahkan STDERR ke mana pun STDOUT menunjuk". Jika Anda mengubah STDOUT nanti, STDERR tetap di tempatnya, itu tidak secara ajaib sejalan dengan perubahan lebih lanjut ke STDOUT.
sumber
Perintahnya dari kiri ke kanan. Manual Bash telah membahas apa yang Anda minta. Kutipan dari
REDIRECTION
bagian manual:dan beberapa baris kemudian:
Penting untuk dicatat bahwa pengalihan diselesaikan terlebih dahulu sebelum perintah dijalankan! Lihat https://askubuntu.com/a/728386/295286
sumber
Selalu dari kiri ke kanan ... kecuali kapan
Sama seperti dalam Matematika kita melakukan dari kiri ke kanan kecuali perkalian dan pembagian dilakukan sebelum penambahan dan pengurangan, kecuali operasi di dalam tanda kurung (+ -) akan dilakukan sebelum perkalian dan pembagian.
Sesuai panduan pemula Bash di sini ( Panduan Pemula Bash ) ada 8 urutan hierarki tentang apa yang muncul lebih dulu (sebelum kiri ke kanan):
Jadi selalu dari kiri ke kanan ... kecuali ketika ...
sumber