Apa perbedaan antara <<, <<< dan <<dalam bash?

102

Apa perbedaan antara <<, <<<dan < <dalam bash?

Searene
sumber
20
Setidaknya dalam masa Google-sentris ini, sulit untuk mencari operator berbasis simbol ini. Apakah ada mesin pencari tempat Anda bisa mencolokkan "<< <<< <<" dan mendapatkan sesuatu yang bermanfaat?
Daniel Griscom
11
@DanielGriscom Ada SymbolHound .
Dennis
1
@DanielGriscom Stack Exchange digunakan untuk mendukung simbol pencarian, tetapi kemudian sesuatu pecah dan tidak ada yang pernah memperbaikinya.
muru
Sudah ada di sana (dan sudah hampir satu tahun): Apa saja kontrol dan pengalihan shell?
Scott

Jawaban:

115

Dokumen di sini

<<dikenal sebagai here-documentstruktur. 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 wcprogram untuk menunggu EOFstring, lalu ketikkan lima kata, dan kemudian ketikkan EOFuntuk memberi sinyal bahwa kita telah selesai memberikan input. Efeknya, ini mirip dengan berjalan wcsendiri, 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 straceperintah. Ganti bashdengan shuntuk melihat bagaimana /bin/shmelakukan 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 bcdapat kita lakukan bc <<< 5*4untuk 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 /tmpentri 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_RDONLYflag sebagai fd 4 dan kemudian dihapus tautannya, kemudian dup2()ke fd 0, yang diwarisi catkemudian ):

$ 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/63mana 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 kshsebelum bash sebagai halaman man ksh dan jawaban ini menyarankan. Kerang suka tcshdan mkshbagaimanapun 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)
Sergiy Kolodyazhnyy
sumber
7
< <digunakan ketika seseorang mendapatkan stdin dari proses substitusi . Perintah tersebut akan terlihat seperti: cmd1 < <(cmd2). Misalnya,wc < <(date)
John1024
4
Ya, substitusi proses didukung oleh bash, zsh, dan AT&T ksh {88,93} tetapi tidak oleh pdksh / mksh.
John1024
2
< < bukanlah sesuatu dengan sendirinya, dalam hal proses substitusi itu hanya <diikuti oleh sesuatu yang lain yang mulai dengan<
immibis
1
@uru Sejauh yang saya tahu, <<<pertama kali diterapkan oleh port Unix dari shell rc Plan 9 kemudian diadopsi oleh zsh, bash dan ksh93. Saya tidak akan menyebutnya bashism.
jlliagre
3
Contoh lain di mana perpipaan tidak dapat digunakan: tidakecho 'foo' | read; echo ${REPLY} akan kembali , karena dimulai dalam sub-shell - perpipaan memulai sub-shell. Namun, kembali dengan benar , karena tidak ada sub-shell. fooreadread < <(echo 'foo'); echo ${REPLY}foo
Paddy Landau
26

< < 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.

muru
sumber
saya mendapatkan jawaban Anda bahwa, <<() lebih bermanfaat daripada <() kan?
solfish
1
@solfish <()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
muru
12

< <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 bashyang command1dijalankan di subkulit dalam kasus kedua ketika sedang berjalan di shell saat ini di yang pertama. Itu berarti variabel yang ditetapkan command1tidak akan hilang dengan varian subtitusi proses.

Jlliagre
sumber
11

< <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 lsperintah yang dapat dibaca baris demi baris dan echodiedit dalam loop.

<()digunakan untuk proses substitusi. Informasi lebih lanjut dan contoh untuk <()dapat ditemukan di tautan ini:

Proses penggantian dan pipa

mengintip
sumber