Câu trả lời:
Bạn có thể sử dụng vòng lặp while với quy trình thay thế:
while read -r line
do
echo "$line"
done < <(jobs)
Một cách tối ưu để đọc một biến đa dòng là đặt một IFS
biến trống và printf
biến trong một dòng mới:
# Printf '%s\n' "$var" is necessary because printf '%s' "$var" on a
# variable that doesn't end with a newline then the while loop will
# completely miss the last line of the variable.
while IFS= read -r line
do
echo "$line"
done < <(printf '%s\n' "$var")
Lưu ý: Theo shellcheck sc2031 , việc sử dụng trạm biến áp được ưu tiên hơn so với đường ống để tránh [tinh tế] tạo ra một lớp con.
Ngoài ra, xin vui lòng nhận ra rằng bằng cách đặt tên biến, jobs
nó có thể gây nhầm lẫn vì đó cũng là tên của một lệnh shell thông thường.
echo
thành printf %s
, để tập lệnh của bạn sẽ hoạt động ngay cả với đầu vào không thuần hóa.
/tmp
thư mục phải có thể ghi được, vì nó phụ thuộc vào việc có thể tạo một tệp công việc tạm thời. Nếu bạn thấy mình trên một hệ thống bị hạn chế ở chế độ /tmp
chỉ đọc (và không thể thay đổi bởi bạn), bạn sẽ hài lòng về khả năng sử dụng giải pháp thay thế, ví dụ như với printf
đường ống.
printf "%s\n" "$var" | while IFS= read -r line
Để xử lý đầu ra của một dòng lệnh theo dòng ( giải thích ):
jobs |
while IFS= read -r line; do
process "$line"
done
Nếu bạn đã có dữ liệu trong một biến:
printf %s "$foo" | …
printf %s "$foo"
gần như giống hệt echo "$foo"
, nhưng in theo $foo
nghĩa đen, trong khi echo "$foo"
có thể hiểu $foo
là một tùy chọn cho lệnh echo nếu nó bắt đầu bằng a -
và có thể mở rộng các chuỗi dấu gạch chéo ngược $foo
trong một số shell.
Lưu ý rằng trong một số shell (ash, bash, pdksh, nhưng không phải ksh hoặc zsh), phía bên phải của một đường ống chạy trong một quy trình riêng biệt, do đó, bất kỳ biến nào bạn đặt trong vòng lặp đều bị mất. Ví dụ: tập lệnh đếm dòng sau đây in 0 trong các shell này:
n=0
printf %s "$foo" |
while IFS= read -r line; do
n=$(($n + 1))
done
echo $n
Một cách giải quyết là đặt phần còn lại của tập lệnh (hoặc ít nhất là phần cần giá trị $n
từ vòng lặp) vào danh sách lệnh:
n=0
printf %s "$foo" | {
while IFS= read -r line; do
n=$(($n + 1))
done
echo $n
}
Nếu hành động trên các dòng không trống là đủ tốt và đầu vào không lớn, bạn có thể sử dụng phân tách từ:
IFS='
'
set -f
for line in $(jobs); do
# process line
done
set +f
unset IFS
Giải thích: cài đặt IFS
thành một dòng mới làm cho việc phân tách từ chỉ xảy ra ở dòng mới (trái ngược với bất kỳ ký tự khoảng trắng nào trong cài đặt mặc định). set -f
tắt Globing (tức là mở rộng ký tự đại diện), điều này sẽ xảy ra đối với kết quả của việc thay thế lệnh $(jobs)
hoặc thay thế biến $foo
. Các for
vòng lặp hoạt động trên tất cả các mảnh của $(jobs)
, đó là tất cả các dòng không trống trong đầu ra lệnh. Cuối cùng, khôi phục lại toàn cầu và IFS
cài đặt.
local IFS=something
. Nó sẽ không ảnh hưởng đến giá trị phạm vi toàn cầu. IIRC, unset IFS
không đưa bạn trở lại mặc định (và chắc chắn không hoạt động nếu trước đó không phải là mặc định).
Vấn đề: nếu bạn sử dụng vòng lặp while, nó sẽ chạy trong subshell và tất cả các biến sẽ bị mất. Giải pháp: sử dụng cho vòng lặp
# change delimiter (IFS) to new line.
IFS_BAK=$IFS
IFS=$'\n'
for line in $variableWithSeveralLines; do
echo "$line"
# return IFS back if you need to split new line by spaces:
IFS=$IFS_BAK
IFS_BAK=
lineConvertedToArraySplittedBySpaces=( $line )
echo "{lineConvertedToArraySplittedBySpaces[0]}"
# return IFS back to newline for "for" loop
IFS_BAK=$IFS
IFS=$'\n'
done
# return delimiter to previous value
IFS=$IFS_BAK
IFS_BAK=
while read
vòng lặp trong bash có nghĩa là vòng lặp while nằm trong một khung con, vì vậy các biến không phải là toàn cục. while read;do ;done <<< "$var"
làm cho cơ thể vòng lặp không phải là một subshell. (Bash gần đây có một tùy chọn để đặt phần thân của một cmd | while
vòng lặp không nằm trong một khung con, như ksh luôn có.)
Trong các phiên bản bash gần đây, sử dụng mapfile
hoặc readarray
để đọc hiệu quả đầu ra lệnh thành mảng
$ readarray test < <(ls -ltrR)
$ echo ${#test[@]}
6305
Disclaimer: ví dụ khủng khiếp, nhưng bạn có thể hoàn toàn đưa ra một lệnh tốt hơn để sử dụng hơn là chính mình
readarray
một hàm và gọi hàm đó một vài lần.
-r
cũng là một ý tưởng tốt; Nó ngăn \` interpretation... (it is in your links, but its probably worth mentioning, just to round out your
IFS = `(điều cần thiết để tránh mất khoảng trắng)
Bạn có thể sử dụng <<< để chỉ cần đọc từ biến chứa dữ liệu được phân tách bằng dòng mới:
while read -r line
do
echo "A line of input: $line"
done <<<"$lines"
while IFS= read
.... Nếu bạn muốn ngăn \ phiên dịch, thì hãy sử dụngread -r