Apa perbedaan antara <<
, <<<
dan < <
dalam bash?
Apa perbedaan antara <<
, <<<
dan < <
dalam bash?
Jawaban:
Dokumen di sini
<<
dikenal sebagai here-document
struktur. Anda membiarkan program tahu apa yang akan menjadi teks akhir, dan setiap kali pembatas itu terlihat, program akan membaca semua hal yang Anda berikan kepada program sebagai input dan melakukan tugas di atasnya.
Inilah yang saya maksud:
$ wc << EOF
> one two three
> four five
> EOF
2 5 24
Dalam contoh ini kita memberi tahu wc
program untuk menunggu EOF
string, lalu ketikkan lima kata, dan kemudian ketikkan EOF
untuk memberi sinyal bahwa kita telah selesai memberikan input. Efeknya, ini mirip dengan berjalan wc
sendiri, mengetik kata-kata, lalu menekanCtrlD
Dalam bash ini diimplementasikan melalui file temp, biasanya dalam bentuk /tmp/sh-thd.<random string>
, sedangkan di dash mereka diimplementasikan sebagai pipa anonim. Ini dapat diamati melalui pelacakan panggilan sistem dengan strace
perintah. Ganti bash
dengan sh
untuk melihat bagaimana /bin/sh
melakukan pengalihan ini.
$ strace -e open,dup2,pipe,write -f bash -c 'cat <<EOF
> test
> EOF'
Di sini string
<<<
dikenal sebagai here-string
. Alih-alih mengetik teks, Anda memberikan string teks yang sudah dibuat sebelumnya ke program. Sebagai contoh, dengan program seperti yang bc
dapat kita lakukan bc <<< 5*4
untuk mendapatkan output untuk kasus tertentu, tidak perlu menjalankan bc secara interaktif.
String-bash di sini diimplementasikan melalui file-file sementara, biasanya dalam format /tmp/sh-thd.<random string>
, yang kemudian tidak terhubung, sehingga membuat mereka menempati beberapa ruang memori sementara tetapi tidak muncul dalam daftar /tmp
entri direktori, dan secara efektif ada sebagai file anonim, yang mungkin masih ada direferensikan melalui file deskriptor oleh shell itu sendiri, dan deskriptor file yang diwarisi oleh perintah dan kemudian diduplikasi ke file deskriptor 0 (stdin) melalui dup2()
fungsi. Ini dapat diamati melalui
$ ls -l /proc/self/fd/ <<< "TEST"
total 0
lr-x------ 1 user1 user1 64 Aug 20 13:43 0 -> /tmp/sh-thd.761Lj9 (deleted)
lrwx------ 1 user1 user1 64 Aug 20 13:43 1 -> /dev/pts/4
lrwx------ 1 user1 user1 64 Aug 20 13:43 2 -> /dev/pts/4
lr-x------ 1 user1 user1 64 Aug 20 13:43 3 -> /proc/10068/fd
Dan melalui penelusuran syscalls (output disingkat agar mudah dibaca; perhatikan bagaimana file temp dibuka sebagai fd 3, data ditulis untuk itu, kemudian dibuka kembali dengan O_RDONLY
flag sebagai fd 4 dan kemudian dihapus tautannya, kemudian dup2()
ke fd 0, yang diwarisi cat
kemudian ):
$ strace -f -e open,read,write,dup2,unlink,execve bash -c 'cat <<< "TEST"'
execve("/bin/bash", ["bash", "-c", "cat <<< \"TEST\""], [/* 47 vars */]) = 0
...
strace: Process 10229 attached
[pid 10229] open("/tmp/sh-thd.uhpSrD", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
[pid 10229] write(3, "TEST", 4) = 4
[pid 10229] write(3, "\n", 1) = 1
[pid 10229] open("/tmp/sh-thd.uhpSrD", O_RDONLY) = 4
[pid 10229] unlink("/tmp/sh-thd.uhpSrD") = 0
[pid 10229] dup2(4, 0) = 0
[pid 10229] execve("/bin/cat", ["cat"], [/* 47 vars */]) = 0
...
[pid 10229] read(0, "TEST\n", 131072) = 5
[pid 10229] write(1, "TEST\n", 5TEST
) = 5
[pid 10229] read(0, "", 131072) = 0
[pid 10229] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=10229, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++
Opini: berpotensi karena di sini string menggunakan file teks sementara, itu adalah kemungkinan alasan di sini-string selalu menyisipkan baris baru, karena file teks dengan definisi POSIX harus memiliki garis yang diakhiri dengan karakter baris baru.
Substitusi proses
Seperti yang dijelaskan tldp.org ,
Substitusi proses memasukkan output dari suatu proses (atau proses) ke stdin dari proses lain.
Jadi pada dasarnya ini mirip dengan pemipaan stdout dari satu perintah ke yang lain, misalnya echo foobar barfoo | wc
. Tetapi perhatikan: di halaman bash Anda akan melihat bahwa itu dilambangkan sebagai <(list)
. Jadi pada dasarnya Anda dapat mengarahkan output dari beberapa perintah (!).
Catatan: secara teknis ketika Anda mengatakan < <
Anda tidak mengacu pada satu hal, tetapi dua pengalihan dengan satu <
dan proses pengalihan dari <( . . .)
.
Sekarang apa yang terjadi jika kita hanya memproses substitusi?
$ echo <(echo bar)
/dev/fd/63
Seperti yang Anda lihat, shell membuat deskriptor file sementara di /dev/fd/63
mana output berjalan (yang menurut jawaban Gilles , adalah pipa anonim). Itu berarti <
mengarahkan kembali deskriptor file itu sebagai input ke perintah.
Jadi contoh yang sangat sederhana adalah membuat proses substitusi output dari dua perintah echo ke wc:
$ wc < <(echo bar;echo foo)
2 2 8
Jadi di sini kita membuat shell membuat deskriptor file untuk semua output yang terjadi dalam tanda kurung dan mengarahkan ulang itu sebagai input ke wc
. Seperti yang diharapkan, wc menerima aliran itu dari dua perintah gema, yang dengan sendirinya akan menghasilkan dua baris, masing-masing memiliki kata, dan tepat kami memiliki 2 kata, 2 baris, dan 6 karakter ditambah dua baris baru dihitung.
Catatan Sisi: Substitusi proses dapat disebut sebagai bashism (perintah atau struktur yang dapat digunakan dalam shell canggih seperti bash
, tetapi tidak ditentukan oleh POSIX), tetapi itu diterapkan ksh
sebelum bash sebagai halaman man ksh dan jawaban ini menyarankan. Kerang suka tcsh
dan mksh
bagaimanapun tidak memiliki proses substitusi. Jadi bagaimana kita bisa berkeliling mengarahkan output dari beberapa perintah ke perintah lain tanpa proses substitusi? Pengelompokan plus perpipaan!
$ (echo foo;echo bar) | wc
2 2 8
Secara efektif ini sama dengan contoh di atas, Namun, ini berbeda di bawah tenda dari proses substitusi, karena kami membuat stdout dari seluruh subkulit dan stdin wc
terkait dengan pipa . Di sisi lain, proses substitusi membuat perintah membaca deskriptor file sementara.
Jadi jika kita dapat melakukan pengelompokan dengan perpipaan, mengapa kita perlu proses substitusi? Karena terkadang kita tidak bisa menggunakan perpipaan. Pertimbangkan contoh di bawah ini - membandingkan output dari dua perintah dengan diff
(yang membutuhkan dua file, dan dalam hal ini kami memberikannya dua file deskriptor)
diff <(ls /bin) <(ls /usr/bin)
< <
digunakan ketika seseorang mendapatkan stdin dari proses substitusi . Perintah tersebut akan terlihat seperti: cmd1 < <(cmd2)
. Misalnya,wc < <(date)
< <
bukanlah sesuatu dengan sendirinya, dalam hal proses substitusi itu hanya <
diikuti oleh sesuatu yang lain yang mulai dengan<
<<<
pertama kali diterapkan oleh port Unix dari shell rc Plan 9 kemudian diadopsi oleh zsh, bash dan ksh93. Saya tidak akan menyebutnya bashism.
echo 'foo' | read; echo ${REPLY}
akan kembali , karena dimulai dalam sub-shell - perpipaan memulai sub-shell. Namun, kembali dengan benar , karena tidak ada sub-shell. foo
read
read < <(echo 'foo'); echo ${REPLY}
foo
< <
adalah kesalahan sintaksis:
$ cat < <
bash: syntax error near unexpected token `<'
< <()
adalah subtitusi proses ( <()
) yang dikombinasikan dengan pengalihan ( <
):
Contoh yang dibuat-buat:
$ wc -l < <(grep ntfs /etc/fstab)
4
$ wc -l <(grep ntfs /etc/fstab)
4 /dev/fd/63
Dengan substitusi proses, path ke deskriptor file digunakan seperti nama file. Jika Anda tidak ingin (atau tidak bisa) menggunakan nama file secara langsung, Anda menggabungkan subtitusi proses dengan pengalihan.
Untuk menjadi jelas, tidak ada < <
operator.
<()
memberikan hal seperti nama file, jadi lebih bermanfaat secara umum - < <()
mengganti stdin di tempat yang mungkin tidak diperlukan. Dalam wc
, yang terakhir kebetulan lebih bermanfaat. Mungkin kurang bermanfaat di tempat lain
< <
adalah kesalahan sintaks, Anda mungkin berarti command1 < <( command2 )
yang merupakan pengalihan input sederhana diikuti oleh substitusi proses dan sangat mirip tetapi tidak setara dengan:
command2 | command1
Perbedaan dengan asumsi Anda menjalankan bash
yang command1
dijalankan di subkulit dalam kasus kedua ketika sedang berjalan di shell saat ini di yang pertama. Itu berarti variabel yang ditetapkan command1
tidak akan hilang dengan varian subtitusi proses.
< <
akan memberikan kesalahan sintaksis. Penggunaan yang benar adalah sebagai berikut:
Menjelaskan dengan bantuan contoh:
Contoh untuk < <()
:
while read line;do
echo $line
done< <(ls)
Pada contoh di atas, input ke loop sementara akan datang dari ls
perintah yang dapat dibaca baris demi baris dan echo
diedit dalam loop.
<()
digunakan untuk proses substitusi. Informasi lebih lanjut dan contoh untuk <()
dapat ditemukan di tautan ini: