เชลล์สคริปต์ในขณะที่อ่านลูปบรรทัดหยุดหลังจากบรรทัดแรก


107

ฉันมีเชลล์สคริปต์ต่อไปนี้ จุดประสงค์คือเพื่อวนผ่านแต่ละบรรทัดของไฟล์เป้าหมาย (ซึ่งพา ธ เป็นพารามิเตอร์อินพุตไปยังสคริปต์) และทำงานกับแต่ละบรรทัด ตอนนี้ดูเหมือนว่าจะใช้ได้เฉพาะกับบรรทัดแรกในไฟล์เป้าหมายและหยุดหลังจากที่บรรทัดนั้นได้รับการประมวลผล มีอะไรผิดปกติกับสคริปต์ของฉันหรือไม่?

#!/bin/bash
# SCRIPT: do.sh
# PURPOSE: loop thru the targets 

FILENAME=$1
count=0

echo "proceed with $FILENAME"

while read LINE; do
   let count++
   echo "$count $LINE"
   sh ./do_work.sh $LINE
done < $FILENAME

echo "\ntotal $count targets"

ในdo_work.shฉันเรียกใช้sshคำสั่งสองสามคำสั่ง


1
สคริปต์ของคุณใช้ได้ แต่อาจมีบางอย่างผิดปกติกับ do_work.sh
sleepsort

2
ใช่มันอาจกินการป้อนข้อมูลทั้งหมดหรือมันอาจจะเรียกว่าเป็นและก็ออกหรือsource execแต่รหัสนี้ดูไม่เป็นของแท้ OP จะสังเกตว่าเสียงสะท้อนต้องใช้-eเพื่อแสดงฟีดบรรทัดอย่างถูกต้อง ...
Michael Krelin - แฮ็กเกอร์

3
ไม่do_work.shทำงานsshโดยบังเอิญหรือไม่?
dogbane

1
ใช่ do_work.sh รันคำสั่ง ssh สองสามคำสั่ง มีอะไรพิเศษเกี่ยวกับเรื่องนี้?
bcbishop

1
ดีกว่าที่คุณแสดงdo_work.shแหล่งที่มาและยังใช้do.shกับset -xการแก้ปัญหา
koola

คำตอบ:


178

ปัญหาคือdo_work.shรันsshคำสั่งและโดยค่าเริ่มต้นsshอ่านจาก stdin ซึ่งเป็นไฟล์อินพุตของคุณ ด้วยเหตุนี้คุณจะเห็นเฉพาะบรรทัดแรกที่ประมวลผลเนื่องจากsshใช้ไฟล์ที่เหลือและลูป while ของคุณจะสิ้นสุดลง

เพื่อป้องกันสิ่งนี้ให้ส่ง-nตัวเลือกไปยังsshคำสั่งของคุณเพื่อให้อ่าน/dev/nullแทน stdin


1
มีประโยชน์มากช่วยฉันเรียกใช้ zsh oneliner นี้: cat hosts | ขณะอ่านโฮสต์ ทำ ssh $ host do_something; เสร็จแล้ว
หนู

3
@rat คุณยังคงต้องการที่จะหลีกเลี่ยงที่ไร้ประโยชน์ catคุณคิดว่าสัตว์ฟันแทะโดยเฉพาะจะระวังเรื่องนี้
tripleee

while read host ; do $host do_something ; done < /etc/hostsจะหลีกเลี่ยงมัน ค่อนข้างประหยัดชีวิตขอบคุณ!
หนู

httpieเป็นคำสั่งอื่นที่อ่าน STDIN ตามค่าเริ่มต้นและจะต้องทนทุกข์ทรมานจากพฤติกรรมเดียวกันเมื่อถูกเรียกภายใน bash หรือ fish loop ใช้http --ignore-stdinหรือตั้งค่าอินพุตมาตรฐาน/dev/nullเป็นด้านบน
รามัน

12

โดยทั่วไปวิธีแก้ปัญหาที่ไม่เฉพาะเจาะจงsshคือการเปลี่ยนทิศทางอินพุตมาตรฐานสำหรับคำสั่งใด ๆ ที่อาจใช้whileอินพุตของลูป

while read -r LINE; do
   let count++
   echo "$count $LINE"
   sh ./do_work.sh "$LINE" </dev/null
done < "$FILENAME"

การเพิ่ม</dev/nullเป็นจุดสำคัญที่นี่ (แม้ว่าการอ้างอิงที่ถูกต้องจะค่อนข้างสำคัญเช่นกันโปรดดูที่เมื่อใดที่จะรวมเครื่องหมายคำพูดรอบตัวแปรเชลล์ ) คุณจะต้องการที่จะใช้read -rเฉพาะถ้าคุณจำเป็นต้องมีมรดกพฤติกรรมแปลก ๆ -rออกไปเล็กน้อยคุณจะได้รับโดยไม่ต้อง

วิธีแก้ปัญหาอื่น ๆ ที่ค่อนข้างเฉพาะเจาะจงsshคือการตรวจสอบให้แน่ใจว่าsshคำสั่งใด ๆมีอินพุตมาตรฐานที่เชื่อมโยงกันเช่นการเปลี่ยน

ssh otherhost some commands here

เพื่ออ่านคำสั่งจากเอกสารที่นี่แทนซึ่งสะดวก (สำหรับสถานการณ์เฉพาะนี้) เชื่อมโยงอินพุตมาตรฐานของsshคำสั่ง:

ssh otherhost <<'____HERE'
    some commands here
____HERE

5

ตัวเลือก ssh -n ป้องกันการตรวจสอบสถานะการออกของ ssh เมื่อใช้ HEREdoc ในขณะที่ส่งเอาต์พุตไปยังโปรแกรมอื่น ดังนั้นควรใช้ / dev / null เป็น stdin

#!/bin/bash
while read ONELINE ; do
   ssh ubuntu@host_xyz </dev/null <<EOF 2>&1 | filter_pgm 
   echo "Hi, $ONELINE. You come here often?"
   process_response_pgm 
EOF
   if [ ${PIPESTATUS[0]} -ne 0 ] ; then
      echo "aborting loop"
      exit ${PIPESTATUS[0]}
   fi
done << input_list.txt

นี่ไม่สมเหตุสมผลเลย <<EOFแทนที่</dev/nullการเปลี่ยนเส้นทาง การ<<เปลี่ยนเส้นทางหลังจากdoneผิด
tripleee

1

สิ่งนี้เกิดขึ้นกับฉันเพราะฉันมีset -eและgrepในลูปกำลังส่งคืนโดยไม่มีเอาต์พุต (ซึ่งให้รหัสข้อผิดพลาดที่ไม่ใช่ศูนย์)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.