คือสิ่งที่แตกต่างระหว่าง<<
, <<<
และ< <
ในทุบตี?
คือสิ่งที่แตกต่างระหว่าง<<
, <<<
และ< <
ในทุบตี?
คำตอบ:
เอกสารที่นี่
<<
เรียกว่าhere-document
โครงสร้าง คุณแจ้งให้โปรแกรมทราบว่าอะไรคือข้อความสิ้นสุดและเมื่อใดก็ตามที่มีการเห็นตัวคั่นโปรแกรมจะอ่านเนื้อหาทั้งหมดที่คุณให้กับโปรแกรมเป็นอินพุตและทำงานตามที่ระบุ
นี่คือสิ่งที่ฉันหมายถึง:
$ wc << EOF
> one two three
> four five
> EOF
2 5 24
ในตัวอย่างนี้เราบอกให้wc
โปรแกรมรอEOF
สายอักขระจากนั้นพิมพ์คำห้าคำจากนั้นพิมพ์EOF
สัญญาณเพื่อบอกว่าเราป้อนข้อมูลเสร็จแล้ว ผลก็คล้ายกับทำงานwc
ด้วยตัวเองพิมพ์คำแล้วกดCtrlD
ในทุบตีเหล่านี้จะดำเนินการผ่านไฟล์ temp มักจะอยู่ในรูปแบบ/tmp/sh-thd.<random string>
ในขณะที่พวกเขาจะถูกนำไปใช้เป็นประท่อที่ไม่ระบุชื่อ สิ่งนี้สามารถสังเกตได้ผ่านการติดตามการเรียกของระบบด้วยstrace
คำสั่ง แทนที่bash
ด้วยsh
เพื่อดูวิธี/bin/sh
การเปลี่ยนเส้นทางนี้
$ strace -e open,dup2,pipe,write -f bash -c 'cat <<EOF
> test
> EOF'
นี่สตริง
<<<
here-string
เป็นที่รู้จักกัน แทนที่จะพิมพ์ข้อความคุณให้สตริงข้อความที่ทำไว้ล่วงหน้าให้กับโปรแกรม ตัวอย่างเช่นด้วยโปรแกรมเช่นที่bc
เราสามารถทำได้bc <<< 5*4
เพื่อรับเอาท์พุทสำหรับกรณีเฉพาะนั้นไม่จำเป็นต้องรัน bc แบบโต้ตอบ
นี่สตริงในทุบตีจะดำเนินการผ่านไฟล์ชั่วคราวมักจะอยู่ในรูปแบบ/tmp/sh-thd.<random string>
ซึ่งจะถูกยกเลิกการเชื่อมโยงในภายหลังจึงทำให้พวกเขาครอบครองพื้นที่หน่วยความจำบางส่วนชั่วคราว แต่ไม่ปรากฏในรายการของ/tmp
รายการไดเรกทอรีและมีอยู่อย่างมีประสิทธิภาพเป็นไฟล์ที่ไม่ระบุชื่อ ถูกอ้างอิงผ่าน file descriptor โดยเชลล์เองและ descriptor ไฟล์นั้นจะถูกสืบทอดโดยคำสั่งและทำซ้ำในภายหลังบน file descriptor 0 (stdin) ผ่านทางdup2()
ฟังก์ชัน สามารถสังเกตได้ผ่านทาง
$ 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
และผ่านการติดตาม syscalls (output สั้นลงเพื่อให้สามารถอ่าน; แจ้งให้ทราบว่าไฟล์ temp จะเปิดเป็น FD 3 ข้อมูลที่เขียนไปแล้วก็เป็นอีกครั้งที่เปิดO_RDONLY
สถานะเป็น FD 4 และต่อมายกเลิกการเชื่อมโยงแล้วdup2()
บน FD 0 ซึ่งเป็นกรรมพันธุ์ด้วยcat
ในภายหลัง ):
$ 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 +++
ความคิดเห็น: อาจเป็นเพราะที่นี่สตริงใช้ประโยชน์จากไฟล์ข้อความชั่วคราวมันเป็นเหตุผลที่เป็นไปได้ว่าทำไมสตริงที่นี่แทรกบรรทัดใหม่ต่อท้ายเสมอเนื่องจากไฟล์ข้อความตามคำนิยาม POSIXจะต้องมีบรรทัดที่ลงท้ายด้วยอักขระขึ้นบรรทัดใหม่
การทดแทนกระบวนการ
ดังที่tldp.orgอธิบาย
การทดแทนกระบวนการฟีดเอาต์พุตของกระบวนการ (หรือกระบวนการ) ลงใน stdin ของกระบวนการอื่น
ดังนั้นในผลกระทบนี้จะคล้ายกับท่อstdoutecho foobar barfoo | wc
ของคำสั่งหนึ่งไปยังอีกเช่น แต่แจ้งให้ทราบล่วงหน้า: ในmanpage ทุบตี<(list)
คุณจะเห็นว่ามันจะแสดงเป็น ดังนั้นโดยทั่วไปคุณสามารถเปลี่ยนทิศทางเอาต์พุตของคำสั่งหลายคำสั่ง (!)
หมายเหตุ:ในทางเทคนิคเมื่อคุณพูดว่า< <
คุณไม่ได้หมายถึงสิ่งหนึ่ง แต่สองเปลี่ยนเส้นทางเดียวกับ การเปลี่ยนเส้นทางและขั้นตอนของการส่งออกจาก<
<( . . .)
ตอนนี้จะเกิดอะไรขึ้นถ้าเราแค่ประมวลผลการทดแทน?
$ echo <(echo bar)
/dev/fd/63
อย่างที่คุณเห็นเชลล์สร้างตัวบ่งชี้ไฟล์ชั่วคราว/dev/fd/63
โดยที่เอาต์พุตไป (ซึ่งตามคำตอบของ Gilles คือไพพ์ที่ไม่ระบุชื่อ) นั่นหมายถึงการ <
เปลี่ยนเส้นทางตัวบอกไฟล์นั้นเป็นอินพุตในคำสั่ง
ตัวอย่างง่ายๆดังนั้นจะทำการทดแทนกระบวนการของการส่งออกจากสองคำสั่ง echo เป็น wc:
$ wc < <(echo bar;echo foo)
2 2 8
ดังนั้นที่นี่เราทำให้เชลล์สร้าง file descriptor สำหรับผลลัพธ์ทั้งหมดที่เกิดขึ้นในวงเล็บและเปลี่ยนเส้นทางที่เป็นอินพุตตามที่wc
คาดไว้ wc ได้รับกระแสนั้นจากสองคำสั่ง echo ซึ่งโดยตัวมันเองจะออกสองบรรทัดแต่ละคำมี และเหมาะสมเรามี 2 คำ 2 บรรทัดและ 6 ตัวอักษรบวกสองบรรทัดใหม่นับ
หมายเหตุด้านข้าง: การทดแทนกระบวนการอาจเรียกว่าbashism (คำสั่งหรือโครงสร้างที่ใช้งานได้ในเชลล์ขั้นสูงเช่นbash
แต่ไม่ได้ระบุโดย POSIX) แต่ถูกนำไปใช้ksh
ก่อนการมีอยู่ของ bash เป็นหน้า kshและคำตอบนี้แนะนำ เชลล์ชอบtcsh
และmksh
ไม่ได้ทดแทนกระบวนการ แล้วเราจะไปเปลี่ยนเส้นทางเอาต์พุตของหลาย ๆ คำสั่งไปยังคำสั่งอื่นโดยไม่ทดแทนกระบวนการได้อย่างไร? การจัดกลุ่มรวมถึงการวางท่อ!
$ (echo foo;echo bar) | wc
2 2 8
ได้อย่างมีประสิทธิภาพนี้เป็นเช่นเดียวกับตัวอย่างข้างต้น แต่นี้จะแตกต่างกันภายใต้ประทุนจากกระบวนการทดแทนเนื่องจากเราทำให้ stdout ของ subshell ทั้งหมดและ stdin ของการเชื่อมโยงกับท่อwc
ในทางกลับกันการทดแทนกระบวนการทำให้คำสั่งอ่าน descriptor ไฟล์ชั่วคราว
ดังนั้นถ้าเราสามารถจัดกลุ่มกับ piping ทำไมเราถึงต้องการการทดแทนกระบวนการ เพราะบางครั้งเราไม่สามารถใช้ท่อได้ ลองพิจารณาตัวอย่างด้านล่าง - เปรียบเทียบผลลัพธ์ของสองคำสั่งด้วยdiff
(ซึ่งต้องการสองไฟล์และในกรณีนี้เราจะให้สองตัวอธิบายไฟล์)
diff <(ls /bin) <(ls /usr/bin)
< <
ถูกนำมาใช้เมื่อมีการรับ stdin จากทดแทนกระบวนการ cmd1 < <(cmd2)
คำสั่งดังกล่าวอาจมีลักษณะเช่น: ตัวอย่างเช่นwc < <(date)
< <
ไม่ได้เป็นสิ่งที่ด้วยตัวเองในกรณีของกระบวนการทดแทนจะเป็นเพียง<
ตามมาด้วยอย่างอื่นที่เกิดขึ้นจะเริ่มต้นด้วย<
<<<
ถูกนำมาใช้ครั้งแรกโดยพอร์ต Unix ของ Plan 9 rc shell จากนั้นนำ zsh, bash และ ksh93 มาใช้ในภายหลัง ฉันจะไม่เรียกมันว่าเป็นการทุบตี
echo 'foo' | read; echo ${REPLY}
จะไม่ส่งคืนfoo
เนื่องจากread
เริ่มต้นใน sub-shell - piping เริ่ม sub-shell อย่างไรก็ตามread < <(echo 'foo'); echo ${REPLY}
ส่งคืนอย่างถูกต้องfoo
เนื่องจากไม่มี sub-shell
< <
เป็นข้อผิดพลาดทางไวยากรณ์:
$ cat < <
bash: syntax error near unexpected token `<'
< <()
เป็นการทดแทนกระบวนการ ( <()
) รวมกับการเปลี่ยนเส้นทาง ( <
):
ตัวอย่างที่วางแผนไว้:
$ wc -l < <(grep ntfs /etc/fstab)
4
$ wc -l <(grep ntfs /etc/fstab)
4 /dev/fd/63
ด้วยการทดแทนกระบวนการพา ธ ไปยังตัวอธิบายไฟล์จะถูกใช้เช่นชื่อไฟล์ ในกรณีที่คุณไม่ต้องการ (หรือไม่สามารถ) ใช้ชื่อไฟล์โดยตรงคุณรวมการทดแทนกระบวนการด้วยการเปลี่ยนเส้นทาง
เพื่อให้ชัดเจนไม่มี< <
ผู้ปฏิบัติงาน
<()
ให้ชื่อไฟล์ที่เหมือนกันดังนั้นจึงมีประโยชน์มากกว่า - < <()
เปลี่ยน stdin โดยที่ไม่จำเป็น ในwc
หลังเกิดขึ้นจะมีประโยชน์มากขึ้น มันอาจมีประโยชน์น้อยกว่าที่อื่น
< <
เป็นข้อผิดพลาดทางไวยากรณ์คุณอาจหมายถึงว่าcommand1 < <( command2 )
การเปลี่ยนเส้นทางอินพุตอย่างง่ายตามด้วยการทดแทนกระบวนการและคล้ายกันมาก แต่ไม่เทียบเท่ากับ:
command2 | command1
ความแตกต่างที่สมมติว่าคุณกำลังเรียกใช้bash
อยู่command1
ถูกเรียกใช้ใน subshell ในกรณีที่สองในขณะที่มันรันในเชลล์ปัจจุบันในอันแรก นั่นหมายความว่าตัวแปรที่ตั้งค่าไว้command1
จะไม่สูญหายไปด้วยชุดตัวเลือกการแทนที่กระบวนการ
< <
จะให้ข้อผิดพลาดทางไวยากรณ์ การใช้งานที่เหมาะสมมีดังนี้:
การอธิบายด้วยความช่วยเหลือของตัวอย่าง:
ตัวอย่างสำหรับ< <()
:
while read line;do
echo $line
done< <(ls)
ในตัวอย่างข้างต้นอินพุตไปยัง while loop จะมาจากls
คำสั่งที่สามารถอ่านทีละบรรทัดและecho
ed ในลูป
<()
ใช้สำหรับการทดแทนกระบวนการ ข้อมูลเพิ่มเติมและตัวอย่างสำหรับ<()
สามารถพบได้ที่ลิงค์นี้: