วิธีไพพ์อินพุตไปยัง Bash ในขณะที่วนซ้ำและเก็บรักษาตัวแปรหลังจากลูปสิ้นสุดลง


88

Bash อนุญาตให้ใช้: cat <(echo "$FILECONTENT")

Bash ยังอนุญาตให้ใช้: while read i; do echo $i; done </etc/passwd

เพื่อรวมสองสิ่งนี้ก่อนหน้านี้สามารถใช้ได้: echo $FILECONTENT | while read i; do echo $i; done

ปัญหาสุดท้ายคือมันสร้างเชลล์ย่อยและหลังจากที่ตัวแปร while ลูปสิ้นสุดลง iไม่สามารถเข้าถึงได้อีกต่อไป

คำถามของฉันคือ:

วิธีการบรรลุสิ่งนี้: while read i; do echo $i; done <(echo "$FILECONTENT")หรือกล่าวอีกนัยหนึ่ง: ฉันจะแน่ใจได้อย่างไรiมีชีวิตอยู่ในขณะที่วนซ้ำ?

โปรดทราบว่าฉันตระหนักถึงการใส่คำสั่ง while เข้าไป{}แต่สิ่งนี้ไม่สามารถแก้ปัญหาได้ (ลองนึกภาพว่าคุณต้องการใช้ฟังก์ชัน while loop in และiตัวแปรreturn )



ที่เกี่ยวข้อง: stackoverflow.com/questions/37229058/… . อธิบายตัวเลือกทั้งหมดรวมถึงการทดแทนกระบวนการที่กล่าวถึงด้านล่างlastpipeและข้อดีข้อเสีย
ivan_pozdeev

ที่เกี่ยวข้อง: ตัวแปรการปรับเปลี่ยนภายในขณะที่ห่วงไม่ได้จำได้
codeforester

คำตอบ:


117

สัญกรณ์ที่ถูกต้องสำหรับการทดแทนกระบวนการคือ:

while read i; do echo $i; done < <(echo "$FILECONTENT")

ค่าสุดท้ายของiกำหนดในลูปจะพร้อมใช้งานเมื่อลูปสิ้นสุดลง อีกทางเลือกหนึ่งคือ:

echo $FILECONTENT | 
{
while read i; do echo $i; done
...do other things using $i here...
}

วงเล็บปีกกาเป็นการดำเนินการจัดกลุ่ม I / O และไม่ได้สร้างซับเชลล์ขึ้นเอง ในบริบทนี้พวกเขาเป็นส่วนหนึ่งของไปป์ไลน์ดังนั้นจึงถูกเรียกใช้เป็น subshell แต่เป็นเพราะ|ไม่ใช่{ ... }. คุณพูดถึงสิ่งนี้ในคำถาม AFAIK คุณสามารถทำผลตอบแทนจากภายในฟังก์ชันเหล่านี้ได้


Bash ยังให้บริการในshoptตัวและหนึ่งในตัวเลือกมากมายคือ:

lastpipe

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

ดังนั้นการใช้สิ่งนี้ในสคริปต์ทำให้ modfied sumพร้อมใช้งานหลังจากลูป:

FILECONTENT="12 Name
13 Number
14 Information"
shopt -s lastpipe   # Comment this out to see the alternative behaviour
sum=0
echo "$FILECONTENT" |
while read number name; do ((sum+=$number)); done
echo $sum

การทำเช่นนี้ที่บรรทัดคำสั่งมักจะเรียกใช้ "job control is not active" (นั่นคือที่บรรทัดคำสั่งการควบคุมงานจะทำงานอยู่) การทดสอบโดยไม่ใช้สคริปต์ล้มเหลว

นอกจากนี้ตามที่Gareth Reesระบุไว้ในคำตอบของเขาบางครั้งคุณสามารถใช้สตริงที่นี่ :

while read i; do echo $i; done <<< "$FILECONTENT"

สิ่งนี้ไม่ต้องการshopt; คุณอาจสามารถบันทึกกระบวนการโดยใช้มันได้


ขออภัยในความไม่รู้ของฉัน ฉันรู้ว่านี่เป็นวิธีแก้ปัญหาที่ถูกต้องและฉันทำเครื่องหมายว่าเป็นคำตอบเพื่อให้ฉันได้ผล แต่ตอนที่ฉันเรียกใช้while read i; do echo $i; done < <(cat /etc/passwd); echo $iมันไม่ได้กลับบรรทัดสุดท้ายสองครั้ง ฉันทำอะไรผิด?
Wakan Tanka

@WakanTanka: ฉันต้องทดลองสักหน่อย ... ฉันเชื่อว่าคำตอบคือการอ่านที่ล้มเหลวiจะรีเซ็ตเป็นค่าว่างดังนั้นเสียงสะท้อนหลังลูปจึงสะท้อนบรรทัดว่าง
Jonathan Leffler

1
ความรุ่งโรจน์สำหรับการอ้างอิงการทดแทนกระบวนการ ฉันไม่รู้ตัว
Atcold

@Wakan Tanka: ได้ผลลัพธ์แบบเดียวกับของคุณฉันใช้while read i; do x=$i; done < <(cat /etc/passwd); echo i=$i; echo x=$xแล้วได้ผล // บางทีพฤติกรรมทุบตีเปลี่ยนไปในสมัยนั้น?
yurenchen

คุณเป็นผู้ช่วยชีวิต! มองหาวิธีแก้ปัญหาที่คล้ายกันมาหลายชั่วโมงแล้ว คุณเป็นคนเดียวที่ได้ผล
แพ็ต

28

Jonathan Leffler อธิบายวิธีการทำสิ่งที่คุณต้องการโดยใช้การทดแทนกระบวนการแต่ความเป็นไปได้อีกประการหนึ่งคือการใช้สตริงที่นี่ :

while read i; do echo "$i"; done <<<"$FILECONTENT"

ซึ่งจะช่วยประหยัดกระบวนการ


0

ฟังก์ชั่นนี้ทำให้ไฟล์ jpg ซ้ำกัน $ NUM เท่า (bash)

function makeDups() {
NUM=$1
echo "Making $1 duplicates for $(ls -1 *.jpg|wc -l) files"
ls -1 *.jpg|sort|while read f
do
  COUNT=0
  while [ "$COUNT" -le "$NUM" ]
  do
    cp $f ${f//sm/${COUNT}sm}
    ((COUNT++))
  done
done
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.