Ulangi melalui variabel shell yang dipisahkan koma

109

Misalkan saya memiliki variabel shell Unix seperti di bawah ini

variable=abc,def,ghij

Saya ingin mengekstrak semua nilai ( abc, defdan ghij) menggunakan for loop dan meneruskan setiap nilai ke dalam sebuah prosedur.

Skrip harus memungkinkan ekstraksi sejumlah nilai yang dipisahkan koma dari $variable.

Ramanathan K
sumber
Apa sebenarnya yang ingin Anda lakukan dengan mengulang?
SMA
1
Saya ingin meneruskan setiap bidang (katakanlah abc) sebagai argumen untuk prosedur.
Ramanathan K

Jawaban:

125

Anda dapat menggunakan skrip berikut untuk melintasi variabel Anda secara dinamis, tidak peduli berapa banyak bidang yang dimilikinya selama hanya dipisahkan dengan koma.

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

Alih-alih echo "$i"panggilan di atas, antara loop for dodan donedi dalam, Anda bisa menjalankan prosedur Anda proc "$i".


Pembaruan : Cuplikan di atas berfungsi jika nilai variabel tidak mengandung spasi. Jika Anda memiliki persyaratan seperti itu, gunakan salah satu solusi yang dapat berubah IFS, lalu parsing variabel Anda.


Semoga ini membantu.

anakron
sumber
8
Ini tidak berfungsi jika $variableberisi spasi kosong, misalnya variable=a b,c,d=> mencetak 4 baris (a | b | c | d) daripada 3 (ab | c | d)
Dan
Serta mendapatkan kutipan baru seperti ** "value **. bisakah Anda memperbarui jika ada regex untuk menghapusnya ..
gks
136

Tidak mengotak-atik IFS
Tidak memanggil perintah eksternal

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

Menggunakan manipulasi string bash http://www.tldp.org/LDP/abs/html/string-manipulation.html

kutu buku
sumber
3
Hal yang menyenangkan tentang ini adalah Anda dapat menumpuk beberapa loop menggunakan pembatas yang berbeda. Saran yang bagus!
Brad Parks
5
Tip bagus untuk menghindari kekacauan IFS!
Laimoncijus
13
Peringatan kecil - jika salah satu nilai dalam $iberisi spasi, nilai tersebut akan dipisahkan (dan mungkin itulah alasannya diteruskan sebagai dipisahkan koma daripada dipisahkan spasi)
Toby Speight
4
Jika fungsi sebelumnya mengubah pengaturan IFS, maka ini tidak berhasil ... Ubah ke ini:for i in ${variable//,/$IFS} do; echo "$i"; done
Joep
2
Saya mendapatkan pesan kesalahan "Substitusi buruk" saat mencoba pendekatan ini.
Lost Crotchet
55

Jika Anda menyetel pemisah bidang yang berbeda, Anda dapat langsung menggunakan forloop:

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

Anda juga dapat menyimpan nilai dalam sebuah array dan kemudian mengulanginya seperti yang ditunjukkan dalam Bagaimana cara membagi string pada pembatas di Bash? :

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

Uji

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

Anda dapat menemukan pendekatan yang lebih luas dalam solusi ini untuk Cara mengulang melalui daftar yang dipisahkan koma dan menjalankan perintah untuk setiap entri .

Contoh pendekatan kedua:

$ 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 --
fedorqui 'JADI berhenti merugikan'
sumber
Lihat juga stackoverflow.com/questions/918886/… :-) Selamat mencoba semua.
shellter
Ya! Saya juga memikirkannya, hanya itu kemudian diperlukan langkah-langkah: a whileuntuk membaca ke dalam array dan satu lagi untuk mengulang melalui hasil.
fedorqui 'SO berhenti merusak'
3
Membuat saya senang ketika seseorang benar-benar tahu cara menggunakan cangkang, daripada menyatukan sekelompok binutil.
PeterT
apakah Anda harus IFSkembali ke sebelumnya?
pstanton
1
@edor. Itulah satu-satunya variabel yang ingin saya putar ulang dan itu membuat file yaml saya lebih mudah dibaca (daripada satu variabel lingkungan PANJANG, ia memiliki banyak baris)
GammaGames
5
#/bin/bash   
TESTSTR="abc,def,ghij"

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

Saya lebih suka menggunakan tr daripada sed, karena sed memiliki masalah dengan karakter khusus seperti \ r \ n dalam beberapa kasus.

solusi lainnya adalah mengatur IFS ke pemisah tertentu

Marek Lisiecki
sumber
1

Berikut adalah solusi berbasis tr alternatif yang tidak menggunakan gema, diekspresikan sebagai satu baris.

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

Rasanya lebih rapi tanpa gaung tapi itu hanya preferensi pribadi saya.

6EQUJ5
sumber
0

Coba yang ini.

#/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
Karthikeyan.RS
sumber
tetapi bagaimana jika saya tidak yakin tentang jumlah kolom di testpid variabel?
Ramanathan K
Juga, saya harus meneruskan setiap variabel yang diajukan di atas sebagai argumen untuk prosedur. Dan saya ingin melakukan ini sampai panjang variabel menjadi nol. (yaitu; semua bidang harus diteruskan satu kali ke prosedur untuk diproses)
Ramanathan K
Saya tidak tahu tentang prosedurnya. Dari tautan ini Anda bisa mendapatkan hitungan bidang. http://stackoverflow.com/questions/8629330/unix-count-of-columns-in-file. Setelah itu buat perulangan menjadi while loop. Anda bisa mendapatkan yang itu.
Karthikeyan.RS
Ini untuk menghitung bidang dari file yang saya kira. Bagaimana jika saya perlu menghitung bidang dalam variabel (misalkan variabel adalah testpid = abc, def, ghij)
Ramanathan K
echo variabel dan pipa output ke awk. Atau lihat jawaban saya yang diperbarui. Semoga bermanfaat.
Karthikeyan.RS
0

Solusi lain tidak menggunakan IFS dan masih mempertahankan ruang:

$ var="a bc,def,ghij"
$ while read line; do echo line="$line"; done < <(echo "$var" | tr ',' '\n')
line=a bc
line=def
line=ghij
pengguna3071170
sumber
Ini berfungsi dengan benar di bash, namun zsh menelan spasi.
lleaff
0

Inilah solusi bash murni saya yang tidak mengubah IFS, dan dapat menggunakan pembatas regex kustom.

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
}
placidwolverine
sumber