Loop through an array of strings in Bash?
Loop through an array of strings in Bash?
I want to write a script that loops through 15 strings (array possibly?) Is that possible?
Something like:
for databaseName in listOfNames
then
# Do something
end
18 Answers
18
You can use it like this:
## 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
Also works for multi-line array declaration
declare -a arr=("element1"
"element2" "element3"
"element4"
)
In BASH it is safer to quote the variable using
""
for the cases when $i
may contain white spaces or shell expandable characters.– anubhava
Sep 26 '14 at 16:29
""
$i
It does work with spaces in elements if it is quoted like showed above. Better you test it yourself.
– anubhava
Nov 12 '14 at 10:19
What are the advantages of
declare -a
? Can't I do something simpler, like arr=("element1" "element2" "element3")
?– Ignorant
Jan 19 '16 at 14:15
declare -a
arr=("element1" "element2" "element3")
declare
or typeset
are an explicit way of declaring variable in shell scripts.– anubhava
Jan 19 '16 at 15:38
declare
typeset
That is possible, of course.
for databaseName in a b c d e f; do
# do something like: echo $databaseName
done
See Bash Loops for, while and until for details.
What is the problem with this approach? In simple cases it seems to work and, then, is more intuitive than @anubhava's answer.
– Jan-Philip Gehrcke
Aug 30 '12 at 11:54
This works particularly well with command substitution, eg
for year in $(seq 2000 2013)
.– Brad Koch
May 20 '13 at 14:53
for year in $(seq 2000 2013)
The problem is that he asked about iterating through an array.
– mgalgs
Jul 18 '13 at 23:00
The 'declare' approach works best if you have to iterate over the same array in more than one place. This approach is cleaner but less flexible.
– StampyCode
Jan 3 '14 at 10:21
Why isn't this #1? It's cleaner, and you can easily reuse the array just by setting a string, i.e.,
DATABASES="a b c d e f"
.– Nerdmaster
Jul 2 '14 at 21:42
DATABASES="a b c d e f"
None of those answers include a counter...
#!/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
Output:
1 / 3 : one
2 / 3 : two
3 / 3 : three
This should be the accepted answer, is the only one that works when array elements contain spaces.
– jesjimher
Nov 12 '14 at 9:40
You should start i at 0 for sanity, of course.
– GManNickG
Feb 3 '15 at 19:32
That's just for the sake of the example's output with a counter. It's also quite trivial to change that, and works just the same.
– caktux
Jul 9 '15 at 5:35
The echo at the end is buggy. You don't need to quote constants, you need to quote expansions, or can safely just quote both as follows:
echo "$i / $arraylength : $array[$i-1]"
-- otherwise, if your $i
contains a glob it'll be expanded, if it contains a tab it'll be changed to a space, etc.– Charles Duffy
Jul 9 '16 at 15:02
echo "$i / $arraylength : $array[$i-1]"
$i
@bzeaman, sure -- but if you're sloppy about such things then it requires contextual analysis (as you just did) to prove something correct, and re-analysis if that context changes or the code is reused in a different place or for whatever other reason an unexpected control flow is possible. Write it the robust way and it's correct regardless of context.
– Charles Duffy
Sep 2 '16 at 14:57
In the same spirit as 4ndrew's answer:
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. No whitespace in the names:
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
Notes
listOfNames="RA RB R C RD"
Other ways to bring in data include:
Read from 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
IFS='n'
IFS='r'
#!/bin/bash
Other Sources
(while read loop)
This creates impression that eol is used as string separators and, therefore, whitespaces are allowed within the strings. However, strings with whitespaces are further separated into substrings, which is very very bad. I think that this answer stackoverflow.com/a/23561892/1083704 is better.
– Val
Jul 11 '14 at 9:02
@Val, I added code comment with a reference to
IFS
. (For everyone, IFS
lets one specify a specific delimiter, which allows other whitespace to be included in strings without being separated into substrings).– user2533809
Jul 21 '14 at 23:43
IFS
IFS
This doesn't work for me.
$databaseName
just contains the whole list, thus does only a single iteration.– AlikElzin-kilaka
Jul 18 '16 at 13:04
$databaseName
@AlikElzin-kilaka My answer below solves this problem so that the loop is run for every line of the string.
– Jamie
Oct 16 '16 at 22:00
Yes
for Item in Item1 Item2 Item3 Item4 ;
do
echo $Item
done
Output :
Item1
Item2
Item3
Item4
Over multiple lines
for Item in Item1
Item2
Item3
Item4
do
echo $Item
done
Output :
Item1
Item2
Item3
Item4
Simple list variable
List=( Item1 Item2 Item3 )
or
List=(
Item1
Item2
Item3
)
Display the list variable :
echo $List[*]
Out put :
Item1 Item2 Item3
Loop through the list:
for Item in $List[*]
do
echo $Item
done
Out put :
Item1
Item2
Item3
Create a function to go through a list:
Loop()
for item in $* ;
do
echo $item
done
Loop $List[*]
To preserve spaces ; single or double quote list entries and double quote list expansions :
List=(' Item 1 '
' Item 2'
' Item 3'
)
for item in "$List[@]";
do
echo "$item"
done
Output :
Item 1
Item 2
Item 3
Using the declare keyword (command) to create the list , which is technically called an array :
declare -a List=(
"element 1"
"element 2"
"element 3"
)
for entry in "$List[@]"
do
echo "$entry"
done
Out put :
element 1
element 2
element 3
Creating an associative array . A dictionary :
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
Output :
Argentina is in America
Vietnam is in Asia
France is in Europe
CVS variables or files in to a list .
Changing the internal field separator from a space , to what ever you want .
In the example below it is changed to a comma
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
Output :
Item 1
Item 2
Item 3
If need to number them :
`
this is called a back tick . Put the command inside back ticks .
`commend`
It is next to the number one on your keyboard and or above the tab key . On a standard american english language keyboard .
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
Out put is :
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
Becoming more familiar with bashes behavior :
Create a list in a 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
Read the list file in to a list and display
List=$(cat List_entries.txt)
echo $List
echo '$List'
echo "$List"
echo $List[*]
echo '$List[*]'
echo "$List[*]"
echo $List[@]
echo '$List[@]'
echo "$List[@]"
BASH commandline reference manual : Special meaning of certain characters or words to the shell.
THIS IS WRONG. Needs to be
"$List[@]"
to be correct, with the quotes. $List[@]
is wrong. $List[*]
is wrong. Try List=( "* first item *" "* second item *" )
-- you'll get correct behavior for for item in "$List[@]"; do echo "$item"; done
, but not from any other variant.– Charles Duffy
May 4 at 19:36
"$List[@]"
$List[@]
$List[*]
List=( "* first item *" "* second item *" )
for item in "$List[@]"; do echo "$item"; done
Yes special chars do get interpreted , which may or may not be desirable . I updated the answer to include .
– FireInTheSky
May 27 at 10:57
I still would strongly suggest showing the more correct/robust approach first. Folks often take the first answer that looks like it'll work; if that answer has hidden caveats, they may only expose themselves later. (It's not just wildcards that are broken by lack of quoting;
List=( "first item" "second item" )
will be broken into first
, item
, second
, item
as well).– Charles Duffy
May 27 at 15:13
List=( "first item" "second item" )
first
item
second
item
You might also consider avoiding use of an example that might lead people to parse
ls
output, in contravention of best practices.– Charles Duffy
May 27 at 15:16
ls
You're refuting claims I never made. I'm quite familiar with bash's quoting semantics -- see stackoverflow.com/tags/bash/topusers
– Charles Duffy
May 31 at 23:59
You can use the syntax of $arrayName[@]
$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
This is also easy to read:
FilePath=(
"/tmp/path1/" #FilePath[0]
"/tmp/path2/" #FilePath[1]
)
#Loop
for Path in "$FilePath[@]"
do
echo "$Path"
done
This one is clear and worked for me (including with spaces and variable substitution in the FilePath array elements) only when I set the IFS variable correctly before the FilePath array definition:
IFS=$'n'
This may work for other solutions too in this scenario.– Alan Forsyth
Jan 22 '16 at 12:41
IFS=$'n'
Surprised that nobody's posted this yet -- if you need the indices of the elements while you're looping through the array, you can do this:
arr=(foo bar baz)
for i in $!arr[@]
do
echo $i "$arr[i]"
done
Output:
0 foo
1 bar
2 baz
I find this a lot more elegant than the "traditional" for-loop style (for (( i=0; i<$#arr[@]; i++ ))
).
for (( i=0; i<$#arr[@]; i++ ))
($!arr[@]
and $i
don't need to be quoted because they're just numbers; some would suggest quoting them anyway, but that's just personal preference.)
$!arr[@]
$i
listOfNames="db_one db_two db_three"
for databaseName in $listOfNames
do
echo $databaseName
done
or just
for databaseName in db_one db_two db_three
do
echo $databaseName
done
The declare array doesn't work for Korn shell. Use the below example for the 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
try the code highlighter in the editor to make your code look good.
– dove
Nov 2 '12 at 19:39
Good to know, but this question is about bash.
– Brad Koch
May 20 '13 at 14:47
Who uses Korn? Ok band, but that's about it...
– Mike Purcell
Nov 5 '15 at 16:40
Lotsa bugs here. Can't have list entries with spaces, can't have list entries with glob characters.
for i in $foo[*]
is basically always the wrong thing -- for i in "$foo[@]"
is the form that preserves the original list's boundaries and prevents glob expansion. And the echo needs to be echo "$i"
– Charles Duffy
Jul 9 '16 at 15:00
for i in $foo[*]
for i in "$foo[@]"
echo "$i"
In addition to anubhava's correct answer: If basic syntax for loop is:
for var in "$arr[@]" ;do ...$var... ;done
there is a special case in bash:
When running a script or a function, arguments passed at command lines will be assigned to $@
array variable, you can access by $1
, $2
, $3
, and so on.
$@
$1
$2
$3
This can be populated (for test) by
set -- arg1 arg2 arg3 ...
A loop over this array could be written simply:
for item ;do
echo "This is item: $item."
done
Note that the reserved work in
is not present and no array name too!
in
Sample:
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: ....
Note that this is same than
for item in "$@";do
echo "This is item: $item."
done
#!/bin/bash
for item ;do
printf "Doing something with '%s'.n" "$item"
done
Save this in a script myscript.sh
, chmod +x myscript.sh
, then
myscript.sh
chmod +x myscript.sh
./myscript.sh arg1 arg2 arg3 ...
Doing something with 'arg1'.
Doing something with 'arg2'.
Doing something with 'arg3'.
Doing something with '...'.
myfunc() for item;do cat <<<"Working about '$item'."; done ;
Then
myfunc item1 tiem2 time3
Working about 'item1'.
Working about 'tiem2'.
Working about 'time3'.
If you are using Korn shell, there is "set -A databaseName ", else there is "declare -a databaseName"
To write a script working on all shells,
set -A databaseName=("db1" "db2" ....) ||
declare -a databaseName=("db1" "db2" ....)
# now loop
for dbname in "$arr[@]"
do
echo "$dbname" # or whatever
done
It should be work on all shells.
Nope, it doesn't:
$ bash --version
GNU bash, version 4.3.33(0)-release (amd64-portbld-freebsd10.0) $ set -A databaseName=("db1" "db2" ....) || declare -a databaseName=("db1" "db2" ....)
bash: syntax error near unexpected token `('– Bernie Reiter
Jan 17 '17 at 19:47
$ bash --version
$ set -A databaseName=("db1" "db2" ....) || declare -a databaseName=("db1" "db2" ....)
Simple way :
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
This is similar to user2533809's answer, but each file will be executed as a separate command.
#!/bin/bash
names="RA
RB
R C
RD"
while read -r line; do
echo line: "$line"
done <<< "$names"
Possible first line of every Bash script/session:
say() for line in "$@" ; do printf "%sn" "$line" ; done ;
Use e.g.:
$ aa=( 7 -4 -e ) ; say "$aa[@]"
7
-4
-e
May consider: echo
interprets -e
as option here
echo
-e
Single line looping,
declare -a listOfNames=('db_a' 'db_b' 'db_c')
for databaseName in $listOfNames[@]; do echo $databaseName; done;
you will get an output like this,
db_a
db_b
db_c
Try this. It is working and tested.
for k in "$array[@]"
do
echo $k
done
# For accessing with the echo command: echo $array[0], $array[1]
This does not actually work correctly. Try
array=( "hello world" )
, or arrray=( "*" )
; in the first case it will print hello
and world
separately, in the second it will print a list of files instead of the *
– Charles Duffy
Jul 9 '16 at 14:55
array=( "hello world" )
arrray=( "*" )
hello
world
*
...before calling something "tested" in shell, be sure to check the corner cases, which whitespace and globs are both among.
– Charles Duffy
Jul 9 '16 at 14:58
I loop through an array of my projects for a git pull
update:
git pull
#!/bin/sh
projects="
web
ios
android
"
for project in $projects do
cd $HOME/develop/$project && git pull
end
While this code snippet may solve the question, including an explanation really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. Please also try not to crowd your code with explanatory comments, this reduces the readability of both the code and the explanations!
– kayess
Dec 8 '16 at 8:37
Thank you for your interest in this question.
Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).
Would you like to answer one of these unanswered questions instead?
somehow the "" around $i is required to access each array element, can you explain why or point to some material I can read to understand it? Thanks so much!
– olala
Sep 26 '14 at 16:22