Buclați o matrice de șiruri de caractere în Bash? (Programare, Array-Uri, Bash, Shell)

Mo. a intrebat.

Vreau să scriu un script care să facă o buclă prin 15 șiruri de caractere (array, eventual?) Este posibil?

Ceva de genul:

for databaseName in listOfNames
then
  # Do something
end

20 răspunsuri
anubhava

Puteți să-l utilizați astfel:

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

De asemenea, funcționează și pentru declarația de matrice cu mai multe linii

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

Comentarii

    29

  • Rețineți că ghilimelele duble din jurul "${arr[@]}" sunt foarte importante. Fără ele, bucla for va împărți array-ul în funcție de subșiruri separate de spațiile din interiorul șirurilor, în loc de elemente de șiruri întregi în cadrul array-ului. de exemplu: dacă ați avea declare -a arr=("element 1" "element 2" "element 3"), , atunci for i in ${arr[@]} ar itera din greșeală de 6 ori, deoarece fiecare șir devine 2 subșiruri separate de spațiul din șir, în timp ce for i in "${arr[@]}" ar itera de 3 ori, în mod corect, așa cum se dorește, menținând fiecare șir ca o singură unitate în ciuda faptului că are un spațiu în el. –  > Por Gabriel Staples.
4ndrew

Acest lucru este posibil, bineînțeles.

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

A se vedea Bucle Bash pentru, while și until pentru detalii.

Comentarii

    21

  • Care este problema cu această abordare? În cazuri simple pare să funcționeze și, apoi, este mai intuitivă decât răspunsul lui @anubhava. –  > Por Dr. Jan-Philip Gehrcke.
  • 19

  • Acest lucru funcționează deosebit de bine în cazul substituirii comenzilor, de exemplu for year in $(seq 2000 2013). –  > Por Brad Koch.
  • 24

  • Problema este că el a întrebat despre iterarea printr-un array. –  > Por mgalgs.
  • 21

  • Abordarea „declare” funcționează cel mai bine dacă trebuie să iterați peste același array în mai multe locuri. Această abordare este mai curată, dar mai puțin flexibilă. –  > Por StampyCode.
  • 16

  • De ce nu este aceasta numărul 1? Este mai curată și puteți reutiliza cu ușurință matricea doar prin setarea unui șir de caractere, de exemplu, DATABASES="a b c d e f". –  > Por Nerdmaster.
caktux

Nici unul dintre aceste răspunsuri nu include un contor…

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

Ieșire:

1  /  3  :  one
2  /  3  :  two
3  /  3  :  three

Comentarii

  • Acesta ar trebui să fie răspunsul acceptat, este singurul care funcționează atunci când elementele tabloului conțin spații. –  > Por jesjimher.
  • Asta doar de dragul ieșirii exemplului cu un contor. De asemenea, este destul de banal să schimbi asta și funcționează la fel. –  > Por caktux.
  • Ecoul de la sfârșit este eronat. Nu trebuie să citați constantele, trebuie să citați expansiunile, sau puteți să le citați în siguranță pe amândouă după cum urmează: echo "$i / ${arraylength} : ${array[$i-1]}" — altfel, dacă $i conține un glob va fi extins, dacă conține o tabulație va fi schimbat în spațiu, etc. –  > Por Charles Duffy.
  • @bzeaman, sigur — dar dacă ești neglijent cu astfel de lucruri, atunci este nevoie de o analiză contextuală (așa cum tocmai ai făcut) pentru a dovedi că ceva este corect, și de o nouă analiză dacă acel context se schimbă sau dacă codul este reutilizat într-un loc diferit sau dacă, din orice alt motiv, este posibil un flux de control neașteptat. Scrieți-l în mod robust și este corect indiferent de context. –  > Por Charles Duffy.
  • Acest lucru nu va funcționa în cazul unui tablou rarefiat, adică în cazul în care din tablou lipsesc elemente. De exemplu, dacă aveți elemente ale tabloului A[1]=’xx’, A[4]=’yy’ și A[9]=’zz’, lungimea va fi 3 și bucla nu va procesa toate elementele. –  > Por Dl Ed.
abc

Da

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

Ieșire:

Item1
Item2
Item3
Item4

Pentru a păstra spațiile; intrări în liste cu ghilimele simple sau duble și extinderi de liste cu ghilimele duble.

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

Ieșire:

Item 1
Item 2
Item 3
Item 4

Pentru a crea o listă pe mai multe linii.

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

Ieșire:

Item1
Item2
Item3
Item4

Variabilă de listă simplă.

List=( Item1 Item2 Item3 )

sau

List=(
      Item1 
      Item2 
      Item3
     )

Afișarea variabilei listă:

echo ${List[*]}

Ieșire:

Item1 Item2 Item3

Bucla prin listă:

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

Ieșire:

Item1
Item2
Item3

Creați o funcție care să parcurgă o listă:

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

Folosirea cuvântului cheie declare (comandă) pentru a crea lista, care se numește, din punct de vedere tehnic, un array:

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

Ieșire:

element 1
element 2
element 3

Crearea unui tablou asociativ. Un dicționar:

declare -A continent

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

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

Ieșire:

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

Introducerea variabilelor sau a fișierelor CSV într-o listă.
Schimbarea separatorului intern de câmpuri, de la un spațiu, la ce doriți.
În exemplul de mai jos, acesta este schimbat în virgulă

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

Ieșire:

Item 1
Item 2
Item 3

Dacă este nevoie să le numerotați:

` 

acest lucru se numește „bifare inversă”. Puneți comanda în interiorul căsuțelor din spate.

`command` 

Aceasta se află lângă numărul unu de pe tastatură și sau deasupra tastei tab, pe o tastatură standard de limbă engleză americană.

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

Ieșirea este:

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

Devenind mai familiarizat cu comportamentul bashes:

Crearea unei liste într-un fișier

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

Citiți fișierul cu lista într-un fișier într-o listă și afișați-o

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

Manual de referință pentru linia de comandă BASH: Semnificația specială a anumitor caractere sau cuvinte pentru shell.

Comentarii

  • ACEST LUCRU ESTE GREȘIT. Trebuie să fie "${List[@]}" pentru a fi corect, cu ghilimele. ${List[@]} este greșit. ${List[*]} este greșit. Încercați List=( "* first item *" "* second item *" ) — veți obține un comportament corect pentru for item in "${List[@]}"; do echo "$item"; done, , dar nu și pentru orice altă variantă. –  > Por Charles Duffy.
  • Da, caracterele speciale sunt interpretate, ceea ce poate fi sau nu de dorit. Am actualizat răspunsul pentru a include . –  > Por abc.
  • Aș sugera în continuare cu tărie să arătați abordarea mai corectă/robustă prima. Oamenii iau adesea primul răspuns care pare să funcționeze; dacă acel răspuns are anumite avertismente ascunse, s-ar putea să se expună doar mai târziu. (Nu sunt doar wildcards care sunt rupte din cauza lipsei de citare; List=( "first item" "second item" ) va fi rupt în first, , item, , second, , item de asemenea). –  > Por Charles Duffy.
  • De asemenea, ați putea lua în considerare evitarea utilizării unui exemplu care ar putea determina oamenii să interpreteze ls ieșire, în contradicție cu cele mai bune practici. –  > Por Charles Duffy.
  • Refutați afirmații pe care nu le-am făcut niciodată. Sunt destul de familiarizat cu semantica de citare a lui bash – vezi stackoverflow.com/tags/bash/topusers –  > Por Charles Duffy.
dajon

În același spirit ca și răspunsul lui 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. Fără spații albe în nume:

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

Note

  1. În al doilea exemplu, folosind listOfNames="RA RB R C RD" are același rezultat.

Alte modalități de a introduce date includ:

  • stdin (enumerate mai jos),
  • variabile,
  • o matrice (răspunsul acceptat),
  • un fișier…

Citiți din 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 „separator de câmp la linie” [1] poate fi specificat în script pentru a permite alte spații albe (de ex. IFS='
    '
    , , sau pentru MacOS IFS='r')
  2. Îmi place și răspunsul acceptat 🙂 — Am inclus aceste fragmente ca alte modalități utile care răspund, de asemenea, la întrebare.
  3. Includerea #!/bin/bash în partea de sus a fișierului de script indică mediul de execuție.
  4. Mi-a luat luni de zile să descopăr cum să codific acest lucru simplu 🙂

Alte surse(while read loop)

Comentarii

  • Acest lucru creează impresia că eol este folosit ca separatori de șiruri de caractere și, prin urmare, spațiile albe sunt permise în interiorul șirurilor. Cu toate acestea, șirurile cu spații albe sunt separate ulterior în subșiruri, ceea ce este foarte foarte rău. Cred că acest răspuns stackoverflow.com/a/23561892/1083704 este mai bun. –  > Por Val.
  • @Val, am adăugat un comentariu de cod cu o referință la IFS. (Pentru toată lumea, IFS permite specificarea unui delimitator specific, ceea ce permite includerea altor spații albe în șiruri fără a fi separate în subșiruri). –  > Por dajon.
  • Acest lucru nu funcționează pentru mine. $databaseName conține doar întreaga listă, deci face doar o singură iterație. –  > Por AlikElzin-kilaka.
  • @AlikElzin-kilaka Răspunsul meu de mai jos rezolvă această problemă astfel încât bucla să fie executată pentru fiecare linie a șirului. –  > Por Jamie.
Fizer Khan

Puteți utiliza sintaxa ${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

tckmn

Surprins că nimeni nu a postat încă acest lucru – dacă aveți nevoie de indicii elementelor în timp ce faceți o buclă prin matrice, puteți face acest lucru:

arr=(foo bar baz)

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

Ieșire:

0 foo
1 bar
2 baz

Găsesc acest lucru mult mai elegant decât stilul „tradițional” for-loop (for (( i=0; i<${#arr[@]}; i++ ))).

(${!arr[@]} și $i nu trebuie să fie citate deoarece sunt doar numere; unii ar sugera să le citez oricum, dar aceasta este doar o preferință personală).

Comentarii

  • Acesta ar trebui să fie într-adevăr răspunsul ales. 1: Este simplu și ușor de citit. 2: Se ocupă corect de spațiile albe, nu există nicio prostie IFS care să stea în cale. 3: Gestionează corect array-urile rarefiate. –  > Por mrm.
Treethawat Thanawachiramate

Este, de asemenea, ușor de citit:

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

#Loop
for Path in "${FilePath[@]}"
do
    echo "$Path"
done

Comentarii

  • Acesta este clar și a funcționat pentru mine (inclusiv cu spații și substituirea variabilei în elementele de matrice FilePath) numai atunci când am setat corect variabila IFS înainte de definirea matricei FilePath: IFS=$'
    '
    Aceasta poate funcționa și pentru alte soluții în acest scenariu. –  > Por Alan Forsyth.
F. Hauri

Matrice implicită pentru script sau funcții:

În plus față de răspunsul corect al lui anubhava: Dacă sintaxa de bază pentru buclă este:

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

există un specială caz în :

Atunci când se execută un script sau o funcție, argumente transmise la liniile de comandă vor fi atribuite la [email protected] variabilă array, pe care o puteți accesa prin $1, , $2, , $3, , și așa mai departe.

Aceasta poate fi completată (pentru test) prin

set -- arg1 arg2 arg3 ...

A bucla pe acest array ar putea fi scrisă simplu:

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

Notă că lucrarea rezervată in nu este prezentă și nici numele tabloului nu este prezent!

Exemplu:

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: ....

Rețineți că este același lucru cu

for item in "[email protected]";do
    echo "This is item: $item."
  done

Apoi într-un script:

#!/bin/bash

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

Salvați acest lucru într-un script myscript.sh, , chmod +x myscript.sh, , apoi

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

Același lucru într-un funcție:

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

Atunci

myfunc item1 tiem2 time3
Working about 'item1'.
Working about 'tiem2'.
Working about 'time3'.

nroose
listOfNames="db_one db_two db_three"
for databaseName in $listOfNames
do
  echo $databaseName
done

sau doar

for databaseName in db_one db_two db_three
do
  echo $databaseName
done

rashedcs

Mod simplu :

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}

Bhanu

Declararea matricei nu funcționează pentru Korn shell. Folosiți exemplul de mai jos pentru Korn shell:

promote_sla_chk_lst="cdi xlob"

set -A promote_arry $promote_sla_chk_lst

for i in ${promote_arry[*]};
    do
            echo $i
    done

Comentarii

  • Încercați să folosiți indicatorul de cod din editor pentru a face codul să arate bine. –  > Por dove.
  • Este bine de știut, dar această întrebare se referă la bash. –  > Por Brad Koch.
  • Lotsa bug-uri aici. Nu pot avea intrări în listă cu spații, nu pot avea intrări în listă cu caractere glob. for i in ${foo[*]} este practic întotdeauna un lucru greșit… for i in "${foo[@]}" este forma care păstrează limitele listei originale și previne extinderea globurilor. Iar echo trebuie să fie echo "$i" –  > Por Charles Duffy.
Jamie

Acest lucru este similar cu răspunsul utilizatorului2533809, dar fiecare fișier va fi executat ca o comandă separată.

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

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

Simpal Kumar

Încercați acest lucru. Funcționează și este testat.

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

# For accessing with the echo command: echo ${array[0]}, ${array[1]}

Comentarii

  • Acest lucru nu funcționează de fapt corect. Încercați array=( "hello world" ), , sau arrray=( "*" ); în primul caz, se va imprima hello și world separat, în al doilea caz va imprima o listă de fișiere în loc de * –  > Por Charles Duffy.
  • …înainte de a numi ceva „testat” în shell, asigurați-vă că ați verificat cazurile colțuroase, printre care se numără atât spațiile albe, cât și globele. –  > Por Charles Duffy.
Vulturul inteligent

Dacă folosiți shell-ul Korn, există „set -A databaseName „, altfel există „declare -a databaseName

Pentru a scrie un script care să funcționeze pe toate shell-urile,

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

done

ar trebui să funcționeze pe toate shell-urile.

Comentarii

  • Nu, nu funcționează: $ bash --version GNU bash, versiunea 4.3.33(0)-release (amd64-portbld-freebsd10.0). $ set -A databaseName=("db1" "db2" ....) || declare -a databaseName=("db1" "db2" ....) bash: eroare de sintaxă în apropierea simbolului neașteptat `(‘ –  > Por Bernie Reiter.
Mahdi-Malv

Ceea ce aveam cu adevărat nevoie pentru acest lucru era ceva de genul următor:

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

De exemplu:

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

(Ar ucide toate procesele cu vlc în numele lor)

elf12

Prima linie posibilă a fiecărui script/sesiune Bash:

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

Folosiți de exemplu:

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

Poate lua în considerare: echo interpretează -e ca opțiune aici

Mohideen bin Mohammed

Buclă pe o singură linie,

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

veți obține o ieșire ca aceasta,

db_a
db_b
db_c

jiahut

Fac o buclă printr-o matrice a proiectelor mele pentru un git pull actualizare:

#!/bin/sh
projects="
web
ios
android
"
for project in $projects do
    cd  $HOME/develop/$project && git pull
end

Comentarii

  • În timp ce acest fragment de cod poate rezolva întrebarea, includerea unei explicații ajută într-adevăr la îmbunătățirea calității mesajului dvs. Nu uitați că răspundeți la întrebare pentru cititorii din viitor, iar acele persoane ar putea să nu cunoască motivele pentru sugestia dvs. de cod. De asemenea, vă rugăm să încercați să nu aglomerați codul cu comentarii explicative, acest lucru reduce lizibilitatea atât a codului, cât și a explicațiilor! –  > Por kayess.
  • ar trebui să explicați IFS comportamentul variabilelor din scriptul dvs.  > Por Akhil Jalagam.
Dan Bray

Modul în care faci o buclă printr-un array, depinde de prezența caracterelor de linie nouă. În cazul în care caracterele de linie nouă separă elementele tabloului, tabloul poate fi denumit "$array", , în caz contrar ar trebui să fie denumit "${array[@]}". Următorul script va clarifica acest lucru:

#!/bin/bash

mkdir temp
mkdir temp/aaa
mkdir temp/bbb
mkdir temp/ccc
array=$(ls temp)
array1=(aaa bbb ccc)
array2=$(echo -e "aaa
bbb
ccc")

echo '$array'
echo "$array"
echo
for dirname in "$array"; do
    echo "$dirname"
done
echo
for dirname in "${array[@]}"; do
    echo "$dirname"
done
echo
echo '$array1'
echo "$array1"
echo
for dirname in "$array1"; do
    echo "$dirname"
done
echo
for dirname in "${array1[@]}"; do
    echo "$dirname"
done
echo
echo '$array2'
echo "$array2"
echo
for dirname in "$array2"; do
    echo "$dirname"
done
echo
for dirname in "${array2[@]}"; do
    echo "$dirname"
done
rmdir temp/aaa
rmdir temp/bbb
rmdir temp/ccc
rmdir temp