Câu hỏi Bash: Lặp lại các dòng trong một biến


Làm thế nào để một iterate đúng trên dòng trong bash hoặc trong một biến, hoặc từ đầu ra của một lệnh? Chỉ cần đặt biến IFS thành một dòng mới hoạt động cho đầu ra của một lệnh nhưng không phải khi xử lý một biến chứa các dòng mới.

Ví dụ

#!/bin/bash

list="One\ntwo\nthree\nfour"

#Print the list with echo
echo -e "echo: \n$list"

#Set the field separator to new line
IFS=$'\n'

#Try to iterate over each line
echo "For loop:"
for item in $list
do
        echo "Item: $item"
done

#Output the variable to a file
echo -e $list > list.txt

#Try to iterate over each line from the cat command
echo "For loop over command output:"
for item in `cat list.txt`
do
        echo "Item: $item"
done

Điều này cho đầu ra:

echo: 
One
two
three
four
For loop:
Item: One\ntwo\nthree\nfour
For loop over command output:
Item: One
Item: two
Item: three
Item: four

Như bạn có thể thấy, lặp lại biến hoặc lặp qua cat lệnh in từng dòng một một cách chính xác. Tuy nhiên, đầu tiên cho vòng lặp in tất cả các mục trên một dòng. Bất kỳ ý tưởng?


168
2018-05-16 12:14


gốc




Các câu trả lời:


Với bash, nếu bạn muốn nhúng dòng mới vào một chuỗi, hãy kèm theo chuỗi $'':

$ list="One\ntwo\nthree\nfour"
$ echo "$list"
One\ntwo\nthree\nfour
$ list=$'One\ntwo\nthree\nfour'
$ echo "$list"
One
two
three
four

Và nếu bạn có một chuỗi như vậy đã có trong một biến, bạn có thể đọc nó theo từng dòng với:

while read -r line; do
    echo "... $line ..."
done <<< "$list"

209
2018-05-16 13:47



Chỉ cần lưu ý rằng done <<< "$list" là quan trọng - Jason Axelson
Nguyên nhân done <<< "$list" điều quan trọng là vì điều đó sẽ chuyển "$ list" làm đầu vào read - wisbucky
Tôi cần phải nhấn mạnh điều này: DOUBLE-QUOTING $list là rất quan trọng. - André Chalella
Làm thế nào tôi sẽ làm điều đó trong tro? Bởi vì tôi nhận được syntax error: unexpected redirection. - atripes
Có những nhược điểm cho cách tiếp cận đó vì vòng lặp while sẽ chạy trong một subshell: bất kỳ biến nào được gán trong vòng lặp sẽ không tồn tại. - glenn jackman


Bạn có thể dùng while + read:

some_command | while read line ; do
   echo === $line ===
done

Btw. các -e tùy chọn để echo không chuẩn. Sử dụng printf thay vào đó, nếu bạn muốn tính di động.


47
2018-05-16 12:21



Lưu ý rằng nếu bạn sử dụng cú pháp này, các biến được gán bên trong vòng lặp sẽ không dính sau vòng lặp. Kỳ lạ thay, <<< phiên bản được đề xuất bởi Jackman Jackman làm làm việc với phép gán biến. - Sparhawk
@Sparhawk Vâng, đó là bởi vì đường ống bắt đầu một subshell thực hiện while phần. Các <<< phiên bản không (trong phiên bản bash mới, ít nhất). - maxelost


#!/bin/sh

items="
one two three four
hello world
this should work just fine
"

IFS='
'
count=0
for item in $items
do
  count=$((count+1))
  echo $count $item
done

18
2018-03-17 21:45



Điều này xuất ra tất cả các mục, chứ không phải các dòng. - Tomasz Posłuszny
@ TomaszPosłuszny Không, kết quả đầu ra 3 (đếm là 3) dòng trên máy tính của tôi. Lưu ý thiết lập của IFS. Bạn cũng có thể sử dụng IFS=$'\n' cho cùng một hiệu ứng. - jmiserez


Đây là một cách thú vị để thực hiện vòng lặp for:

for item in ${list//\\n/
}
do
   echo "Item: $item"
done

Một chút hợp lý hơn / có thể đọc được sẽ là:

cr='
'
for item in ${list//\\n/$cr}
do
   echo "Item: $item"
done

Nhưng đó là tất cả quá phức tạp, bạn chỉ cần một không gian trong đó:

for item in ${list//\\n/ }
do
   echo "Item: $item"
done

Bạn $line biến không chứa dòng mới. Nó chứa các phiên bản của \ theo dõi bởi n. Bạn có thể thấy điều đó rõ ràng với:

$ cat t.sh
#! /bin/bash
list="One\ntwo\nthree\nfour"
echo $list | hexdump -C

$ ./t.sh
00000000  4f 6e 65 5c 6e 74 77 6f  5c 6e 74 68 72 65 65 5c  |One\ntwo\nthree\|
00000010  6e 66 6f 75 72 0a                                 |nfour.|
00000016

Sự thay thế là thay thế những người có không gian, đủ để nó hoạt động trong các vòng lặp:

$ cat t.sh
#! /bin/bash
list="One\ntwo\nthree\nfour"
echo ${list//\\n/ } | hexdump -C

$ ./t.sh 
00000000  4f 6e 65 20 74 77 6f 20  74 68 72 65 65 20 66 6f  |One two three fo|
00000010  75 72 0a                                          |ur.|
00000013

Bản giới thiệu:

$ cat t.sh
#! /bin/bash
list="One\ntwo\nthree\nfour"
echo ${list//\\n/ } | hexdump -C
for item in ${list//\\n/ } ; do
    echo $item
done

$ ./t.sh 
00000000  4f 6e 65 20 74 77 6f 20  74 68 72 65 65 20 66 6f  |One two three fo|
00000010  75 72 0a                                          |ur.|
00000013
One
two
three
four

7
2018-05-16 12:45



Thú vị, bạn có thể giải thích những gì đang xảy ra ở đây? Có vẻ như bạn đang thay thế \ n bằng một dòng mới ... Sự khác nhau giữa chuỗi gốc và chuỗi mới là gì? - Alex Spurling
@ Alex: cập nhật câu trả lời của tôi - với một phiên bản đơn giản quá :-) - Mat
Tôi đã thử điều này và nó dường như không hoạt động nếu bạn có không gian trong đầu vào của bạn. Trong ví dụ trên, chúng ta có "one \ ntwo \ nnthree" và nó hoạt động nhưng nó không thành công nếu chúng ta có "entry one \ nentry two \ nentry three" vì nó cũng thêm một dòng mới cho không gian. - John Rocha
Chỉ cần lưu ý rằng thay vì xác định cr bạn có thể dùng $'\n'. - devios1