Phân chia đầu ra lệnh bằng cách ngắt dòng?


9

Tôi có một lệnh trả về nhiều dòng. Để xử lý thêm tôi cần xử lý từng dòng duy nhất của những dòng đó.

Mã hiện tại của tôi hoạt động bằng cách sửa đổi IFS ( Dấu tách trường nội bộ ):

ROWS=$(some command returning multiple lines)

O=$IFS #save original IFS
IFS=$(echo -en "\n\b") # set IFS to linebreak

for ROW in ${ROWS[@]}
do
  echo "$ROW"
done

IFS=$O #restore old IFS

Tôi tự hỏi, có cách nào tốt hơn để truy cập vào các dòng duy nhất của đầu ra nhiều dòng, một mà không sửa đổi IFS không? Đặc biệt là khả năng đọc kịch bản của tôi trở nên tồi tệ bằng cách sửa đổi IFS.

Cập nhật: Tôi gặp khó khăn để có câu trả lời hoạt động, ví dụ: câu trả lời từ choroba:

while IFS= read -r line ; do
    let var+=line #line 42
done << $(sqlite3 -list -nullvalue NULL -separator ',' /var/log/asterisk/master.db "${QUERY}")
echo "$var" # line 44

đưa cho tôi

./bla.sh: row 44: Warning: here-document at line 43 delimited by end-of-file (wanted `$(sqlite3 -list -nullvalue NULL -separator , /var/log/asterisk/master.db ${QUERY})')
./bla.sh: row 42: let: echo "": syntax error: invalid arithmetic operator. (error causing character is \"""\").

có ai giúp tôi cái này không? Cảm ơn!


Câu trả lời của choroba nói phải làm < <(some command returning multiple lines), nhưng đó không phải là những gì bạn đang làm.
Mikel

Câu trả lời:


13

Thế còn readtrong một whilevòng lặp?

some command returning multiple lines | while IFS= read -r line ; do
    echo "$line"
done

Tuy nhiên, hãy cẩn thận, đường ống được chạy trong một khung con, điều đó có nghĩa là bạn không thể thay đổi các giá trị biến cho phần còn lại của tập lệnh. Nếu bạn cần một cái gì đó để tồn tại từ vòng lặp while của mình, bạn có thể sử dụng thay thế quy trình của bash:

while IFS= read -r line ; do
    let var+=line
done < <(some command returning multiple lines)
echo "$var"

Sử dụng thay thế quy trình là một giải pháp thông minh cho vấn đề này bash, nhưng đáng chú ý là phạm vi của các biến phụ chỉ là một vấn đề bash, làm điều tương tự zshsẽ không có vấn đề này.
Caleb

ksh cũng không có vấn đề này.
Didi Kohen

cảm ơn các bạn, tôi đã thử giải pháp này, nhưng tôi không thể làm cho nó hoạt động được, xin vui lòng xem bài viết gốc được cập nhật của tôi, cảm ơn!
stefan.at.wpf

@ stefan.at.wpf: Bạn không thể đặt dấu cách và ký hiệu đô la xung quanh tùy ý. Chúng có ý nghĩa.
choroba

3

Chỉ thiết lập IFSmột dòng mới là không đủ. (Nhân tiện, tại sao bạn cũng chia tách các ký tự backspace?)

Trong mã của bạn, ${ROWS[@]}(đó là một cách viết lạ $ROWS- ROWSkhông phải là một mảng) không được trích dẫn hai lần. (Nếu đó là bên trong dấu ngoặc kép, bạn sẽ nhận được một chuỗi duy nhất, vì ROWSkhông phải là một mảng.) Vì vậy, các vỏ tách giá trị của biến vào các lĩnh vực ở từng IFSnhân vật, sau đó đối xử với từng lĩnh vực làm mẫu glob. Ví dụ: nếu một trong các dòng được in bằng lệnh chứa ký tự đơn *, thì dòng này sẽ được thay thế bằng tên tệp trong thư mục hiện tại.

Bạn có thể tắt Globing với set -f. Trong hầu hết các trường hợp bạn cài đặt IFSđể sử dụng tính năng tách trường của shell, bạn cũng cần tắt tính năng tạo khối. Đặt lại với set +f.

Thành ngữ đáng tin cậy để đọc từng dòng đầu ra của lệnh là while IFS= read -r.

some command returning multiple lines |
while IFS= read -r ROW; do
  
done

Lưu ý rằng hầu hết các shell chạy từng lệnh của một đường ống trong một lớp con riêng biệt. Vì vậy, nếu bạn cần đặt các biến và sử dụng chúng sau vòng lặp, hãy bọc các lệnh này trong một nhóm cùng với vòng lặp. (Ksh và zsh là trường hợp ngoại lệ, chúng chạy lệnh cuối cùng của đường ống trong vỏ cha.)

some command returning multiple lines | {
  while IFS= read -r ROW; do
    
    row_count=$((row_count+1))
  done
  echo "There were $row_count rows."
}

1

Bạn đang trộn cú pháp here-document và here-string trong câu hỏi cập nhật của bạn.

Sử dụng tài liệu ở đây:

while IFS= read -r line ; do
    let var+=line #line 42
done <<ENDMARK
$(sqlite3 -list -nullvalue NULL -separator ',' /var/log/asterisk/master.db "${QUERY}")
ENDMARK

Hoặc ở đây-chuỗi:

while IFS= read -r line ; do
    let var+=line #line 42
done <<< $(sqlite3 -list -nullvalue NULL -separator ',' /var/log/asterisk/master.db "${QUERY}")
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.