Đọc đầu vào trong bash bên trong vòng lặp while


97

Tôi đang có một tập lệnh bash giống như sau,

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

nhưng điều này rõ ràng không cung cấp cho tôi đầu ra phù hợp như khi tôi đọc trong vòng lặp while nó cố gắng đọc từ tên tệp của tệp vì có thể chuyển hướng I / O.

Bất kỳ cách nào khác để làm tương tự?


Điều tương tự cũng xảy ra khi bạn chuyển đổi người dùng trong bash và chạy lệnh đọc dưới dùng chuyển trong kịch bản
krupal

Câu trả lời:


106

Đọc từ thiết bị đầu cuối điều khiển:

read input </dev/tty

thêm thông tin: http://compgroups.net/comp.unix.shell/Fixing-stdin-inside-a-rediited-loop


12
-1, vì điều này sẽ phá vỡ bất kỳ chuyển hướng nào khác. Ví dụ: bash yourscript < /foo/barsẽ đợi người dùng nhập, điều này chỉ được chấp nhận khi đọc mật khẩu. Câu trả lời của @GordonDavisson thích hợp hơn cho tất cả các mục đích sử dụng khác.
tiwo

55

Bạn có thể chuyển hướng stdin thông thường qua đơn vị 3 để giữ cho nó bên trong đường dẫn:

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

BTW, nếu bạn thực sự đang sử dụng cách catnày, hãy thay thế nó bằng một chuyển hướng và mọi thứ trở nên dễ dàng hơn:

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

Hoặc, bạn có thể hoán đổi stdin và đơn vị 3 trong phiên bản đó - đọc tệp với đơn vị 3 và chỉ cần để stdin một mình:

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

tại sao tập lệnh thứ hai của bạn bị treo?
Luca Borrione

2
@LucaBorrione: Bạn đang sử dụng nó như thế nào? Có phải nó đang đợi bạn cung cấp đầu vào cho nó (lưu ý rằng read lineđang đọc từ thông báo kết thúc, nhưng nếu bạn chỉ chạy nó như được viết read -u 3 inputlà đọc từ bảng điều khiển)?
Gordon Davisson

4

Hãy thử thay đổi vòng lặp như sau:

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

Bài kiểm tra đơn vị:

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

@ w2lame Đã kiểm tra lại, thay đổi vòng lặp "while" thành "for" - phù hợp với tôi. Hãy thử "quan hệ tình dục -x" nhìn thấy đâu là lỗi xuất phát từ
dimba

4
không sử dụng con mèo, thấy câu trả lời từ Hải Vũ
Fredrik Pihl

+1. Điều này dễ thực hiện hơn nhiều cho các nhu cầu cụ thể của tôi so với các đề xuất khác.
Nathan Wallace

1
Xem Không đọc dòng với For trên wiki Wooledge. Ngoài ra, shellcheck.net cảnh báo SC2013
Charles Duffy

3

Có vẻ như bạn đã đọc hai lần, việc đọc bên trong vòng lặp while là không cần thiết. Ngoài ra, bạn không cần gọi lệnh cat:

while read input
do
    echo $input
done < filename

4
Mục tiêu của OP là để người dùng đọc bên trong vòng lặp, trong khi vòng ngoài là đọc từ tệp. Vì vậy, họ muốn hai lần đọc khác nhau, từ hai nguồn khác nhau một cách hợp pháp. Điều này rõ ràng cả từ văn bản của câu hỏi (mô tả readhành vi của bên trong là "không đúng [vì] nó cố gắng đọc từ tệp filename") và câu trả lời được chấp nhận của họ.
Charles Duffy

1

Tôi đã tìm thấy tham số này -u với đọc.

"-u 1" có nghĩa là "đọc từ 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
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.