Memutar melalui serangkaian string di Bash?

1503

Saya ingin menulis skrip yang loop melalui 15 string (array mungkin?) Apakah itu mungkin?

Sesuatu seperti:

for databaseName in listOfNames
then
  # Do something
end
Mo.
sumber

Jawaban:

2392

Anda bisa menggunakannya seperti ini:

## declare an array variable
declare -a arr=("element1" "element2" "element3")

## now loop through the above array
for i in "${arr[@]}"
do
   echo "$i"
   # or do whatever with individual element of the array
done

# You can access them using echo "${arr[0]}", "${arr[1]}" also

Juga berfungsi untuk deklarasi array multi-line

declare -a arr=("element1" 
                "element2" "element3"
                "element4"
                )
anubhava
sumber
777

Itu mungkin, tentu saja.

for databaseName in a b c d e f; do
  # do something like: echo $databaseName
done 

Lihat Bash Loops untuk, sementara dan sampai untuk detailnya.

4ndrew
sumber
18
Apa masalah dengan pendekatan ini? Dalam kasus-kasus sederhana ini tampaknya berfungsi dan, kemudian, lebih intuitif daripada jawaban @ anubhava.
Dr. Jan-Philip Gehrcke
17
Ini bekerja sangat baik dengan substitusi perintah, misalnya for year in $(seq 2000 2013).
Brad Koch
23
Masalahnya adalah dia bertanya tentang iterasi melalui array.
mgalg
20
Pendekatan 'mendeklarasikan' berfungsi paling baik jika Anda harus mengulangi array yang sama di lebih dari satu tempat. Pendekatan ini lebih bersih tetapi kurang fleksibel.
StampyCode
15
Mengapa ini tidak # 1? Ini lebih bersih, dan Anda dapat dengan mudah menggunakan kembali array hanya dengan mengatur string, yaitu DATABASES="a b c d e f",.
Nerdmaster
201

Tak satu pun dari jawaban itu termasuk penghitung ...

#!/bin/bash
## declare an array variable
declare -a array=("one" "two" "three")

# get length of an array
arraylength=${#array[@]}

# use for loop to read all values and indexes
for (( i=1; i<${arraylength}+1; i++ ));
do
  echo $i " / " ${arraylength} " : " ${array[$i-1]}
done

Keluaran:

1  /  3  :  one
2  /  3  :  two
3  /  3  :  three
caktux
sumber
7
Ini harus menjadi jawaban yang diterima, adalah satu-satunya yang berfungsi ketika elemen array mengandung spasi.
jesjimher
1
Itu hanya demi hasil contoh dengan penghitung. Ini juga cukup sepele untuk mengubahnya, dan berfungsi sama saja.
caktux
7
Gema pada akhirnya adalah buggy. Anda tidak perlu mengutip konstanta, Anda perlu mengutip ekspansi, atau dapat dengan aman hanya mengutip keduanya sebagai berikut: echo "$i / ${arraylength} : ${array[$i-1]}"- jika tidak, jika Anda $imengandung bola dunia itu akan diperluas, jika mengandung tab itu akan berubah menjadi ruang, dll.
Charles Duffy
10
@ Bzeaman, tentu - tetapi jika Anda ceroboh tentang hal-hal seperti itu maka diperlukan analisis kontekstual (seperti yang baru saja Anda lakukan) untuk membuktikan sesuatu yang benar, dan analisis ulang jika konteks itu berubah atau kode digunakan kembali di tempat yang berbeda atau untuk apa pun alasan lain aliran kontrol yang tidak terduga adalah mungkin. Tulis dengan cara yang kuat dan benar terlepas dari konteksnya.
Charles Duffy
5
Ini tidak akan berfungsi untuk array yang jarang, yaitu jika array tersebut kehilangan elemen. Misalnya, jika Anda memiliki elemen array A [1] = 'xx', A [4] = 'yy' dan A [9] = 'zz', panjangnya akan 3 dan loop tidak akan memproses semua elemen .
Tuan Ed
145

Iya

for Item in Item1 Item2 Item3 Item4 ;
  do
    echo $Item
  done

Keluaran:

Item1
Item2
Item3
Item4

Untuk melestarikan ruang; entri daftar kutipan tunggal atau ganda dan ekspansi daftar kutipan ganda.

for Item in 'Item 1' 'Item 2' 'Item 3' 'Item 4' ;
  do
    echo "$Item"
  done

Keluaran:

Item 1
Item 2
Item 3
Item 4

Untuk membuat daftar lebih dari beberapa baris

for Item in Item1 \
            Item2 \
            Item3 \
            Item4
  do
    echo $Item
  done

Keluaran:

Item1
Item2
Item3
Item4


Variabel daftar sederhana

List=( Item1 Item2 Item3 )

atau

List=(
      Item1 
      Item2 
      Item3
     )

Tampilkan variabel daftar:

echo ${List[*]}

Keluaran:

Item1 Item2 Item3

Loop melalui daftar:

for Item in ${List[*]} 
  do
    echo $Item 
  done

Keluaran:

Item1
Item2
Item3

Buat fungsi untuk menelusuri daftar:

Loop(){
  for item in ${*} ; 
    do 
      echo ${item} 
    done
}
Loop ${List[*]}

Menggunakan kata kunci mendeklarasikan (perintah) untuk membuat daftar, yang secara teknis disebut array:

declare -a List=(
                 "element 1" 
                 "element 2" 
                 "element 3"
                )
for entry in "${List[@]}"
   do
     echo "$entry"
   done

Keluaran:

element 1
element 2
element 3

Membuat array asosiatif. Sebuah kamus:

declare -A continent

continent[Vietnam]=Asia
continent[France]=Europe
continent[Argentina]=America

for item in "${!continent[@]}"; 
  do
    printf "$item is in ${continent[$item]} \n"
  done

Keluaran:

 Argentina is in America
 Vietnam is in Asia
 France is in Europe

Variabel CVS atau file dalam daftar .
Mengubah pemisah bidang internal dari ruang, ke apa pun yang Anda inginkan.
Dalam contoh di bawah ini diubah menjadi koma

List="Item 1,Item 2,Item 3"
Backup_of_internal_field_separator=$IFS
IFS=,
for item in $List; 
  do
    echo $item
  done
IFS=$Backup_of_internal_field_separator

Keluaran:

Item 1
Item 2
Item 3

Jika perlu memberi nomor:

` 

ini disebut centang kembali. Masukkan perintah di dalam kutu kembali.

`commend` 

Itu di sebelah nomor satu di keyboard Anda dan atau di atas tombol tab. Pada keyboard bahasa Inggris standar Amerika.

List=()
Start_count=0
Step_count=0.1
Stop_count=1
for Item in `seq $Start_count $Step_count $Stop_count`
    do 
       List+=(Item_$Item)
    done
for Item in ${List[*]}
    do 
        echo $Item
    done

Output adalah:

Item_0.0
Item_0.1
Item_0.2
Item_0.3
Item_0.4
Item_0.5
Item_0.6
Item_0.7
Item_0.8
Item_0.9
Item_1.0

Menjadi lebih akrab dengan perilaku bashes:

Buat daftar di file

cat <<EOF> List_entries.txt
Item1
Item 2 
'Item 3'
"Item 4"
Item 7 : *
"Item 6 : * "
"Item 6 : *"
Item 8 : $PWD
'Item 8 : $PWD'
"Item 9 : $PWD"
EOF

Baca file daftar ke daftar dan tampilan

List=$(cat List_entries.txt)
echo $List
echo '$List'
echo "$List"
echo ${List[*]}
echo '${List[*]}'
echo "${List[*]}"
echo ${List[@]}
echo '${List[@]}'
echo "${List[@]}"

Manual referensi baris perintah BASH: Arti khusus karakter atau kata-kata tertentu untuk shell.

FireInTheSky
sumber
7
INI SALAH. Harus "${List[@]}"benar, dengan kutipan . ${List[@]}salah. ${List[*]}salah. Coba List=( "* first item *" "* second item *" )- Anda akan mendapatkan perilaku yang benar untuk for item in "${List[@]}"; do echo "$item"; done, tetapi tidak dari varian lain.
Charles Duffy
Ya karakter khusus ditafsirkan, yang mungkin atau mungkin tidak diinginkan. Saya memperbarui jawaban untuk dimasukkan.
FireInTheSky
2
Saya masih sangat menyarankan menunjukkan pendekatan yang lebih benar / kuat dulu . Orang sering mengambil jawaban pertama yang kelihatannya akan bekerja; jika jawaban itu memiliki peringatan tersembunyi, mereka hanya dapat mengekspos diri mereka nanti. (Ini bukan hanya wildcard yang rusak oleh kurangnya mengutip; List=( "first item" "second item" )akan dipecah menjadi first, item, second, itemjuga).
Charles Duffy
Anda mungkin juga mempertimbangkan untuk menghindari penggunaan contoh yang dapat menyebabkan orang mengurai lsoutput, bertentangan dengan praktik terbaik .
Charles Duffy
1
Anda menyangkal klaim yang tidak pernah saya buat. Saya cukup akrab dengan semantik kutipan bash - lihat stackoverflow.com/tags/bash/topusers
Charles Duffy
108

Dengan semangat yang sama dengan jawaban 4ndrew:

listOfNames="RA
RB
R C
RD"

# To allow for other whitespace in the string:
# 1. add double quotes around the list variable, or
# 2. see the IFS note (under 'Side Notes')

for databaseName in "$listOfNames"   #  <-- Note: Added "" quotes.
do
  echo "$databaseName"  # (i.e. do action / processing of $databaseName here...)
done

# Outputs
# RA
# RB
# R C
# RD

B. Tidak ada spasi putih dalam nama:

listOfNames="RA
RB
R C
RD"

for databaseName in $listOfNames  # Note: No quotes
do
  echo "$databaseName"  # (i.e. do action / processing of $databaseName here...)
done

# Outputs
# RA
# RB
# R
# C
# RD

Catatan

  1. Dalam contoh kedua, menggunakan listOfNames="RA RB R C RD"memiliki output yang sama.

Cara lain untuk memasukkan data meliputi:

Baca dari stdin

# line delimited (each databaseName is stored on a line)
while read databaseName
do
  echo "$databaseName"  # i.e. do action / processing of $databaseName here...
done # <<< or_another_input_method_here
  1. bash IFS "pemisah lapangan untuk line" [ 1 ] pembatas dapat ditentukan dalam script untuk memungkinkan spasi lainnya (yaituIFS='\n' , atau untuk MacOS IFS='\r')
  2. Saya suka jawaban yang diterima juga :) - Saya sudah memasukkan cuplikan ini sebagai cara bermanfaat lainnya yang juga menjawab pertanyaan.
  3. Termasuk #!/bin/bash di bagian atas file skrip menunjukkan lingkungan eksekusi.
  4. Butuh berbulan-bulan untuk mencari tahu bagaimana kode ini hanya :)

Sumber lain ( sambil membaca loop )

pengguna2533809
sumber
Ini menciptakan kesan bahwa eol digunakan sebagai pemisah string dan, oleh karena itu, spasi putih diizinkan di dalam string. Namun, string dengan spasi putih lebih jauh dipisahkan menjadi substring, yang sangat sangat buruk. Saya pikir jawaban ini stackoverflow.com/a/23561892/1083704 lebih baik.
Val
1
@ Val, saya menambahkan komentar kode dengan referensi ke IFS. (Untuk semua orang, IFSmari kita tentukan pembatas tertentu, yang memungkinkan spasi putih lainnya untuk dimasukkan dalam string tanpa dipisahkan menjadi substring).
user2533809
7
Ini tidak berhasil untuk saya. $databaseNamehanya berisi seluruh daftar, sehingga hanya satu iterasi.
AlikElzin-kilaka
@ AlikElzin-kilaka Jawaban saya di bawah menyelesaikan masalah ini sehingga loop dijalankan untuk setiap baris string.
Jamie
42

Anda dapat menggunakan sintaks dari ${arrayName[@]}

#!/bin/bash
# declare an array called files, that contains 3 values
files=( "/etc/passwd" "/etc/group" "/etc/hosts" )
for i in "${files[@]}"
do
    echo "$i"
done
Fizer Khan
sumber
31

Terkejut bahwa belum ada yang memposting ini - jika Anda memerlukan indeks elemen saat Anda mengulang-ulang array, Anda dapat melakukan ini:

arr=(foo bar baz)

for i in ${!arr[@]}
do
    echo $i "${arr[i]}"
done

Keluaran:

0 foo
1 bar
2 baz

Saya menemukan ini jauh lebih elegan daripada gaya for-loop "tradisional" (for (( i=0; i<${#arr[@]}; i++ )) ).

( ${!arr[@]}dan $itidak perlu dikutip karena itu hanya angka; beberapa akan menyarankan mengutipnya, tapi itu hanya preferensi pribadi.)

tckmn
sumber
2
Ini benar-benar harus menjadi jawaban yang dipilih. 1: Sederhana dan mudah dibaca. 2: Ini menangani spasi putih dengan benar, tidak ada omong kosong IFS yang menghalangi. 3: Ini menangani array jarang dengan benar.
mrm
20

Ini juga mudah dibaca:

FilePath=(
    "/tmp/path1/"    #FilePath[0]
    "/tmp/path2/"    #FilePath[1]
)

#Loop
for Path in "${FilePath[@]}"
do
    echo "$Path"
done
Treethawat Thanawachiramate
sumber
4
Yang ini jelas dan bekerja untuk saya (termasuk dengan spasi dan substitusi variabel dalam elemen array FilePath) hanya ketika saya mengatur variabel IFS dengan benar sebelum definisi array FilePath: IFS=$'\n' Ini dapat bekerja untuk solusi lain juga dalam skenario ini.
Alan Forsyth
8

Array tersirat untuk skrip atau fungsi:

Selain jawaban yang benar anubhava : Jika sintaks dasar untuk loop adalah:

for var in "${arr[@]}" ;do ...$var... ;done

ada kasus khusus di:

Saat menjalankan skrip atau fungsi, argumen lulus di jalur perintah akan ditugaskan untuk $@variabel array, Anda dapat mengakses dengan $1, $2, $3, dan sebagainya.

Ini dapat diisi (untuk pengujian) oleh

set -- arg1 arg2 arg3 ...

Satu putaran atas ini array yang dapat ditulis hanya:

for item ;do
    echo "This is item: $item."
  done

Catatan bahwa karya yang dipesan intidak ada dan tidak ada nama array juga!

Sampel:

set -- arg1 arg2 arg3 ...
for item ;do
    echo "This is item: $item."
  done
This is item: arg1.
This is item: arg2.
This is item: arg3.
This is item: ....

Perhatikan bahwa ini sama dengan

for item in "$@";do
    echo "This is item: $item."
  done

Lalu menjadi naskah :

#!/bin/bash

for item ;do
    printf "Doing something with '%s'.\n" "$item"
  done

Simpan ini dalam skrip myscript.sh,chmod +x myscript.sh maka

./myscript.sh arg1 arg2 arg3 ...
Doing something with 'arg1'.
Doing something with 'arg2'.
Doing something with 'arg3'.
Doing something with '...'.

Sama dalam suatu fungsi :

myfunc() { for item;do cat <<<"Working about '$item'."; done ; }

Kemudian

myfunc item1 tiem2 time3
Working about 'item1'.
Working about 'tiem2'.
Working about 'time3'.
F. Hauri
sumber
7
listOfNames="db_one db_two db_three"
for databaseName in $listOfNames
do
  echo $databaseName
done

atau hanya

for databaseName in db_one db_two db_three
do
  echo $databaseName
done
nroose
sumber
7

Cara sederhana:

arr=("sharlock"  "bomkesh"  "feluda" )  ##declare array

len=${#arr[*]}  # it returns the array length

#iterate with while loop
i=0
while [ $i -lt $len ]
do
    echo ${arr[$i]}
    i=$((i+1))
done


#iterate with for loop
for i in $arr
do
  echo $i
done

#iterate with splice
 echo ${arr[@]:0:3}
ruam
sumber
6

Array menyatakan tidak bekerja untuk Korn shell. Gunakan contoh di bawah ini untuk shell Korn:

promote_sla_chk_lst="cdi xlob"

set -A promote_arry $promote_sla_chk_lst

for i in ${promote_arry[*]};
    do
            echo $i
    done
Bhanu
sumber
2
coba stabilo kode di editor untuk membuat kode Anda terlihat bagus.
menyelam
5
Senang tahu, tetapi pertanyaan ini tentang bash.
Brad Koch
1
Lotsa bug di sini. Tidak dapat memiliki entri daftar dengan spasi, tidak dapat memiliki entri daftar dengan karakter glob. for i in ${foo[*]}pada dasarnya selalu hal yang salah - for i in "${foo[@]}"adalah bentuk yang mempertahankan batas-batas daftar asli dan mencegah ekspansi global. Dan gaungnya harusecho "$i"
Charles Duffy
4

Ini mirip dengan jawaban user2533809, tetapi setiap file akan dieksekusi sebagai perintah terpisah.

#!/bin/bash
names="RA
RB
R C
RD"

while read -r line; do
    echo line: "$line"
done <<< "$names"
Jamie
sumber
3

Jika Anda menggunakan Korn shell, ada " set -A databaseName ", kalau tidak ada " mendeklarasikan -a databaseName "

Untuk menulis skrip yang bekerja pada semua shell,

 set -A databaseName=("db1" "db2" ....) ||
        declare -a databaseName=("db1" "db2" ....)
# now loop 
for dbname in "${arr[@]}"
do
   echo "$dbname"  # or whatever

done

Itu harus bekerja pada semua kerang.

Elang yang cerdas
sumber
3
Tidak, ini tidak: $ bash --versionGNU bash, versi 4.3.33 (0) -release (amd64-portbld-freebsd10.0) $ set -A databaseName=("db1" "db2" ....) || declare -a databaseName=("db1" "db2" ....)bash: kesalahan sintaksis dekat token yang tidak terduga `('
Bernie Reiter
2

Coba ini. Ini bekerja dan diuji.

for k in "${array[@]}"
do
    echo $k
done

# For accessing with the echo command: echo ${array[0]}, ${array[1]}
Simpal Kumar
sumber
3
Ini sebenarnya tidak berfungsi dengan benar. Coba array=( "hello world" ), atau arrray=( "*" ); dalam kasus pertama itu akan mencetak hellodan worldsecara terpisah, dalam yang kedua itu akan mencetak daftar file, bukan*
Charles Duffy
6
... sebelum memanggil sesuatu "diuji" di shell, pastikan untuk memeriksa kasing sudut, mana spasi putih dan gumpalan keduanya.
Charles Duffy
2

Baris pertama yang mungkin dari setiap skrip / sesi Bash:

say() { for line in "${@}" ; do printf "%s\n" "${line}" ; done ; }

Gunakan misalnya:

$ aa=( 7 -4 -e ) ; say "${aa[@]}"
7
-4
-e

Dapat mempertimbangkan: echopenafsiran -esebagai opsi di sini

elf12
sumber
2

Perulangan garis tunggal,

 declare -a listOfNames=('db_a' 'db_b' 'db_c')
 for databaseName in ${listOfNames[@]}; do echo $databaseName; done;

Anda akan mendapatkan output seperti ini,

db_a
db_b
db_c
Mohideen bin Mohammed
sumber
2

Yang benar-benar saya butuhkan untuk ini adalah sesuatu seperti ini:

for i in $(the_array); do something; done

Contohnya:

for i in $(ps -aux | grep vlc  | awk '{ print $2 }'); do kill -9 $i; done

(Akan membunuh semua proses dengan vlc dalam nama mereka)

Mahdi-Malv
sumber
1

Saya mengulangi array proyek saya untuk git pullpembaruan:

#!/bin/sh
projects="
web
ios
android
"
for project in $projects do
    cd  $HOME/develop/$project && git pull
end
jiahut
sumber
7
Sementara potongan kode ini dapat menyelesaikan pertanyaan, termasuk penjelasan sangat membantu untuk meningkatkan kualitas posting Anda. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa depan, dan orang-orang itu mungkin tidak tahu alasan untuk saran kode Anda. Tolong juga cobalah untuk tidak membuat kerumunan kode Anda dengan komentar penjelasan, ini mengurangi keterbacaan kode dan penjelasan!
kayess