อ่านอินพุตของผู้ใช้ภายในลูป


103

ฉันมีสคริปต์ทุบตีซึ่งมีลักษณะดังต่อไปนี้

cat filename | while read line
do
    read input;
    echo $input;
done

แต่สิ่งนี้ไม่ได้ให้ผลลัพธ์ที่ถูกต้องแก่ฉันอย่างชัดเจนเมื่อฉันอ่านใน while ลูปมันพยายามอ่านจากชื่อไฟล์ไฟล์เนื่องจากการเปลี่ยนเส้นทาง I / O ที่เป็นไปได้

วิธีอื่นในการทำเช่นเดียวกัน?


สิ่งเดียวกันเกิดขึ้นเมื่อคุณเปลี่ยนผู้ใช้ใน bash และเรียกใช้คำสั่ง read ภายใต้ผู้ใช้ที่เปลี่ยนในสคริปต์
krupal

คำตอบ:


109

อ่านจากอุปกรณ์ปลายทางที่ควบคุม:

read input </dev/tty

ข้อมูลเพิ่มเติม: http://compgroups.net/comp.unix.shell/Fixing-stdin-inside-a-redirected-loop


12
-1 เนื่องจากจะหลีกเลี่ยงการเปลี่ยนเส้นทางอื่น ๆ ตัวอย่างเช่นbash yourscript < /foo/barจะรอการป้อนข้อมูลของผู้ใช้ซึ่งยอมรับได้เฉพาะเมื่ออ่านรหัสผ่าน คำตอบโดย @GordonDavisson เป็นที่นิยมสำหรับการใช้งานอื่น ๆ ทั้งหมด
tiwo

57

คุณสามารถเปลี่ยนเส้นทาง stdin ปกติไปยังหน่วยที่ 3 เพื่อเก็บไว้ในท่อ:

{ cat notify-finished | while read line; do
    read -u 3 input
    echo "$input"
done; } 3<&0

BTW หากคุณใช้catวิธีนี้จริงๆให้แทนที่ด้วยการเปลี่ยนเส้นทางและสิ่งต่างๆจะง่ายยิ่งขึ้น:

while read line; do
    read -u 3 input
    echo "$input"
done 3<&0 <notify-finished

หรือคุณสามารถสลับ stdin และหน่วย 3 ในเวอร์ชันนั้น - อ่านไฟล์ด้วยหน่วยที่ 3 และปล่อยให้ stdin อยู่คนเดียว:

while read line <&3; do
    # read & use stdin normally inside the loop
    read input
    echo "$input"
done 3<notify-finished

เหตุใดสคริปต์ที่สองของคุณจึงหยุดทำงาน
Luca Borrione

2
@LucaBorrione: ใช้แล้วเป็นยังไงบ้าง? กำลังรอให้คุณป้อนข้อมูล (โปรดทราบว่าread lineกำลังอ่านจากการแจ้งเตือนเสร็จสิ้น แต่ถ้าคุณเพิ่งเรียกใช้ตามที่เขียนread -u 3 inputคือการอ่านจากคอนโซล)
Gordon Davisson

3

ดูเหมือนว่าคุณจะอ่านสองครั้ง แต่ไม่จำเป็นต้องอ่านภายในลูป while นอกจากนี้คุณไม่จำเป็นต้องเรียกใช้คำสั่ง cat:

while read input
do
    echo $input
done < filename

4
เป้าหมายของ OP คือการอ่านภายในลูปมาจากผู้ใช้ในขณะที่การอ่านนอกลูปคือการอ่านจากไฟล์ ดังนั้นพวกเขาจึงต้องการการอ่านที่แตกต่างกันสองแบบจากแหล่งที่มาที่แตกต่างกันสองแหล่ง สิ่งนี้ชัดเจนทั้งจากข้อความของคำถาม (อธิบายreadพฤติกรรมภายในว่า "ไม่ถูกต้อง [เพราะ] พยายามอ่านจากไฟล์filename") และคำตอบที่ยอมรับ
Charles Duffy

3

ลองเปลี่ยนลูปดังนี้:

for line in $(cat filename); do
    read input
    echo $input;
done

การทดสอบหน่วย:

for line in $(cat /etc/passwd); do
    read input
    echo $input;
    echo "[$line]"
done

@ w2lame ทดสอบอีกครั้งเปลี่ยน "while" loop เป็น "for" loop - ทำงานให้ฉัน ลอง "sex -x" ดูว่าข้อผิดพลาดมาจาก
ไหน

4
อย่าใช้แมวดูคำตอบจาก Hai Vu
Fredrik Pihl

+1. สิ่งนี้ง่ายกว่ามากในการปรับใช้ตามความต้องการเฉพาะของฉันมากกว่าคำแนะนำอื่น
Nathan Wallace

1
ดูDon't Read Lines With Forบน Wooledge wiki นอกจากนี้shellcheck.netยังเตือนSC2013
Charles Duffy

1

ฉันพบพารามิเตอร์นี้ -u ด้วยการอ่าน

"-u 1" หมายถึง "อ่านจาก stdin"

while read -r newline; do
    ((i++))
    read -u 1 -p "Doing $i""th file, called $newline. Write your answer and press Enter!"
    echo "Processing $newline with $REPLY" # united input from two different read commands.
done <<< $(ls)

-6
echo "Enter the Programs you want to run:"
> ${PROGRAM_LIST}
while read PROGRAM_ENTRY
do
   if [ ! -s ${PROGRAM_ENTRY} ]
   then
      echo ${PROGRAM_ENTRY} >> ${PROGRAM_LIST}
   else
      break
   fi
done
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.