Mengapa perilaku `command 1> file.txt 2> file.txt` berbeda dari` command 1> file.txt 2> & 1`?

20

Ketika Anda ingin mengarahkan stdout dan stderr ke file yang sama, Anda dapat melakukannya dengan menggunakan command 1>file.txt 2>&1, atau command &>file.txt. Tetapi mengapa perilaku command 1>file.txt 2>file.txtberbeda dari dua perintah di atas?

Berikut ini adalah perintah verifikasi.

$ cat redirect.sh
#!/bin/bash

{ echo -e "output\noutput" && echo -e "error" 1>&2; } 1>file.txt 2>&1
{ echo -e "output\noutput" && echo -e "error" 1>&2; } 1>file1.txt 2>file1.txt
{ echo -e "error" 1>&2 && echo -e "output\noutput"; } 1>file2.txt 2>file2.txt
{ echo -e "output" && echo -e "error\nerror" 1>&2; } 1>file3.txt 2>file3.txt
{ echo -e "error\nerror" 1>&2 && echo -e "output"; } 1>file4.txt 2>file4.txt

$ ./redirect.sh

$ echo "---file.txt---"; cat file.txt;\
echo "---file1.txt---"; cat file1.txt; \
echo "---file2.txt---"; cat file2.txt; \
echo "---file3.txt---"; cat file3.txt; \
echo "---file4.txt----"; cat file4.txt;
 ---file.txt---
output
output
error
---file1.txt---
error

output
---file2.txt---
output
output
---file3.txt---
error
error
---file4.txt----
output
rror

Sejauh hasilnya terlihat, sepertinya string gema kedua menimpa gema string pertama ketika Anda menjalankan command 1>file.txt 2>file.txt, tapi saya tidak tahu mengapa itu akan terjadi. (Apakah ada referensi di suatu tempat?)

fhiyo
sumber

Jawaban:

43

Anda perlu tahu dua hal:

  • Deskriptor file terbuka yang dikenal ke sisi mode aplikasi dari suatu proses mereferensikan objek kernel internal yang dikenal sebagai deskripsi file , yang merupakan turunan dari file terbuka. Mungkin ada beberapa deskripsi file per file, dan beberapa deskriptor file berbagi deskripsi file.
  • The posisi file saat adalah atribut dari file deskripsi . Jadi jika beberapa file deskriptor memetakan ke satu deskripsi file, mereka semua berbagi posisi file saat ini yang sama, dan perubahan pada posisi file yang diberlakukan menggunakan satu file deskriptor tersebut mempengaruhi semua deskriptor file lainnya.

    Perubahan tersebut diberlakukan oleh proses memanggil read()/ readv(), write()/ writev(), lseek()panggilan, dan sistem sedemikian. The echoperintah panggilan write()/ writev()tentu saja.

Jadi yang terjadi adalah ini:

  • command 1>file.txt 2>&1hanya membuat satu deskripsi file, karena shell hanya membuka file sekali. Shell merek baik output standar dan standar deskriptor file error peta untuk deskripsi file tunggal. Ini menduplikasi output standar ke kesalahan standar. Jadi penulisan melalui salah satu deskriptor file akan memindahkan posisi file saat ini yang dibagikan: masing-masing menulis setelah sebelumnya menulis deskripsi file umum. Dan seperti yang Anda lihat, hasil dari echoperintah tidak saling menimpa.
  • command 1>file.txt 2>file.txtmembuat dua deskripsi file, karena shell membuka file yang sama dua kali, sebagai respons terhadap dua pengalihan eksplisit. Output standar dan deskriptor file kesalahan standar memetakan dua deskripsi file yang berbeda, yang kemudian memetakan ke file tunggal yang sama. Kedua deskripsi file memiliki posisi file sepenuhnya independen saat ini, dan masing-masing menulis segera pergi menulis sebelumnya pada deskripsi file yang sama. Dan seperti yang Anda lihat hasilnya adalah bahwa apa yang ditulis melalui satu dapat menimpa apa yang ditulis melalui yang lain, dalam berbagai cara berbeda sesuai dengan urutan apa Anda menjalankan penulisan.

Bacaan lebih lanjut

JdeBP
sumber
1
Seharusnya deskripsi file terbuka daripada deskripsi file . Ini lebih tentang catatan bagaimana file dibuka lebih dari file itu sendiri. Setidaknya itulah terminologi yang digunakan oleh dokumentasi POSIX, Linux, Solaris, dan GNU.
Stéphane Chazelas
16

Menggunakan >memberitahu untuk menimpa file. Karena Anda memiliki stdout dan stderr menulis ke file dalam dua operasi yang berbeda, yang terakhir untuk menulis akan menimpa yang pertama.

Anda dapat melakukan:

command 1>>file.txt 2>>file.txt

atau

command &>file.txt Hanya bash v4 ke atas.

>> memerintahkannya untuk menambahkan file sehingga tidak akan menggantikan output operasi sebelumnya.

&> hanyalah cara yang lebih mudah untuk menulis 2>&1

Jesse_b
sumber
2
mengapa ls 1>&0dan ls 0>&0masih menampilkan output ls?
Yvain
Saya terkejut bahwa menggunakan >>karya. Mengapa ini tidak memiliki masalah dua deskripsi file dengan offset independen? @ JdeBP, tahukah Anda? Saya berpikir bahwa membuka file dalam mode append sama dengan membuka dalam mode tulis, mencari ke posisi akhir, kemudian menolak mencari lebih lanjut.
JoL
4
@ jlmg: File mode-tambahan dapat dicari, tetapi setiap penulisan diawali dengan pencarian yang tersirat sampai akhir. Apakah pencarian tersirat itu atomik atau kurang jelas bagi saya.
Kevin
1
Itu sepenuhnya tergantung pada pertanyaan lanjutan. Yang seperti ini yang terkait ini menunjukkan bahwa jawabannya tidak lengkap. Dan tidak mungkin orang akan mencari pertanyaan lanjutan tanpa juga ingin tahu jawaban lengkapnya. Oleh karena itu sangat mungkin bahwa pertanyaan seperti itu akan memiliki jawaban yang menggandakan informasi, dan dengan demikian ditutup sebagai duplikat.
trlkly
1
@Kevin, pada sistem file yang sepenuhnya mendukung-POSIX, pencarian O_APPEND implisit bersifat atomik. Yang mengatakan, tidak setiap filesystem mengimplementasikan semantik yang relevan dengan benar - misalnya, NFS (setidaknya v3 dan sebelumnya - saya tidak jelas pada v4) tidak memiliki dukungan yang relevan dimasukkan ke dalam protokol kawat, sehingga server memiliki tidak ada cara untuk mengetahui apakah klien membuka file dengan O_APPEND.
Charles Duffy