it-swarm-id.com

Bagaimana saya bisa membaca baris demi baris dari variabel dalam bash?

Saya memiliki variabel yang berisi keluaran multiline dari suatu perintah. Apa cara paling efisien untuk membaca output baris demi baris dari variabel?

Sebagai contoh:

jobs="$(jobs)"
if [ "$jobs" ]; then
    # read lines from $jobs
fi
53
Eugene Yarmash

Anda dapat menggunakan loop sementara dengan substitusi proses:

while read -r line
do
    echo "$line"
done < <(jobs)

Cara optimal untuk membaca variabel multiline adalah dengan menetapkan variabel kosong IFS dan printf variabel dengan trailing newline:

# 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")

Catatan: Sesuai shellcheck sc2031 , penggunaan subtitusi proses lebih disukai daripada pipa untuk menghindari [secara halus] membuat subkulit.

Juga, harap menyadari bahwa dengan memberi nama variabel jobs ini dapat menyebabkan kebingungan karena itu juga nama perintah Shell yang umum.

70
dogbane

Untuk memproses output baris perintah demi baris ( penjelasan ):

jobs |
while IFS= read -r line; do
  process "$line"
done

Jika Anda sudah memiliki data dalam variabel:

printf %s "$foo" | …

printf %s "$foo" Hampir identik dengan echo "$foo", Tetapi mencetak $foo Secara harfiah, sedangkan echo "$foo" Dapat menafsirkan $foo Sebagai opsi untuk perintah gema jika dimulai dengan -, dan mungkin memperluas urutan garis miring terbalik di $foo di beberapa shell.

Perhatikan bahwa di beberapa shell (abu, bash, pdksh, tetapi bukan ksh atau zsh), sisi kanan pipa berjalan dalam proses terpisah, sehingga variabel apa pun yang Anda atur dalam loop hilang. Misalnya, skrip penghitungan baris berikut mencetak 0 pada shell ini:

n=0
printf %s "$foo" |
while IFS= read -r line; do
  n=$(($n + 1))
done
echo $n

Solusinya adalah dengan meletakkan sisa skrip (atau setidaknya bagian yang membutuhkan nilai $n Dari loop) dalam daftar perintah:

n=0
printf %s "$foo" | {
  while IFS= read -r line; do
    n=$(($n + 1))
  done
  echo $n
}

Jika bertindak pada baris yang tidak kosong cukup baik dan input tidak besar, Anda dapat menggunakan pemisahan kata:

IFS='
'
set -f
for line in $(jobs); do
  # process line
done
set +f
unset IFS

Penjelasan: pengaturan IFS ke satu baris baru membuat pemisahan kata hanya terjadi pada baris baru (sebagai lawan dari karakter spasi putih di bawah pengaturan default). set -f Mematikan globbing (mis. Ekspansi wildcard), yang jika tidak akan terjadi pada hasil substitusi perintah $(jobs) atau variabel pengganti $foo. The for loop bekerja pada semua bagian dari $(jobs), yang semuanya merupakan baris yang tidak kosong dalam output perintah. Terakhir, kembalikan pengaturan globbing dan IFS.

Masalah: jika Anda menggunakan while, itu akan berjalan dalam subkulit dan semua variabel akan hilang. Solusi: gunakan untuk loop

# 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=
16
Dima
jobs="$(jobs)"
while IFS= read
do
    echo $REPLY
done <<< "$jobs"

Referensi:

11
l0b0

Dalam versi bash terbaru, gunakan mapfile atau readarray untuk secara efisien membaca output perintah ke dalam array

$ readarray test < <(ls -ltrR)
$ echo ${#test[@]}
6305

Penafian: contoh yang mengerikan, tetapi Anda dapat dengan cepat menghasilkan perintah yang lebih baik untuk digunakan daripada diri Anda sendiri

10
sehe

Anda dapat menggunakan <<< untuk membaca dari variabel yang berisi data yang dipisahkan dengan baris baru:

while read -r line
do 
  echo "A line of input: $line"
done <<<"$lines"
0