Durchlaufen Sie eine Reihe von Zeichenfolgen in Bash?

1501

Ich möchte ein Skript schreiben, das 15 Zeichenfolgen durchläuft (Array möglicherweise?). Ist das möglich?

Etwas wie:

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

Antworten:

2391

Sie können es so verwenden:

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

Funktioniert auch für die mehrzeilige Array-Deklaration

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

Das ist natürlich möglich.

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

Weitere Informationen finden Sie unter Bash-Schleifen für, während und bis .

4 zog
quelle
18
Was ist das Problem bei diesem Ansatz? In einfachen Fällen scheint es zu funktionieren und ist dann intuitiver als die Antwort von @ anubhava.
Dr. Jan-Philip Gehrcke
17
Dies funktioniert besonders gut bei der Befehlssubstitution, z for year in $(seq 2000 2013).
Brad Koch
23
Das Problem ist, dass er nach dem Durchlaufen eines Arrays gefragt hat.
mgalgs
20
Der Ansatz "Deklarieren" funktioniert am besten, wenn Sie dasselbe Array an mehr als einer Stelle durchlaufen müssen. Dieser Ansatz ist sauberer, aber weniger flexibel.
StampyCode
15
Warum ist das nicht die Nummer 1? Es ist sauberer und Sie können das Array einfach wiederverwenden, indem Sie einen String festlegen, z DATABASES="a b c d e f".
Nerdmaster
201

Keine dieser Antworten enthält einen Zähler ...

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

Ausgabe:

1  /  3  :  one
2  /  3  :  two
3  /  3  :  three
caktux
quelle
7
Dies sollte die akzeptierte Antwort sein, ist die einzige, die funktioniert, wenn Array-Elemente Leerzeichen enthalten.
Jesjimher
1
Dies dient nur der Ausgabe des Beispiels mit einem Zähler. Es ist auch ziemlich trivial, das zu ändern, und funktioniert trotzdem.
Caktux
7
Das Echo am Ende ist fehlerhaft. Sie müssen keine Konstanten in Anführungszeichen setzen, Sie müssen Erweiterungen in Anführungszeichen setzen oder können sicher beide einfach wie folgt zitieren: echo "$i / ${arraylength} : ${array[$i-1]}"- Wenn Ihr $iGlob einen Globus enthält, wird er erweitert, wenn er eine Registerkarte enthält, in die er geändert wird ein Leerzeichen usw.
Charles Duffy
10
@bzeaman, klar - aber wenn Sie in solchen Dingen schlampig sind, ist eine Kontextanalyse (wie Sie es gerade getan haben) erforderlich, um etwas Richtiges zu beweisen, und eine erneute Analyse, wenn sich dieser Kontext ändert oder der Code an einem anderen Ort oder für wiederverwendet wird Aus irgendeinem anderen Grund ist ein unerwarteter Kontrollfluss möglich. Schreiben Sie es auf robuste Weise und es ist unabhängig vom Kontext korrekt.
Charles Duffy
5
Dies funktioniert nicht für ein Array mit geringer Dichte, dh wenn dem Array Elemente fehlen. Wenn Sie beispielsweise Array-Elemente A [1] = 'xx', A [4] = 'yy' und A [9] = 'zz' haben, beträgt die Länge 3 und die Schleife verarbeitet nicht alle Elemente .
Herr Ed
145

Ja

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

Ausgabe:

Item1
Item2
Item3
Item4

Räume bewahren; Einträge in Listen mit einfachen oder doppelten Anführungszeichen und Erweiterungen mit doppelten Anführungszeichen.

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

Ausgabe:

Item 1
Item 2
Item 3
Item 4

Liste über mehrere Zeilen erstellen

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

Ausgabe:

Item1
Item2
Item3
Item4


Einfache Listenvariable

List=( Item1 Item2 Item3 )

oder

List=(
      Item1 
      Item2 
      Item3
     )

Zeigen Sie die Listenvariable an:

echo ${List[*]}

Ausgabe:

Item1 Item2 Item3

Durchlaufen Sie die Liste:

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

Ausgabe:

Item1
Item2
Item3

Erstellen Sie eine Funktion, um eine Liste durchzugehen:

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

Verwenden Sie das Schlüsselwort declare (Befehl), um die Liste zu erstellen, die technisch als Array bezeichnet wird:

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

Ausgabe:

element 1
element 2
element 3

Erstellen eines assoziativen Arrays. Ein Wörterbuch:

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

Ausgabe:

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

CVS-Variablen oder -Dateien in eine Liste aufnehmen .
Ändern des internen Feldtrennzeichens von einem Leerzeichen nach Belieben.
Im folgenden Beispiel wird es in ein Komma geändert

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

Ausgabe:

Item 1
Item 2
Item 3

Wenn Sie sie nummerieren müssen:

` 

Dies wird als Back Tick bezeichnet. Setzen Sie den Befehl in Back Ticks.

`commend` 

Es befindet sich neben der Nummer eins auf Ihrer Tastatur und oder über der Tabulatortaste. Auf einer Standardtastatur in amerikanischer englischer Sprache.

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

Ausgabe ist:

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

Machen Sie sich mit dem Verhalten von Bashes vertraut:

Erstellen Sie eine Liste in einer Datei

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

Lesen Sie die Listendatei in eine Liste ein und zeigen Sie sie an

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

BASH-Befehlszeilenreferenzhandbuch: Besondere Bedeutung bestimmter Zeichen oder Wörter für die Shell.

Feuer im Himmel
quelle
7
DAS IST FALSCH. Muss "${List[@]}"korrekt sein, mit den Anführungszeichen . ${List[@]}ist falsch. ${List[*]}ist falsch. Versuchen Sie es List=( "* first item *" "* second item *" )- Sie erhalten das richtige Verhalten für for item in "${List[@]}"; do echo "$item"; done, aber nicht für eine andere Variante.
Charles Duffy
Ja, spezielle Zeichen werden interpretiert, was wünschenswert oder nicht wünschenswert sein kann. Ich habe die Antwort aktualisiert, um sie einzuschließen.
FireInTheSky
2
Ich würde immer noch stark darauf hin , die korrektere / robusteren Ansatz zeigt erste . Leute nehmen oft die erste Antwort, die so aussieht, als würde sie funktionieren. Wenn diese Antwort versteckte Vorbehalte enthält, können sie sich erst später aussetzen. (Es ist nicht nur Platzhalter , die durch einen Mangel gebrochen zu zitieren, List=( "first item" "second item" )wird in gebrochen werden first, item, second, itemals auch).
Charles Duffy
Sie könnten auch in Betracht ziehen, die Verwendung eines Beispiels zu vermeiden, das dazu führen könnte, dass Personen die lsAusgabe analysieren , was gegen bewährte Verfahren verstößt .
Charles Duffy
1
Sie widerlegen Behauptungen, die ich nie gemacht habe. Ich bin ziemlich vertraut mit Bashs Zitiersemantik
Charles Duffy
108

Im gleichen Sinne wie 4ndrews Antwort:

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. Kein Leerzeichen in den Namen:

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

Anmerkungen

  1. Im zweiten Beispiel hat using listOfNames="RA RB R C RD"die gleiche Ausgabe.

Andere Möglichkeiten, Daten einzugeben, sind:

Lesen Sie von 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. Das Bash- IFS -Trennzeichen "Feldtrennzeichen für Zeile" [ 1 ] kann im Skript angegeben werden, um andere Leerzeichen (dh IFS='\n'oder für MacOS IFS='\r') zuzulassen.
  2. Ich mag die akzeptierte Antwort auch :) - Ich habe diese Schnipsel als andere hilfreiche Methoden aufgenommen, die auch die Frage beantworten.
  3. Das Einfügen #!/bin/bashoben in die Skriptdatei zeigt die Ausführungsumgebung an.
  4. Ich habe Monate gebraucht, um herauszufinden, wie man das einfach codiert :)

Andere Quellen ( während der Leseschleife )

user2533809
quelle
Dies erweckt den Eindruck, dass eol als Zeichenfolgentrennzeichen verwendet wird und daher Leerzeichen innerhalb der Zeichenfolgen zulässig sind. Zeichenfolgen mit Leerzeichen werden jedoch weiter in Teilzeichenfolgen unterteilt, was sehr, sehr schlecht ist. Ich denke, dass diese Antwort stackoverflow.com/a/23561892/1083704 besser ist.
Val
1
@Val, ich habe einen Codekommentar mit einem Verweis auf hinzugefügt IFS. (Geben Sie für alle IFSein bestimmtes Trennzeichen an, mit dem andere Leerzeichen in Zeichenfolgen eingefügt werden können, ohne in Teilzeichenfolgen getrennt zu werden.)
user2533809
7
Das funktioniert bei mir nicht. $databaseNameenthält nur die gesamte Liste, führt also nur eine einzige Iteration durch.
AlikElzin-Kilaka
@ AlikElzin-kilaka Meine Antwort unten löst dieses Problem, sodass die Schleife für jede Zeile der Zeichenfolge ausgeführt wird.
Jamie
42

Sie können die Syntax von verwenden ${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
quelle
31

Überrascht, dass dies noch niemand gepostet hat - wenn Sie die Indizes der Elemente benötigen, während Sie das Array durchlaufen, können Sie dies tun:

arr=(foo bar baz)

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

Ausgabe:

0 foo
1 bar
2 baz

Ich finde das viel eleganter als den "traditionellen" For-Loop-Stil ( for (( i=0; i<${#arr[@]}; i++ ))).

( ${!arr[@]}und $imüssen nicht zitiert werden, weil es sich nur um Zahlen handelt; einige würden vorschlagen, sie trotzdem zu zitieren, aber das ist nur eine persönliche Präferenz.)

Türknauf
quelle
2
Dies sollte wirklich die gewählte Antwort sein. 1: Es ist einfach und leicht zu lesen. 2: Es behandelt Leerzeichen korrekt, kein IFS-Unsinn stört. 3: Es behandelt spärliche Arrays korrekt.
Herr
20

Dies ist auch leicht zu lesen:

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

#Loop
for Path in "${FilePath[@]}"
do
    echo "$Path"
done
Treethawat Thanawachiramate
quelle
4
Dieser ist klar und funktioniert für mich (einschließlich Leerzeichen und Variablensubstitution in den FilePath-Array-Elementen) nur, wenn ich die IFS-Variable vor der FilePath-Array-Definition korrekt festgelegt habe: IFS=$'\n' Dies funktioniert möglicherweise auch für andere Lösungen in diesem Szenario.
Alan Forsyth
8

Implizites Array für Skript oder Funktionen:

Zusätzlich zu Anubhavas korrekter Antwort: Wenn die grundlegende Syntax für die Schleife lautet:

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

Es gibt einen Sonderfall in::

Wenn ein Skript oder eine Funktion ausgeführt wird , Argumente werden auf Befehlszeilen übergeben zugeordnet werden $@Array - Variable, können Sie durch Zugriff $1, $2, $3, und so weiter.

Dies kann (zu Testzwecken) von ausgefüllt werden

set -- arg1 arg2 arg3 ...

Eine Schleife über dieses Array könnte einfach geschrieben werden:

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

Beachten Sie, dass die reservierte Arbeit innicht vorhanden ist und auch kein Array-Name!

Stichprobe:

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

Beachten Sie, dass dies dasselbe ist wie

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

Dann in ein Skript :

#!/bin/bash

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

Speichern Sie diese in einem Skript myscript.sh, chmod +x myscript.shdann

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

Gleiches in einer Funktion :

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

Dann

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

oder nur

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

Einfacher Weg :

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}
Hautausschläge
quelle
6

Das Deklarationsarray funktioniert nicht für die Korn-Shell. Verwenden Sie das folgende Beispiel für die 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
Bhanu
quelle
2
Probieren Sie den Code-Textmarker im Editor aus, damit Ihr Code gut aussieht.
Taube
5
Gut zu wissen, aber bei dieser Frage geht es um Bash.
Brad Koch
1
Viele Fehler hier. Listeneinträge mit Leerzeichen können nicht vorhanden sein, Listeneinträge mit Glob-Zeichen können nicht vorhanden sein. for i in ${foo[*]}ist im Grunde immer das Falsche - for i in "${foo[@]}"ist die Form, die die Grenzen der ursprünglichen Liste beibehält und die Glob-Erweiterung verhindert. Und das Echo muss seinecho "$i"
Charles Duffy
4

Dies ähnelt der Antwort von user2533809, aber jede Datei wird als separater Befehl ausgeführt.

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

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

Wenn Sie die Korn-Shell verwenden, gibt es " set -A databaseName ", andernfalls gibt es " declare -a databaseName ".

Um ein Skript zu schreiben, das an allen Shells arbeitet,

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

done

Es sollte an allen Muscheln gearbeitet werden.

Der kluge Adler
quelle
3
Nein, das tut es nicht: $ bash --versionGNU bash, Version 4.3.33 (0) -release (amd64-portbld-freebsd10.0) $ set -A databaseName=("db1" "db2" ....) || declare -a databaseName=("db1" "db2" ....)bash: Syntaxfehler in der Nähe eines unerwarteten Tokens `('
Bernie Reiter
2

Versuche dies. Es funktioniert und getestet.

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

# For accessing with the echo command: echo ${array[0]}, ${array[1]}
Simpal Kumar
quelle
3
Dies funktioniert nicht richtig. Versuchen Sie array=( "hello world" ), oder arrray=( "*" ); im ersten Fall wird es gedruckt hellound worldseparat, im zweiten wird es eine Liste von Dateien anstelle der*
Charles Duffy
6
... bevor Sie etwas "Getestetes" in der Shell aufrufen, überprüfen Sie unbedingt die Eckfälle, zu denen sowohl Leerzeichen als auch Globs gehören.
Charles Duffy
2

Mögliche erste Zeile jedes Bash-Skripts / jeder Bash-Sitzung:

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

Verwenden Sie zB:

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

Kann berücksichtigen: echointerpretiert hier -eals Option

elf12
quelle
2

Einzeilige Schleife,

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

Sie erhalten eine Ausgabe wie folgt:

db_a
db_b
db_c
Mohideen bin Mohammed
quelle
2

Was ich dafür wirklich brauchte, war ungefähr so:

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

Zum Beispiel:

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

(Würde alle Prozesse mit vlc in ihrem Namen beenden)

Mahdi-Malv
quelle
1

Ich durchlaufe eine Reihe meiner Projekte, um ein git pullUpdate zu erhalten:

#!/bin/sh
projects="
web
ios
android
"
for project in $projects do
    cd  $HOME/develop/$project && git pull
end
jiahut
quelle
7
Während dieses Code-Snippet die Frage lösen kann, hilft eine Erklärung wirklich, die Qualität Ihres Beitrags zu verbessern. Denken Sie daran, dass Sie die Frage für Leser in Zukunft beantworten und diese Personen möglicherweise die Gründe für Ihren Codevorschlag nicht kennen. Bitte versuchen Sie auch, Ihren Code nicht mit erklärenden Kommentaren zu überfüllen. Dies verringert die Lesbarkeit sowohl des Codes als auch der Erklärungen!
Kayess