Saya mencoba memahami bagaimana tepatnya Bash memperlakukan baris berikut:
$(< "$FILE")
Menurut halaman manual Bash, ini setara dengan:
$(cat "$FILE")
dan saya bisa mengikuti garis penalaran untuk baris kedua ini. Bash melakukan ekspansi variabel pada $FILE
, memasuki substitusi perintah, meneruskan nilai $FILE
ke cat
, cat output isi $FILE
ke output standar, substitusi perintah selesai dengan mengganti seluruh baris dengan output standar yang dihasilkan dari perintah di dalam, dan Bash mencoba untuk mengeksekusi seperti perintah sederhana.
Namun, untuk baris pertama yang saya sebutkan di atas, saya memahaminya sebagai: Bash melakukan substitusi variabel aktif $FILE
, Bash membuka $FILE
untuk membaca input standar, entah bagaimana input standar disalin ke output standar , substitusi perintah selesai, dan Bash mencoba untuk mengeksekusi standar yang dihasilkan keluaran.
Bisakah seseorang tolong jelaskan kepada saya bagaimana isi $FILE
dari stdin ke stdout?
sumber
bash
akan menafsirkan itu sebagaicat filename
", apakah maksud Anda perilaku ini khusus untuk memerintahkan penggantian? Karena jika saya berlari< filename
dengan sendirinya, bash tidak mengatasinya. Ini tidak akan menghasilkan apa-apa dan mengembalikan saya ke prompt.cat < filename
kecat filename
mana saya menentang dan dapat kembali.|
membuat pipa antara dua subproses (atau, dengan beberapa shell, dari subproses ke input standar shell). Operator shell$(…)
membuat pipa dari subproses ke shell itu sendiri (bukan ke input standar). Operator shell<
tidak melibatkan pipa, hanya membuka file dan memindahkan deskriptor file ke input standar.< file
tidak sama dengancat < file
(kecuali dizsh
mana rasanya$READNULLCMD < file
).< file
sempurna POSIX dan hanya terbukafile
untuk membaca dan kemudian tidak melakukan apa-apa (begitufile
dekat langsung). Itu$(< file)
atau`< file`
itu adalah operator khususksh
,zsh
danbash
(dan perilaku dibiarkan tidak ditentukan dalam POSIX). Lihat jawaban saya untuk detailnya.$(cmd1) $(cmd2)
biasanya akan sama dengan$(cmd1; cmd2)
. Tapi melihat kasus di manacmd2
adalah< file
. Jika kita katakan$(cmd1; < file)
, file tersebut tidak dibaca, tetapi, dengan$(cmd1) $(< file)
, itu. Jadi tidak benar untuk mengatakan bahwa$(< file)
itu hanya kasus biasa$(command)
dengan perintah< file
.$(< …)
adalah kasus khusus substitusi perintah, dan bukan penggunaan pengalihan biasa.$(<file)
(juga bekerja dengan`<file`
) adalah operator khusus dari shell Korn yang disalin olehzsh
danbash
. Memang terlihat sangat mirip dengan substitusi perintah tetapi sebenarnya tidak.Dalam shell POSIX, perintah sederhana adalah:
Semua bagian adalah opsional, Anda dapat memiliki pengalihan saja, hanya perintah, hanya tugas atau kombinasi.
Jika ada pengalihan tetapi tidak ada perintah, pengalihan dilakukan (jadi a
> file
akan terbuka dan memotongfile
), tetapi kemudian tidak ada yang terjadi. BegituTerbuka
file
untuk membaca, tetapi tidak ada yang terjadi karena tidak ada perintah. Jadifile
kemudian ditutup dan hanya itu. Jika$(< file)
itu adalah substitusi perintah yang sederhana , maka itu tidak akan berkembang.Dalam spesifikasi POSIX , dalam
$(script)
, jikascript
hanya terdiri dari pengalihan, yang menghasilkan hasil yang tidak ditentukan . Itu untuk memungkinkan perilaku khusus cangkang Korn.Dalam ksh (di sini diuji dengan
ksh93u+
), jika skrip terdiri dari satu dan hanya satu perintah sederhana (meskipun komentar diperbolehkan sebelum dan sesudah) yang hanya terdiri dari pengalihan (tidak ada perintah, tidak ada tugas) dan jika pengalihan pertama adalah stdin (fd 0) hanya input (<
,<<
atau<<<
) redirection, jadi:$(< file)
$(0< file)
$(<&3)
(juga$(0>&3)
sebenarnya karena itu berlaku operator yang sama)$(< file > foo 2> $(whatever))
tapi tidak:
$(> foo < file)
$(0<> file)
$(< file; sleep 1)
$(< file; < file2)
kemudian
<&3
) dikurangi karakter garis belakang yang tertinggal.seakan menggunakan
$(cat < file)
kecuali itucat
$(<${file=foo.txt})
atau$(<file$((++n)))
)Dalam
zsh
, itu sama kecuali bahwa bahwa perilaku khusus yang hanya dipicu ketika hanya ada satu input redirection berkas (<file
atau0< file
, tidak ada<&3
,<<<here
,< a < b
...)Namun, kecuali saat meniru shell lain, di:
yaitu ketika hanya ada pengalihan input tanpa perintah, di luar substitusi perintah,
zsh
menjalankan$READNULLCMD
(pager secara default), dan ketika ada pengalihan input dan output,$NULLCMD
(cat
secara default), bahkan jika$(<&3)
tidak diakui sebagai yang khusus operator, itu masih akan bekerja seperti diksh
olah dengan memanggil pager untuk melakukannya (pager itu bertindak seperticat
stdout akan menjadi pipa).Namun sementara
ksh
itu$(< a < b)
akan diperluas ke kontena
, dalamzsh
, itu berkembang ke kontena
danb
(atau hanyab
jikamultios
opsi dinonaktifkan),$(< a > b)
akan menyalina
keb
dan memperluas ke apa-apa, dll.bash
memiliki operator yang sama tetapi dengan beberapa perbedaan:komentar diperbolehkan sebelum tetapi tidak setelah:
bekerja tetapi:
mengembang tanpa apa-apa.
seperti di
zsh
, hanya satu pengalihan stdin file, meskipun tidak ada jatuh kembali ke$READNULLCMD
, jadi$(<&3)
,$(< a < b)
lakukan pengalihan tetapi memperluas ke apa-apa.bash
tidak memintacat
, itu masih memalsukan proses yang memberi makan konten file melalui pipa membuatnya jauh lebih sedikit dari optimasi daripada di shell lain. Ini berlaku seperti di$(cat < file)
manacat
akan menjadi builtincat
.$(<${file=foo.txt})
, yang disebutkan di atas misalnya, bahwa$file
penugasan hilang setelah itu).In
bash
,IFS= read -rd '' var < file
(juga berfungsizsh
) adalah cara yang lebih efektif untuk membaca konten file teks menjadi variabel. Ini juga memiliki manfaat melestarikan karakter baris baru. Lihat juga$mapfile[file]
dizsh
(dalamzsh/mapfile
modul dan hanya untuk file biasa) yang juga berfungsi dengan file biner.Perhatikan bahwa varian berbasis pdksh
ksh
memiliki beberapa variasi dibandingkan dengan ksh93. Yang menarik, dimksh
(salah satu dari shell yang diturunkan pdksh), didioptimalkan dalam hal isi dokumen di sini (tanpa karakter trailing) diperluas tanpa file atau pipa sementara yang digunakan seperti halnya kasus untuk dokumen di sini, yang menjadikannya sintaks kutipan multi-baris yang efektif.
Menjadi portabel untuk semua versi
ksh
,zsh
danbash
, yang terbaik adalah membatasi hanya dengan$(<file)
menghindari komentar dan mengingat bahwa modifikasi pada variabel yang dibuat di dalamnya mungkin atau tidak dapat dipertahankan.sumber
$(<)
adalah operator pada nama file? Apakah<
dalam$(<)
operator redirection, atau bukan operator sendiri, dan harus menjadi bagian dari seluruh operator$(<)
?$(<file)
dimaksudkan untuk memperluas ke kontenfile
dengan cara yang sama seperti$(cat < file)
akan. Cara melakukannya bervariasi dari shell ke shell yang dijelaskan panjang lebar dalam jawabannya. Jika Anda suka, Anda dapat mengatakan bahwa itu adalah operator khusus yang dipicu ketika apa yang tampak seperti substitusi perintah (secara sintaksis) berisi apa yang tampak seperti pengalihan stdin tunggal (secara sintaksis), tetapi sekali lagi dengan peringatan dan variasi tergantung pada shell seperti yang tercantum di sini .n<&m
dann>&m
lakukan hal yang sama? Saya tidak tahu itu, tapi saya kira itu tidak terlalu mengejutkan.dup(m, n)
. Saya bisa melihat beberapa bukti ksh86 menggunakan stdio dan beberapafdopen(fd, "r" or "w")
, jadi mungkin ada artinya. Tetapi menggunakan stdio di shell tidak masuk akal, jadi saya tidak berharap Anda akan menemukan shell modern di mana itu akan membuat perbedaan. Satu perbedaan adalah itu>&n
adalahdup(n, 1)
(kependekan dari1>&n
), sedangkan<&n
adalahdup(n, 0)
(kependekan dari0<&n
).dup2()
;dup()
hanya membutuhkan satu argumen dan, sepertiopen()
, menggunakan deskriptor file terendah yang tersedia. (Hari ini saya belajar bahwa adadup3()
fungsi .)Karena
bash
melakukannya secara internal untuk Anda, perluas nama file dan berikan file ke output standar, seperti jika Anda melakukannya$(cat < filename)
. Ini adalah fitur bash, mungkin Anda perlu melihatbash
kode sumber untuk mengetahui cara kerjanya.Di sini fungsi untuk menangani fitur ini (Dari
bash
kode sumber, filebuiltins/evalstring.c
):Catatan yang
$(<filename)
tidak persis setara dengan$(cat filename)
; yang terakhir akan gagal jika nama file dimulai dengan tanda hubung-
.$(<filename)
berasal dariksh
, dan ditambahkan kebash
dariBash-2.02
.sumber
cat filename
akan gagal jika nama file dimulai dengan tanda hubung karena cat menerima opsi. Anda dapat mengatasinya pada kebanyakan sistem modern dengancat -- filename
.Pikirkan substitusi perintah sebagai menjalankan perintah seperti biasa dan membuang output pada titik di mana Anda menjalankan perintah.
foo=$(echo "bar")
akan menetapkan nilai variabel$foo
kebar
; output dari perintahecho bar
.Substitusi Perintah
sumber
$(< file)
, dan dia tidak perlu tutorial tentang kasus umum. Jika Anda mengatakan itu$(< file)
hanya kasus biasa$(command)
dengan perintah< file
, maka Anda mengatakan hal yang sama yang dikatakan Adam Katz , dan Anda berdua salah.