Bir değişkendeki bash satır satır satır nasıl okuyabilirim?


48

Bir komutun çok satırlı çıktısını içeren bir değişken var. Çıkış satırını değişkenden satır satır okumanın en etkili yolu nedir?

Örneğin:

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

Yanıtlar:


64

Bir süre döngü işlem değiştirme ile kullanabilirsiniz:

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

Çok satırlı bir değişkeni okumanın en iyi yolu, boş bir IFSdeğişkeni ve printfdeğişkeni izleyen bir satırsonu ayarlamaktır:

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

Not: kabuk kontrolü sc2031'e göre , bir alt kabuk oluşturmaktan [ince bir şekilde] kaçınmak için işlem alt bölümünün kullanılması bir boruya tercih edilir.

Ayrıca, değişkeni isimlendirmenin jobskarışıklığa yol açabileceğini lütfen unutmayın, çünkü bu aynı zamanda ortak bir kabuk komutunun adıdır.


3
Eğer tüm boşluklarınızı korumak istiyorsanız, o zaman kullanın while IFS= read... \ yorumlamayı önlemek istiyorsanız, o zaman kullanınread -r
Peter.O

Ben de değişmiş olarak puan, söz fred.bear giderdikten echoiçin printf %sbetiğinize olmayan uysal girişli bile çalışacak böylece.
Gilles 'SO- kötülük' dur

Çok satırlı bir değişkenden okumak için, printf'den gelen borulara bir sapma tercih edilir (bkz. L0b0'ın cevabı).
ata

1
@ata Bu "tercih edilebilir" kelimesini yeterince sık duymuş olmama rağmen, bir bülbülün /tmp, geçici bir çalışma dosyası oluşturabilmeye dayandığından, her zaman dizinin yazılabilir olmasını gerektirdiği belirtilmelidir . Kısıtlı bir sistemde /tmpsalt okunur olma (ve sizin tarafınızdan değiştirilemeyen) durumunuzu bulursanız, alternatif bir çözüm, örneğin printfboru ile kullanma olasılığından memnun olacaksınız .
sözdizimi

1
İkinci örnekte, çok satırlı değişken izleyen bir yeni satır içermiyorsa, son elemanı kaybedersiniz. Değiştir:printf "%s\n" "$var" | while IFS= read -r line
David H. Bennett,

26

Bir komut satırının çıktısını satır satır işlemek için ( açıklama ):

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

Verileri zaten bir değişkende varsa:

printf %s "$foo" | 

printf %s "$foo"hemen hemen aynıdır echo "$foo", fakat baskılar $fooise tam anlamıyla echo "$foo"yorumlayabilir $foobir başlıyorsa yankı komutuna bir seçenek olarak -ve de ters eğik çizgi dizileri genişleyebilir $foobazı kabuklarda.

Bazı kabuklarda (kül, bash, pdksh, ancak ksh veya zsh değil), bir boru hattının sağ tarafının ayrı bir işlemde çalıştığını ve döngüde ayarladığınız herhangi bir değişkenin kaybolduğunu unutmayın. Örneğin, aşağıdaki satır sayma komut dosyası bu kabuklarda 0 basar:

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

Çözüm, komut dosyasının geri kalanını (veya en azından $ndöngünün değerini gerektiren kısmı ) komut listesine koymaktır :

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

Boş olmayan satırlara etki etmek yeterince iyi ise ve girdi çok büyük değilse, bölme kelimesini kullanabilirsiniz:

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

Açıklama: IFStek bir yeni satıra ayarlamak , kelime bölmenin yalnızca yeni satırlarda yapılmasını sağlar (varsayılan ayardaki herhangi bir boşluk karakterinin aksine). set -fKomut ikame edilmesi $(jobs)veya değişken ikame edici sonucu ortaya çıkacak olan globbing'i (yani joker karakter genişlemesi) kapatır $foo. forDöngüsü, tüm ilgili parçalarını hareket $(jobs)komutu çıkışı boş olmayan tüm satırları,. Son olarak, genelleme ve IFSayarları geri yükleyin .


IFS'yi ayarlamak ve IFS'yi ayarlamakta zorlandım. Bence yapılacak doğru şey, IFS'nin eski değerini saklamak ve IFS'i tekrar eski değerine ayarlamak. Ben bir bash uzmanı değilim, ama deneyimlerime göre, bu sizi orijinal davranışına geri döndürür.
Bjorn Roche

1
@BjornRoche: bir fonksiyonun içinde kullanın local IFS=something. Global kapsam değerini etkilemez. IIRC, unset IFSsizi varsayılana döndürmez (önceden varsayılan değilse kesinlikle çalışmaz).
Peter Cordes

14

Sorun: Eğer döngü kullanırken kullanırsanız alt kabukta çalışacak ve tüm değişkenler kaybolacak. Çözüm: döngü için kullanın

# 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=

ÇOK TEŞEKKÜR EDERİM!! Yukarıdaki çözümlerin hepsi benim için başarısız oldu.
hax0r_n_code

while readbash içindeki bir döngüye borulamak , while döngüsünün bir alt kabukta olduğu anlamına gelir, bu nedenle değişkenler global değildir. while read;do ;done <<< "$var"döngü gövdesini alt kabuk değil yapar. (Son bash, cmd | whileksh'nin her zaman olduğu gibi, bir alt gövdesinin alt gövdesinde olmayan bir döngünün gövdesini koyma seçeneğine sahiptir.)
Peter Cordes


10

Son bash sürümlerinde, komut çıktısını dizilere verimli bir şekilde okumak için kullanın mapfileveya readarraykullanın

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

Feragatname: korkunç bir örnek, ancak kendinizden daha iyi bir komutla gelebilirsiniz.


Güzel bir yol, ancak sistemimde geçici dosyalar bulunan litre / var / tmp. Yine de +1
Eugene Yarmash

@ eugene: Bu komik. Hangi sistem (dağıtım / işletim sistemi) açık?
sehe

Bu FreeBSD 8. Nasıl yeniden üretilir: readarraybir fonksiyon koyun ve fonksiyonu birkaç kez çağırın.
Eugene Yarmash

Güzel olanı, @ sehe. +1
Teresa e Junior

7
jobs="$(jobs)"
while IFS= read
do
    echo $REPLY
done <<< "$jobs"

Referanslar:


3
-rbu da iyi bir fikirdir; \` interpretation... (it is in your links, but its probably worth mentioning, just to round out your IFS = `yi engeller (boşluk kaybını önlemek için esastır)
Peter.O

-1

Yeni satırla ayrılmış verileri içeren değişkenden okumak için <<< öğesini kullanabilirsiniz:

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

1
Unix ve Linux'a Hoşgeldiniz! Bu aslında dört yıl önceki bir cevabı yineliyor. Lütfen katkıda bulunacak yeni bir şeyiniz yoksa cevap yazmayın.
G-Man
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.