Bash: Değişkende satırları yineleme


225

Kişi bir değişkende veya bir komutun çıktısında bash satırları üzerinde doğru şekilde nasıl yinelenir? Yalnızca IFS değişkenini yeni bir satıra ayarlamak bir komutun çıktısı için işe yarar ancak yeni satırlar içeren bir değişken işlenirken kullanılmaz.

Örneğin

#!/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

Bu çıktı verir:

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

Gördüğünüz gibi, değişkenin yankılanması veya catkomut üzerinde yinelenmesi satırların her birini birer birer doğru yazdırır. Ancak, ilk for döngüsü tüm öğeleri tek bir satıra yazdırır. Herhangi bir fikir?


Tüm cevaplar için sadece bir yorum: newline karakterini düzeltmek için $ (echo "$ line" | sed -e 's / ^ [[: space:]] * //') yapmak zorunda kaldım.
servermanfail

Yanıtlar:


290

Bash ile yeni satırları bir dizgeye gömmek istiyorsanız, dizgiyi içine alın $'':

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

Zaten bir değişkende böyle bir dize varsa, satır satır okuyabilirsiniz:

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

30
Bu done <<< "$list"çok önemli bir not
Jason Axelson

21
Nedeni done <<< "$list"çok önemlidir çünkü bunun girdi olarak "$ list" ' read
ü

22
Şunu vurgulamalıyım: ÇIFT-SORMA $listçok önemlidir.
André Chalella

8
echo "$list" | while ...daha net görünebilir... done <<< "$line"
kyb

5
While döngüsü bir subshell içinde çalışacağı için bu yaklaşımın olumsuz yanları vardır: loop'a atanan herhangi bir değişken kalmayacaktır.
glenn jackman

66

while+ Kullanabilirsiniz read:

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

Btw. -eseçeneği echoolmayan bir standarttır. printfTaşınabilirlik istiyorsanız, yerine kullanın .


12
Bu sözdizimini kullanırsanız, döngü içinde atanan değişkenlerin döngüden sonra yapışmayacağını unutmayın. Garip, <<<Glenn Jackman önerdiği versiyonu geliyor değişken atama ile çalışır.
Sparhawk

7
@Sparhawk Evet, çünkü boruyu whileparçayı çalıştıran bir alt kabuk başlatır . <<<Versiyon (en azından, yeni bash sürümlerinde) yok.
maxelost

26
#!/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

2
Bu, satırları değil tüm öğeleri çıkarır.
Tomasz Posłuszny,

4
@ TomaszPosłuszny Hayır, bu makinemde 3 (sayı 3) satır üretiyor. IFS ayarını not edin. IFS=$'\n'Aynı etki için de kullanabilirsiniz .
jmiserez

11

İşte for döngüsünü yapmanın komik bir yolu:

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

Biraz daha mantıklı / okunabilir olur:

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

Ama hepsi bu kadar karmaşık, orada sadece bir yere ihtiyacınız var:

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

You $linedeğişken yeni satır içermez. \Tarafından takip edilen örnekleri içerir n. Bunu açıkça görebilirsiniz:

$ 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

Bu oyuncu değişikliği döngüler için çalışabileceği kadar boşluklarla değiştiriyor:

$ 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

Demo:

$ 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

İlginç, burada neler olduğunu açıklayabilir misiniz? Görünüşe göre yeni bir satırla değiştiriliyor \ n… Orijinal dize ile yenisi arasındaki fark nedir?
Alex Spurling

@Alex: cevabımı güncelledi - çok daha basit bir versiyonuyla :-)
Mat

Bunu denedim ve girdilerinizde boşluk varsa işe yaramadı. Yukarıdaki örnekte "one \ ntwo \ nthree" var ve bu işe yarıyor, ancak alan için de yeni bir satır eklediği için "bir tane \ nentry two \ nentry three" girersek başarısız oluyor.
John Rocha,

2
Sadece bir not tanımlamak yerine crkullanabileceğinizi not edin $'\n'.
devios1

1

Ayrıca önce değişkeni bir diziye dönüştürebilir, sonra bunun üzerinde yineleyebilirsiniz.

lines="abc
def
ghi"

declare -a theArray

while read -r line
do
    theArray+=($line)            
done <<< "$lines"

for line in "${theArray[@]}"
do
    echo "$line"
    #Do something complex here that would break your read loop
done

Bu sadece yararlıdır ve bununla uğraşmak istemezseniz IFSve readkomutla da sorun yaşarsanız, olabileceği gibi, döngü içinde başka bir komut dosyasını çağırırsanız, bu komut dosyası bana geri dönmeden önce geri dönmeden önce okuma arabelleğinizi boşaltabilir .

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.