Virgülle ayrılmış kabuk değişkeni üzerinden döngü yapın


109

Aşağıdaki gibi bir Unix kabuk değişkenim olduğunu varsayalım

variable=abc,def,ghij

Bütün değerler (çıkarmak isteyen abc, defve ghij) döngüsü kullanarak ve bir prosedür her bir değeri geçirir.

Komut dosyası, rastgele virgülle ayrılmış değerlerin çıkarılmasına izin vermelidir. $variable .


Tekrarlayarak tam olarak ne yapmak istiyorsun?
SMA

1
Alanların her birini (diyelim abc) bir prosedüre argüman olarak geçirmek istiyorum.
Ramanathan K

Yanıtlar:


128

Sadece virgülle ayrılmış olduğu sürece, kaç tane alana sahip olursa olsun, değişkeninizde dinamik olarak gezinmek için aşağıdaki komut dosyasını kullanabilirsiniz.

variable=abc,def,ghij
for i in $(echo $variable | sed "s/,/ /g")
do
    # call your procedure/other scripts here below
    echo "$i"
done

echo "$i"Yukarıdaki çağrı yerine , for döngüsü arasında dove doneiçinde, prosedürünüzü çağırabilirsiniz proc "$i".


Güncelleme : Yukarıdaki pasaj, değişkenin değeri boşluk içermediğinde çalışır. Böyle bir gereksiniminiz varsa, lütfen değişebilen çözümlerden birini kullanın IFSve ardından değişkeninizi ayrıştırın.


Bu yardımcı olur umarım.


8
$variableBeyaz boşluk içeriyorsa bu işe yaramaz , örneğin variable=a b,c,d=> 3 (ab | c | d) yerine 4 satır (a | b | c | d) yazdırır
Dan

Ayrıca ** "value ** gibi yeni teklifler de ekleniyor . bunu kaldırmak için herhangi bir normal ifade varsa lütfen günceller misiniz?
gks

139

IFS ile uğraşmamak
Harici komutu çağırmamak

variable=abc,def,ghij
for i in ${variable//,/ }
do
    # call your procedure/other scripts here below
    echo "$i"
done

Bash dizesi manipülasyonunu kullanma http://www.tldp.org/LDP/abs/html/string-manipulation.html


4
Bunun güzel yanı, farklı sınırlayıcıları kullanarak birden çok döngüyü iç içe geçirebilmenizdir. İyi bir öneri!
Brad Parks

6
Karışıklıktan kaçınmak için güzel bir ipucu IFS!
Laimoncijus

14
Küçük uyarı - eğer içindeki değerlerden herhangi biri $iboşluk içeriyorsa, bölünecekler (ve boşlukla ayrılmak yerine virgülle ayrılmış olarak geçmesinin nedeni bu olabilir)
Toby Speight

4
Önceki bir işlev IFS ayarını değiştirdiyse, bu işe yaramaz ... Bunu şu şekilde değiştirin:for i in ${variable//,/$IFS} do; echo "$i"; done
Joep

2
Bu yaklaşımı denediğimde "Kötü ikame" hata mesajını alıyorum.
Lost Crotchet

56

Farklı bir alan ayırıcı ayarlarsanız, doğrudan bir fordöngü kullanabilirsiniz :

IFS=","
for v in $variable
do
   # things with "$v" ...
done

Değerleri bir dizide de saklayabilir ve ardından Bash'de bir sınırlayıcıda bir dizeyi nasıl bölerim? :

IFS=, read -ra values <<< "$variable"
for v in "${values[@]}"
do
   # things with "$v"
done

Ölçek

$ variable="abc,def,ghij"
$ IFS=","
$ for v in $variable
> do
> echo "var is $v"
> done
var is abc
var is def
var is ghij

Bu çözümde, virgülle ayrılmış bir listeyle nasıl yineleme yapılacağına ve her giriş için bir komutun nasıl yürütüleceğine ilişkin daha geniş bir yaklaşım bulabilirsiniz .

İkinci yaklaşımla ilgili örnekler:

$ IFS=, read -ra vals <<< "abc,def,ghij"
$ printf "%s\n" "${vals[@]}"
abc
def
ghij
$ for v in "${vals[@]}"; do echo "$v --"; done
abc --
def --
ghij --

Ayrıca stackoverflow.com/questions/918886/… bakın :-) Herkese iyi şanslar.
shellter

Evet! Bunu da düşündüm, sadece şu adımları atması gerektiğini düşündüm: a whilebir diziyi okumak ve diğeri sonuçlarda döngü yapmak için.
fedorqui,

3
Biri bir grup binutil bir araya getirmek yerine kabuğu nasıl kullanacağını bildiğinde beni mutlu ediyor.
PeterT

ayarlamak zorunda IFSeskisi neyse geri?
pstanton

1
@fedorqui Yaptı! Döngü yapmak istediğim tek değişken buydu ve yaml dosyamı okumayı kolaylaştırdı (bir LONG ortam değişkeni yerine bir sürü satıra sahip)
GammaGames

5
#/bin/bash   
TESTSTR="abc,def,ghij"

for i in $(echo $TESTSTR | tr ',' '\n')
do
echo $i
done

Sed yerine tr kullanmayı tercih ediyorum, çünkü sed bazı durumlarda \ r \ n gibi özel karakterlerle sorun yaşıyor.

diğer çözüm, IFS'yi belirli bir ayırıcıya ayarlamaktır


1

İşte yankı kullanmayan, tek satırlık olarak ifade edilen alternatif bir tr tabanlı çözüm.

for v in $(tr ',' '\n' <<< "$var") ; do something_with "$v" ; done

Eko olmadan daha düzenli hissediyor ama bu sadece benim kişisel tercihim.


0

Bunu dene.

#/bin/bash   
testpid="abc,def,ghij" 
count=`echo $testpid | grep -o ',' | wc -l` # this is not a good way
count=`expr $count + 1` 
while [ $count -gt 0 ]  ; do
     echo $testpid | cut -d ',' -f $i
     count=`expr $count - 1 `
done

ama testpid değişkenindeki alanların sayısından emin değilsem ne olur?
Ramanathan K

Ayrıca, yukarıdaki değişkenin dosyalanmış her birini argüman olarak bir prosedüre iletmem gerekiyor. Ve bunu değişkenin uzunluğu sıfır olana kadar yapmak istiyorum. (yani; tüm alanlar işleme prosedürüne bir kez geçirilmelidir)
Ramanathan K

Prosedürü bilmiyorum. Bu linkten alanların sayısını öğrenebilirsiniz. http://stackoverflow.com/questions/8629330/unix-count-of-columns-in-file. Bundan sonra bunu while döngüsüne çevirin. Onu alabilirsin.
Karthikeyan.RS

Bu sanırım bir dosyadaki alanları saymaktır. Ya bir değişkendeki alanları saymam gerekirse (değişkenin testpid = abc, def, ghij olduğunu varsayalım)
Ramanathan K

değişkeni yankılayın ve çıktıyı awk'ye yönlendirin. Ya da güncellenmiş cevabıma bakın. Yararlı olabilir.
Karthikeyan.RS

0

IFS kullanmayan ve hala alanları koruyan başka bir çözüm:

$ var="a bc,def,ghij"
$ while read line; do echo line="$line"; done < <(echo "$var" | tr ',' '\n')
line=a bc
line=def
line=ghij

Bu bash'de doğru çalışır, ancak zsh boşlukları yutar.
lleaff

0

İşte IFS'yi değiştirmeyen ve özel bir normal ifade sınırlayıcı alabilen saf bash çözümüm.

loop_custom_delimited() {
    local list=$1
    local delimiter=$2
    local item
    if [[ $delimiter != ' ' ]]; then
        list=$(echo $list | sed 's/ /'`echo -e "\010"`'/g' | sed -E "s/$delimiter/ /g")
    fi
    for item in $list; do
        item=$(echo $item | sed 's/'`echo -e "\010"`'/ /g')
        echo "$item"
    done
}
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.