Mengapa $ ls > ls.out
'ls.out' dimasukkan ke dalam daftar nama file di direktori saat ini? Mengapa ini dipilih? Kenapa tidak sebaliknya?
command-line
bash
redirect
Edward Torvalds
sumber
sumber
ls > ../ls.out
Jawaban:
Ketika mengevaluasi perintah,
>
pengalihan diselesaikan terlebih dahulu: sehingga pada saatls
berjalan file output telah dibuat.Ini juga merupakan alasan mengapa membaca dan menulis ke file yang sama menggunakan
>
pengalihan dalam perintah yang sama memotong file; pada saat perintah dijalankan file sudah terpotong:Trik untuk menghindari ini:
<<<"$(ls)" > ls.out
(berfungsi untuk perintah apa pun yang perlu dijalankan sebelum pengalihan diselesaikan)Substitusi perintah dijalankan sebelum perintah luar dievaluasi, jadi
ls
jalankan sebelumls.out
dibuat:ls | sponge ls.out
(berfungsi untuk perintah apa pun yang perlu dijalankan sebelum pengalihan diselesaikan)sponge
menulis ke file hanya ketika sisa pipa telah selesai dieksekusi, jadils
jalankan sebelumls.out
dibuat (sponge
disediakan denganmoreutils
paket):ls * > ls.out
(berfungsi untukls > ls.out
kasus khusus)Ekspansi nama file dilakukan sebelum pengalihan diselesaikan, sehingga
ls
akan berjalan pada argumennya, yang tidak akan berisils.out
:Tentang mengapa pengalihan diselesaikan sebelum program / skrip / apa pun dijalankan, saya tidak melihat alasan spesifik mengapa itu wajib untuk dilakukan, tetapi saya melihat dua alasan mengapa lebih baik untuk melakukannya:
tidak mengarahkan STDIN sebelumnya akan membuat program / skrip / apa pun yang ditahan sampai STDIN dialihkan;
tidak mengarahkan STDOUT sebelumnya harus membuat shell buffer keluaran program / script / apa pun hingga STDOUT dialihkan;
Jadi buang-buang waktu dalam kasus pertama dan buang-buang waktu dan memori dalam kasus kedua.
Inilah yang terjadi pada saya, saya tidak mengklaim ini adalah alasan sebenarnya; tapi saya kira semuanya, jika seseorang memiliki pilihan, mereka akan pergi dengan mengarahkan ulang sebelumnya karena alasan yang disebutkan di atas.
sumber
Dari
man bash
:Kalimat pertama, menunjukkan bahwa output dibuat untuk pergi ke tempat lain selain
stdin
dengan pengalihan tepat sebelum perintah dijalankan. Dengan demikian, agar dapat diarahkan ke file, file harus terlebih dahulu dibuat oleh shell itu sendiri.Untuk menghindari memiliki file, saya sarankan Anda mengarahkan output ke pipa bernama pertama, dan kemudian ke file. Perhatikan penggunaan
&
untuk mengembalikan kontrol atas terminal ke penggunaTapi kenapa?
Pikirkan tentang ini - di mana hasilnya? Sebuah program memiliki fungsi seperti
printf
,sprintf
,puts
, yang semua secara default pergi kestdout
, tapi bisa output mereka akan pergi ke file jika file tidak ada di tempat pertama? Ini seperti air. Bisakah Anda mendapatkan segelas air tanpa meletakkan gelas di bawah faucet terlebih dahulu?sumber
Saya tidak setuju dengan jawaban saat ini. File output harus dibuka sebelum perintah dijalankan atau perintah tidak akan memiliki tempat untuk menulis outputnya.
Ini karena "semuanya adalah file" di dunia kita. Output ke layar adalah SDOUT (alias file deskriptor 1). Untuk aplikasi yang menulis ke terminal, ia membuka fd1 dan menulisnya seperti file.
Ketika Anda mengarahkan output aplikasi dalam sebuah shell, Anda mengubah fd1 sehingga sebenarnya menunjuk ke file. Ketika Anda mem-pipe Anda mengubah STDOUT satu aplikasi menjadi STDIN yang lain (fd0).
Tapi itu semua baik mengatakan itu, tetapi Anda dapat dengan mudah melihat bagaimana ini bekerja
strace
. Ini hal yang cukup berat tetapi contoh ini cukup singkat.Di dalamnya
strace.out
kita bisa melihat sorotan berikut:Ini terbuka
ls.out
sebagaifd3
. Tulis saja. Memotong (menimpa) jika ada, jika tidak menciptakan.Ini sedikit juggling. Kami shunt STDOUT (fd1) ke fd10 dan menutupnya. Ini karena kami tidak mengeluarkan apa pun ke STDOUT yang asli dengan perintah ini. Itu selesai dengan menduplikasi pegangan tulis ke
ls.out
dan menutup yang asli.Ini dia mencari executable. Sebuah pelajaran mungkin untuk tidak memiliki jalan yang panjang;)
Kemudian perintah dijalankan dan orang tua menunggu. Selama operasi ini setiap STDOUT akan benar-benar dipetakan ke pegangan file terbuka
ls.out
. Ketika masalah anakSIGCHLD
, ini memberitahu orang tua prosesnya selesai dan itu dapat dilanjutkan. Itu berakhir dengan sedikit lebih juggling dan penutupanls.out
.Mengapa ada begitu banyak juggling? Tidak, saya juga tidak sepenuhnya yakin.
Tentu saja Anda dapat mengubah perilaku ini. Anda dapat buffer ke memori dengan sesuatu seperti
sponge
dan itu tidak akan terlihat dari perintah melanjutkan. Kami masih memengaruhi deskriptor file, tetapi tidak dengan cara sistem file yang terlihat.sumber
Ada juga artikel yang bagus tentang Implementasi pengalihan dan operator pipa di shell . Yang menunjukkan bagaimana pengalihan dapat diterapkan sehingga
$ ls > ls.out
dapat terlihat seperti:sumber