Tập lệnh Shell trong khi vòng lặp dòng đọc dừng sau dòng đầu tiên


107

Tôi có tập lệnh shell sau. Mục đích là lặp qua từng dòng của tệp đích (có đường dẫn là tham số đầu vào của tập lệnh) và thực hiện công việc đối với từng dòng. Bây giờ, nó dường như chỉ hoạt động với dòng đầu tiên trong tệp đích và dừng lại sau khi dòng đó được xử lý. Có điều gì sai với kịch bản của tôi?

#!/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"

Trong do_work.sh, tôi chạy một vài sshlệnh.


1
Kịch bản của bạn là tốt, nhưng có thể có một cái gì đó sai với do_work.sh
sleepsort

2
Có, nó có thể ăn hết đầu vào, hoặc nó có thể được gọi là sourcevà chỉ cần thoát ra hoặc exec. Nhưng mã này không giống chính hãng, OP sẽ nhận thấy tiếng vang mà yêu cầu -eđể thức ăn dòng hiển thị đúng cách ...
Michael Krelin - tin tặc

3
do_work.shchạy sshtheo bất kỳ cơ hội?
dogbane 10/12/12

1
vâng, do_work.sh chạy một vài lệnh ssh. có gì đặc biệt về điều đó?
bcbishop

1
Tốt hơn bạn nên hiển thị do_work.shnguồn và cũng chạy do.shvới set -xđể gỡ lỗi.
koola 10/12/12

Câu trả lời:


178

Vấn đề là do_work.shchạy sshcác lệnh và theo mặc định sshđọc từ stdin là tệp đầu vào của bạn. Kết quả là, bạn chỉ thấy dòng đầu tiên được xử lý, vì sshtiêu thụ phần còn lại của tệp và vòng lặp while của bạn kết thúc.

Để ngăn chặn điều này, hãy chuyển -ntùy chọn vào sshlệnh của bạn để làm cho nó được đọc từ /dev/nullthay vì stdin.


1
Rất hữu ích, đã giúp tôi chạy oneliner zsh này: cat hosts | trong khi đọc máy chủ; do ssh $ host do_something; done
rat

3
@rat Bạn vẫn muốn tránh những điều vô ích cat. Bạn sẽ nghĩ rằng một loài gặm nhấm nói riêng sẽ cảnh giác với điều này.
tripleee

while read host ; do $host do_something ; done < /etc/hostssẽ tránh nó. Đó là một tiết kiệm cuộc sống, cảm ơn!
rat

httpielà một lệnh khác đọc STDIN theo mặc định và sẽ bị hành vi tương tự khi được gọi bên trong vòng lặp bash hoặc cá. Sử dụng http --ignore-stdinhoặc đặt đầu vào tiêu chuẩn thành /dev/nullnhư trên.
Raman

12

Nói chung hơn, một cách giải quyết không dành riêng cho sshviệc chuyển hướng đầu vào tiêu chuẩn cho bất kỳ lệnh nào có thể tiêu tốn whileđầu vào của vòng lặp.

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

Việc bổ sung </dev/nulllà điểm quan trọng ở đây (mặc dù phần trích dẫn đã sửa cũng phần nào quan trọng; hãy xem thêm Khi nào đặt dấu ngoặc kép quanh một biến shell? ). Bạn sẽ muốn sử dụng read -rtrừ khi bạn yêu cầu cụ thể hành vi hơi kỳ lạ kế thừa mà bạn không có -r.

Một cách giải quyết khác của các loại sắp xếp hơi cụ thể sshlà đảm bảo rằng bất kỳ sshlệnh nào cũng có đầu vào chuẩn của nó bị ràng buộc, ví dụ: bằng cách thay đổi

ssh otherhost some commands here

thay vào đó đọc các lệnh từ một tài liệu tại đây, điều này thuận tiện (đối với trường hợp cụ thể này) liên kết đầu vào tiêu chuẩn sshcho các lệnh:

ssh otherhost <<'____HERE'
    some commands here
____HERE

5

Tùy chọn ssh -n ngăn chặn việc kiểm tra trạng thái thoát của ssh khi sử dụng HEREdoc trong khi chuyển đầu ra cho chương trình khác. Vì vậy, việc sử dụng / dev / null làm stdin được ưu tiên hơn.

#!/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

Điều này không có ý nghĩa. Việc <<EOFghi đè </dev/nullchuyển hướng. Sự <<chuyển hướng sau khi donesai.
tripleee

1

Điều này đã xảy ra với tôi bởi vì tôi đã có set -egreptrong một vòng lặp trả về không có đầu ra (đưa ra mã lỗi khác 0).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.