ในทุบตีอ่านหลังจากท่อไม่ได้ตั้งค่า


22

แก้ไข: ชื่อเดิมคือ "อ่านล้มเหลวในการทุบตี"

ด้วย ksh ฉันใช้อ่านเป็นวิธีที่สะดวกในการแยกค่า:

$ echo 1 2 3 4 5 | read a b dump
$ echo $b $a 
2 1
$

แต่มันล้มเหลวในการทุบตี:

$ echo 1 2 3 4 5 | read a b dump
$ echo $b $a 

$

ฉันไม่พบเหตุผลในหน้าคู่มือทำไมจึงล้มเหลวความคิดใด ๆ


2
นี้จะกล่าวถึง (ค่อนข้างคลุมเครือ) ในหน้า 024 ของเกร็กทุบตีคำถามที่พบบ่อย
สกอตต์

คำตอบ:


27

bash เรียกใช้ทางด้านขวามือของไปป์ไลน์ในบริบทของเชลล์ย่อยดังนั้นการเปลี่ยนแปลงตัวแปร (ซึ่งเป็นสิ่งที่readทำ) จะไม่ได้รับการเก็บรักษาไว้ - พวกมันจะตายเมื่อ subshell ทำในตอนท้ายของคำสั่ง

แต่คุณสามารถใช้การทดแทนกระบวนการได้ :

$ read a b dump < <(echo 1 2 3 4 5)
$ echo $b $a
2 1

ในกรณีreadนี้ทำงานอยู่ภายในเชลล์หลักของเราและคำสั่งที่สร้างเอาต์พุตของเราจะทำงานในเชลล์ย่อย <(...)ไวยากรณ์สร้าง subshell และเชื่อมต่อการส่งออกไปยังท่อซึ่งเราเปลี่ยนเส้นทางลงในการป้อนข้อมูลของreadกับสามัญการดำเนินงาน< เนื่องจากreadวิ่งในเปลือกหลักของเราตัวแปรจึงตั้งค่าไว้อย่างถูกต้อง

ตามที่ระบุไว้ในความคิดเห็นหากเป้าหมายของคุณคือการแยกสตริงเป็นตัวแปรอย่างใดคุณสามารถใช้สตริงที่นี่ :

read a b dump <<<"1 2 3 4 5"

ฉันคิดว่ามีมากกว่านั้น แต่นี่เป็นตัวเลือกที่ดีกว่าถ้าไม่มี


3
read a b dump <<< '1 2 3 4 5'หรือแม้กระทั่ง
choroba

ขอบคุณทุกคน btw ฉันสังเกตเห็นว่า mksh (บน cygwin) ทำเช่นเดียวกับทุบตี
Emmanuel

@Michael Homer คำอธิบายดีมาก! ฉันพบอีกตัวอย่างหนึ่งที่สามารถอธิบายได้ว่าทุกคำสั่งในไปป์ไลน์ทำงานใน subshell ของตัวเอง: cat /etc/passwd | (read -r line ; echo $line). แต่ต่อไปechoของการ$lineที่ไม่ได้อยู่ในท่อใส่อะไรบนหน้าจอเนื่องจากค่าถูกมีอยู่เพียงระหว่างวงเล็บ (subshell) หวังว่าจะช่วยให้ใครบางคน
Yurij Goncharuk

17

นี่ไม่ใช่bashข้อผิดพลาดที่POSIXช่วยให้ทั้งสองbashและkshพฤติกรรมนำไปสู่ความแตกต่างโชคร้ายที่คุณกำลังสังเกต

http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_12

นอกจากนี้แต่ละคำสั่งของไปป์ไลน์แบบหลายคำสั่งอยู่ในสภาพแวดล้อม subshell เป็นส่วนขยายอย่างไรก็ตามคำสั่งใด ๆ หรือทั้งหมดในไปป์ไลน์อาจถูกเรียกใช้งานในสภาพแวดล้อมปัจจุบัน คำสั่งอื่น ๆ ทั้งหมดจะถูกดำเนินการในสภาพแวดล้อมเชลล์ปัจจุบัน

อย่างไรก็ตามด้วยbash 4.2และใหม่กว่าคุณสามารถตั้งค่าlastpipeตัวเลือกในสคริปต์แบบไม่โต้ตอบเพื่อให้ได้ผลลัพธ์ที่คาดหวังเช่น:

#!/bin/bash

echo 1 2 3 4 5 | read a b dump
echo before: $b $a 
shopt -s lastpipe
echo 1 2 3 4 5 | read a b dump
echo after: $b $a 

เอาท์พุท:

before:
after: 2 1

1
+1 ขอบคุณสำหรับข้อมูล "lastpipe" ขออภัยในความล่าช้า
Emmanuel

1
ปัญหาlastpipeคือมันไม่ทำงานในเชลล์อื่น ๆ (เช่นเส้นประ) โดยทั่วไปแล้วไม่มีวิธีที่จะทำสิ่งนี้แบบพกพาสั้น ๆ ของการทำงานทุกอย่างใน subshell นั้นดูstackoverflow.com/questions/36268479/…
anarcat

1
@anarcat นี้ถูกต้อง แต่คำถามที่ถามที่นี่เกี่ยวกับทุบตี
jlliagre

@anarcat: อาจมีการเปลี่ยนแปลงในอนาคตเนื่องจากมีความประสงค์ที่จะเปลี่ยน POSIX ด้วยเหตุผลอื่น (สถานะ PIPE) ดูที่นี่: unix.stackexchange.com/questions/476834/การเปลี่ยนเปลือกหอยอื่น ๆ นั้นไม่สำคัญฉันใช้เวลาหลายเดือน เพื่อเขียน parser และ interpeter ใหม่บน Bourne Shell (bosh) เพื่อใช้งานพฤติกรรมที่รวดเร็วขึ้นของ ksh ที่ทันสมัย
schily
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.