Saya memiliki skrip bash tertentu, yang ingin mempertahankan /dev/stdout
lokasi asli sebelum mengganti deskriptor file pertama dengan lokasi lain.
Jadi, secara alami, saya menulis sesuatu seperti
old_stdout=$(readlink -f /dev/stdout)
Dan itu tidak berhasil. Sangat cepat saya mengerti apa masalahnya:
test@ubuntu:~$ echo $(readlink -f /dev/stdout)
/proc/5175/fd/pipe:[31764]
test@ubuntu:~$ readlink -f /dev/stdout
/dev/pts/18
Obvioulsly, $()
berjalan dalam subkulit, yang disalurkan ke shell induk.
Jadi pertanyaannya adalah: adakah cara yang dapat diandalkan (mencakup portabilitas antara distribusi Linux) untuk menyimpan /dev/stdout
lokasi sebagai string dalam skrip bash?
bash
io-redirection
io
alexey.e.egorov
sumber
sumber
/dev/stdout
akan menyelesaikan masalah dengan mencetak pesan dalam mode senyap. Alternatif mengarahkan ulang setiap tindakan lain yang menghasilkan output, dan ada cukup banyak dari mereka. Sekitar 100 kali lebih banyak dari pesan interaksi pengguna.stderr
. Ini, misalnya, mengapa prompt akanstderr
secara default.stderr
harus juga diarahkan dan disimpan, karena skrip memanggil sejumlah program eksternal, dan semua pesan kesalahan yang mungkin harus dikumpulkan dan dicatat.Jawaban:
Untuk menyimpan deskriptor file, Anda menduplikatnya di fd lain. Menyimpan jalur ke file yang sesuai tidak cukup, Anda harus menyimpan mode pembukaan, bendera pembuka, posisi saat ini di dalam file dan sebagainya. Dan tentu saja, untuk pipa anonim, atau soket, itu tidak akan berfungsi karena mereka tidak memiliki jalur. Yang ingin Anda simpan adalah deskripsi file terbuka yang mengacu pada fd, dan menduplikasi fd sebenarnya mengembalikan fd baru ke deskripsi file terbuka yang sama .
Untuk menggandakan deskriptor file ke file lain, dengan shell seperti Bourne, sintaksnya adalah:
Di atas, fd 1 diduplikasi ke fd 3.
Apa pun fd 3 yang sudah dibuka sebelumnya akan ditutup, tetapi perhatikan bahwa fds 3 hingga 9 (biasanya lebih, hingga 99 dengan
yash
) dicadangkan untuk tujuan itu (dan tidak memiliki arti khusus yang bertentangan dengan 0, 1, atau 2), shell tahu untuk tidak menggunakannya untuk bisnis internalnya sendiri. Satu-satunya alasan fd 3 akan dibuka sebelumnya adalah karena Anda melakukannya di skrip 1 , atau dibocorkan oleh penelepon.Kemudian, Anda dapat mengubah stdout menjadi sesuatu yang lain:
Dan nanti, untuk mengembalikan stdout:
(
3>&-
sedang untuk menutup file descriptor yang tidak lagi kita perlukan).Sekarang, masalah dengan itu adalah bahwa kecuali dalam ksh, setiap perintah yang Anda jalankan setelah itu
exec 3>&1
akan mewarisi fd 3. Itu kebocoran fd. Umumnya bukan masalah besar, tapi itu bisa menimbulkan masalah.ksh
mengatur flag close-on-exec pada fds tersebut (untuk fds lebih dari 2), tetapi tidak pada shell lain dan shell lain tidak memiliki cara untuk mengatur flag itu secara manual.Pekerjaan sekitar untuk shell lain adalah untuk menutup fd 3 untuk setiap perintah, seperti:
Tidak praktis. Di sini, cara terbaik adalah tidak menggunakan
exec
sama sekali, tetapi mengarahkan ulang grup perintah:Di sana, itu shell yang berhati-hati untuk menyimpan stdout dan mengembalikannya setelah itu (dan ia melakukannya secara internal dengan menduplikasi pada fd (di atas 9, di atas 99 untuk
yash
) dengan set flag close-on-exec ).Catatan 1
Sekarang, manajemen fds 3 hingga 9 dapat menjadi rumit dan bermasalah jika Anda menggunakannya secara luas atau dalam fungsi, terutama jika skrip Anda menggunakan beberapa kode pihak ketiga yang pada gilirannya dapat menggunakan fds tersebut.
Beberapa kerang (
zsh
,bash
,ksh93
, semua ditambahkan fitur ( disarankan oleh Oliver Kiddle darizsh
) sekitar waktu yang sama pada tahun 2005 setelah itu dibahas antara para pengembang mereka) memiliki sintaks alternatif untuk menetapkan fd bebas pertama di atas 10 bukan yang membantu dalam hal ini:sumber
rc.local
layanan, misalnya Jadi Anda benar-benar harus menggunakan sesuatu sepertiexec {FD}>&1
atau sesuatu. Tapi ini hanya didukung di bash 4, yang sangat menyedihkan. Jadi ini tidak benar-benar portabel.eval "exec $i>&1"
adalah hal yang ingin saya hindari, karena tidak praktis. Bisakah saya benar-benar mengandalkan bahwa fds di atas 9 akan gratis?bash
akan membiarkan Anda menembak diri sendiri di kaki.Seperti yang Anda lihat, skrip bash tidak seperti bahasa pemrograman biasa tempat Anda dapat menetapkan deskriptor file.
Solusi paling sederhana adalah dengan menggunakan sub-shell untuk menjalankan apa yang Anda inginkan diarahkan sehingga pemrosesan dapat dikembalikan ke atas-shell yang memiliki standar I / O.
Solusi alternatif akan digunakan
tty
untuk mengidentifikasi perangkat TTY dan mengontrol I / O dalam skrip Anda. Sebagai contoh:dan kemudian kamu bisa ..
sumber
$$
akan memberi Anda proses PID saat ini, dalam hal shell interaktif atau skrip PID shell yang relevan.Jadi Anda bisa menggunakan:
Contoh:
sumber
/proc
struktur tertentu menyebabkan masalah portabilitas, seperti halnya menggunakan/dev/stdout
seperti yang disebutkan dalam pertanyaan./proc
struktur tertentu? Ini akan bekerja pada Linux yang memilikiprocfs
..procfs
hampir selalu ada, tetapi kita sering melihat pertanyaan portabilitas dan metodologi pengembangan yang baik termasuk mempertimbangkan portabilitas ke sistem lain.bash
dapat berjalan pada banyak sistem operasi.