3>&4-
adalah ekstensi ksh93 juga didukung oleh bash dan itu adalah kependekan 3>&4 4>&-
, yaitu 3 sekarang menunjuk ke tempat 4 dulu, dan 4 sekarang ditutup, jadi apa yang ditunjukkan oleh 4 sekarang telah pindah ke 3.
Penggunaan umum adalah dalam kasus di mana Anda telah menggandakan stdin
atau stdout
menyimpan salinannya dan ingin mengembalikannya, seperti di:
Misalkan Anda ingin menangkap stderr dari sebuah perintah (dan hanya stderr) sambil meninggalkan stdout sendirian dalam sebuah variabel.
Substitusi perintah var=$(cmd)
, membuat pipa. Ujung penulisan pipa menjadi cmd
stdout (file descriptor 1) dan ujung lainnya dibaca oleh shell untuk mengisi variabel.
Sekarang, jika Anda ingin stderr
pergi ke variabel, Anda bisa melakukan: var=$(cmd 2>&1)
. Sekarang keduanya fd 1 (stdout) dan 2 (stderr) pergi ke pipa (dan akhirnya ke variabel), yang hanya setengah dari yang kita inginkan.
Jika kita lakukan var=$(cmd 2>&1-)
(kependekan var=$(cmd 2>&1 >&-
), sekarang hanya cmd
stderr yang masuk ke pipa, tetapi fd 1 ditutup. Jika cmd
mencoba menulis output apa pun, itu akan kembali dengan EBADF
kesalahan, jika membuka file, itu akan mendapatkan fd gratis pertama dan file terbuka akan ditugaskan untuk itu stdout
kecuali perintah menjaga itu! Bukan juga yang kita inginkan.
Jika kita ingin stdout cmd
dibiarkan sendiri, yaitu menunjuk ke sumber daya yang sama dengan yang menunjuk ke luar substitusi perintah, maka kita perlu entah bagaimana membawa sumber daya itu ke dalam substitusi perintah. Untuk itu kita bisa melakukan salinan substitusi perintah di stdout
luar untuk membawanya masuk.
{
var=$(cmd)
} 3>&1
Yang merupakan cara yang lebih bersih untuk menulis:
exec 3>&1
var=$(cmd)
exec 3>&-
(yang juga memiliki manfaat mengembalikan fd 3 daripada menutupnya pada akhirnya).
Kemudian pada {
(atau exec 3>&1
) dan ke atas }
, fd 1 dan 3 menunjuk ke sumber daya yang sama yang ditunjukkan fd 1 pada awalnya. fd 3 juga akan menunjuk ke sumber daya itu di dalam substitusi perintah (substitusi perintah hanya mengalihkan fd 1, stdout). Jadi di atas, untuk cmd
, kita punya untuk fds 1, 2, 3:
- pipa ke var
- tidak tersentuh
- sama seperti apa yang 1 poin di luar substitusi perintah
Jika kami mengubahnya menjadi:
{
var=$(cmd 2>&1 >&3)
} 3>&1-
Maka itu menjadi:
- sama seperti apa yang 1 poin di luar substitusi perintah
- pipa ke var
- sama seperti apa yang 1 poin di luar substitusi perintah
Sekarang, kita sudah mendapatkan apa yang kita inginkan: stderr pergi ke pipa dan stdout tidak tersentuh. Namun, kami membocorkan bahwa fd 3 ke cmd
.
Sementara perintah (berdasarkan konvensi) menganggap fds 0 hingga 2 terbuka dan menjadi input, output dan kesalahan standar, mereka tidak menganggap fds lainnya. Kemungkinan besar mereka akan membiarkan fd 3 itu tidak tersentuh. Jika mereka membutuhkan deskriptor file lain, mereka hanya akan melakukan open()/dup()/socket()...
yang akan mengembalikan deskriptor file pertama yang tersedia. Jika (seperti skrip shell yang melakukan exec 3>&1
) mereka perlu menggunakannya fd
secara khusus, mereka pertama-tama akan menugaskannya untuk sesuatu (dan dalam proses itu, sumber daya yang dimiliki oleh fd 3 kami akan dirilis oleh proses itu).
Adalah praktik yang baik untuk menutup fd 3 karena cmd
tidak menggunakannya, tetapi bukan masalah besar jika kita membiarkannya ditugaskan sebelum kita menelepon cmd
. Masalahnya mungkin: bahwa cmd
(dan berpotensi proses lain yang ditimbulkannya) memiliki satu fd lebih sedikit tersedia untuk itu. Masalah yang berpotensi lebih serius adalah jika sumber daya yang ditunjuk oleh fd mungkin berakhir dipegang oleh proses yang cmd
dilatarbelakangi oleh itu . Ini bisa menjadi masalah jika sumber daya itu adalah pipa atau saluran komunikasi antar-proses lainnya (seperti ketika skrip Anda dijalankan sebagai script_output=$(your-script)
), karena itu berarti proses membaca dari ujung yang lain tidak akan pernah melihat akhir file sampai saat itu proses latar belakang berakhir.
Jadi di sini, lebih baik menulis:
{
var=$(cmd 2>&1 >&3 3>&-)
} 3>&1
Yang, dengan bash
dapat disingkat menjadi:
{
var=$(cmd 2>&1 >&3-)
} 3>&1
Untuk meringkas alasan mengapa itu jarang digunakan:
- itu gula non-standar dan hanya sintaksis. Anda harus menyeimbangkan penghematan beberapa penekanan tombol dengan membuat skrip Anda lebih portabel dan kurang jelas bagi orang-orang yang tidak terbiasa dengan fitur yang tidak biasa itu.
- Kebutuhan untuk menutup fd asli setelah menduplikatnya sering diabaikan karena sebagian besar waktu, kita tidak menderita akibatnya, jadi kita lakukan saja
>&3
alih - alih >&3-
atau >&3 3>&-
.
Bukti bahwa itu jarang digunakan, karena Anda tahu itu palsu di bash . Dalam bash compound-command 3>&4-
atau any-builtin 3>&4-
daun fd 4 ditutup bahkan setelah compound-command
atau any-builtin
telah kembali. Sebuah tambalan untuk memperbaiki masalah sekarang tersedia (2013-02-19).
{ var=$(cmd 2>&1 >&3) ; } 3>&1-
Bukankah itu salah ketik di penutupan 1?$(...)
).{...}
, fd 3 poin ke apa fd 1 digunakan untuk menunjuk dan fd 1 ditutup, kemudian setelah masuk$(...)
, fd 1 diatur ke pipa yang mengumpankan$var
, kemudian untukcmd
2 ke yang juga, dan kemudian 1 ke apa 3 poin untuk, itu adalah bagian luar 1. Fakta bahwa 1 tetap ditutup setelahnya adalah bug di bash, saya akan melaporkannya. ksh93 dari mana fitur itu berasal tidak memiliki bug itu.Ini berarti untuk mengarahkannya ke tempat yang sama dengan deskriptor file lainnya. Anda perlu melakukan hal ini sangat jarang, selain penanganan terpisah jelas dari kesalahan descriptor standar (
stderr
,fd 2
,/dev/stderr -> /proc/self/fd/2
). Ini bisa berguna dalam beberapa kasus kompleks.Panduan Skrip Bash Lanjutan memiliki contoh level log yang lebih panjang ini dan cuplikan ini:
Dalam Sorcery Sumber Mage kita misalnya menggunakannya untuk membedakan output yang berbeda dari blok kode yang sama:
Ini memiliki substitusi proses tambahan ditambahkan untuk alasan logging (VOYEUR memutuskan apakah data harus ditampilkan di layar atau hanya log), tetapi beberapa pesan harus selalu disajikan. Untuk mencapai itu, kami mencetaknya ke file descriptor 3 dan kemudian menanganinya secara khusus.
sumber
Di Unix, file ditangani oleh deskriptor file (bilangan bulat kecil, misalnya input standar adalah 0, output standar adalah 1, kesalahan standar adalah 2; saat Anda membuka file lain, mereka biasanya ditugaskan deskriptor terkecil yang tidak digunakan). Jadi, jika Anda mengetahui inards dari program ini, dan Anda ingin mengirim output yang masuk ke file deskriptor 5 ke output standar, Anda akan memindahkan deskriptor 5 ke 1. Di situlah
2> errors
berasal, dan konstruksi ingin2>&1
menduplikasi kesalahan ke dalam aliran output.Jadi, hampir tidak pernah digunakan (saya samar-samar ingat menggunakannya sekali atau dua kali dalam kemarahan dalam 25 + tahun penggunaan Unix yang hampir eksklusif), tetapi ketika dibutuhkan sangat penting.
sumber
5>&1
mengirim 5 ke tempat 1 pergi, lalu apa yang sebenarnya1>&5-
dilakukan, selain menutup 5?