2402

I want to write a script that loops through 15 strings (array possibly?) Is that possible?

for databaseName in listOfNames
then
  # Do something
end
1

22 Answers 22

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

## loop through above array
for i in "${arr[@]}"
do
   echo "$i"
   # or do whatever with individual element of 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"
                )
Sign up to request clarification or add additional context in comments.

1 Comment

Note that the double quotes around "${arr[@]}" are really important. Without them, the for loop will break up the array by substrings separated by any spaces within the strings instead of by whole string elements within the array. ie: if you had declare -a arr=("element 1" "element 2" "element 3"), then for i in ${arr[@]} would mistakenly iterate 6 times since each string becomes 2 substrings separated by the space in the string, whereas for i in "${arr[@]}" would iterate 3 times, correctly, as desired, maintaining each string as a single unit despite having a space in it.
1188
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.

5 Comments

This works particularly well with command substitution, eg for year in $(seq 2000 2013).
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.
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: Yes, this is a more simple (and portable) code but storage of the list of strings separated by spaces in a variable (a single string in fact) prevents the individual strings to contain spaces. The reply of user2533809 solves this problem. ------ But there is another problem. If the list is very long and has to be updated such update operations require to copy the complete list every time so they will be very slow.
@BradKoch, why would you use seq (a nonstandard external command that POSIX doesn't require operating systems to provide at all) when bash's for loop has built-in math support? for ((year=2003; year<=2013; year++)); do
384

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=0; i<${arraylength}; i++ ));
do
  echo "index: $i, value: ${array[$i]}"
done

Output:

index: 0, value: one
index: 1, value: two
index: 2, value: three

5 Comments

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.
@CharlesDuffy $i won't contain a glob, because it's the loop counter in this example and so you control its value.
@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.
This won't work for a sparse array, i.e. if the array is missing elements. For example, if you have array elements A[1]='xx', A[4]='yy' and A[9]='zz', the length will be 3 and the loop won't process all the elements.
No need to get the length of array. Simply use for i in "${!array[@]}";
313
for Item in Item1 Item2 ;
  do
    echo $Item
  done

Output:

Item1
Item2

To preserve spaces; single or double quote list entries and double quote list expansions.

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

Output:

Item 1
Item 2

To make list over multiple lines

for Item in Item1 \
            Item2
  do
    echo $Item
  done

Output:

Item1
Item2

Simple list variable
List=( Item1 Item2 )

or

List=(
      Item1 
      Item2
     )

Display the list variable:

echo ${List[*]}

Output:

Item1 Item2

Loop through the list:

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

Output:

Item1
Item2

Create a function to go through a list:

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

Using the declare keyword (command) to create the list, which is technically called an array:

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

Output:

element 1
element 2

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

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

`command` 

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

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

22 Comments

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.
Yes special chars do get interpreted , which may or may not be desirable . I updated the answer to include .
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).
You might also consider avoiding use of an example that might lead people to parse ls output, in contravention of best practices.
That maybe desirable . echo ${fileTypes[@]} ; fileTypes=(html css js PHP) ; for entry in "${fileTypes[@]}" ; do echo "$entry" ; done . It could be used piped in to other programs and it will be single words , that is how it was designed to work originally . That has to be known , learned .
|
117

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

  1. In the second example, using listOfNames="RA RB R C RD" has the same output.

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
  1. the bash IFS "field separator to line" [1] delimiter can be specified in the script to allow other whitespace (i.e. IFS='\n', or for MacOS IFS='\r')
  2. I like the accepted answer also :) -- I've include these snippets as other helpful ways that also answer the question.
  3. Including #!/bin/bash at the top of the script file indicates the execution environment.
  4. It took me months to figure out how to code this simply :)

Other Sources (while read loop)

4 Comments

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, 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).
This doesn't work for me. $databaseName just contains the whole list, thus does only a single iteration.
@AlikElzin-kilaka My answer below solves this problem so that the loop is run for every line of the string.
67

if you need the indices of the elements while you're looping through the array:

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++ ))).

(${!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.)

2 Comments

This really should be the chosen answer. 1: It's simple and easy to read. 2: It handles whitespace correctly, no IFS nonsense is getting in the way. 3: It handles sparse arrays correctly.
@tckmn: is the exclamation mark (used twice) necessary? Seems to be wrong...
56

You can use the syntax of ${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

Comments

38
arr_variable=("kofi" "kwame" "Ama")

## now loop through above array
for i in "${arr_variable[@]}"
do
   echo "$i"

done
   

You can iterate through bash array values using a counter with three-expression (C style) to read all values and indexes for loops syntax:

declare -a kofi=("kofi" "kwame" "Ama")
 
# get the length of the array
length=${#kofi[@]}

for (( j=0; j<${length}; j++ ));
do
  print (f "Current index %d with value %s\n" $j "${kofi[$j]}")
done

1 Comment

a bash array can have holes in it; the correct way to loop through it by index (that also works on associative arrays) is: for i in "${!arr_variable[@]}"; do printf '%d: %s\n' "$i" "${arr_variable[i]}"; done
28

This is also easy to read:

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

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

1 Comment

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

Two little tricks.. and more!

( Have a loog at new chapter 3 2025-06-06 !)

1. Implicit array for script or functions:

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 :

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.

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!

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

Then into a script:

#!/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 arg1 arg2 arg3 ...
Doing something with 'arg1'.
Doing something with 'arg2'.
Doing something with 'arg3'.
Doing something with '...'.

Same in a function:

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

Then

myfunc item1 tiem2 time\ 3
Working about 'item1'.
Working about 'tiem2'.
Working about 'time 3'.

2. Using List of array keys

man bash | sed 'H;/^$/{x;s/^\n//;s/\n$//;/List of array keys/p;};d'
  ${!name[@]}
  ${!name[*]}
         List of array keys.  If name is an array  variable,  expands  to
         the  list  of array indices (keys) assigned in name.  If name is
         not an array, expands to 0 if name is set  and  null  otherwise.
         When  @  is used and the expansion appears within double quotes,
         each key expands to a separate word.
declare -a arr=("element 1" "element 2" "element 3")

for i in "${!arr[@]}";do
    printf 'Field id:%2d is "%s".\n' "$i" "${arr[i]}"
    # Doing something with "${arr[i]}"
done
Field id: 0 is "element 1".
Field id: 1 is "element 2".
Field id: 2 is "element 3".

2.1 Use case sample

Index of array are commonly beginning by 0, but you could assign array element by number directly. For sample, using birthday as index:

#!/bin/bash

exec {people}<<eoPeopleList
    Alice        20020401
    Bob          20011109
    Charles      20050728
    Zoe          20030401
eoPeopleList
printf -v thisYear '%(%Y)T' -1
while read -ru $people name birthdate; do
    birthdays[10#${birthdate:4}]+="$name ($(( thisYear - ${birthdate::4} ))y), "
done
for day in "${!birthdays[@]}";do
    printf 'People who\47s birthday is %(%b)T %2d: %s\n' \
        $((${day::-2}*2462400)) $((10#${day: -2})) "${birthdays[day]%, }"
done

Then running this script (in 2024) will output:

People who's birthday is Apr  1: Alice (22y), Zoe (21y)
People who's birthday is Jul 28: Charles (19y)
People who's birthday is Nov  9: Bob (23y)

2.1b Using more arrays

As we are working aroung arrays, here's a modified version using locale for assigning month's names to an array variable

#!/bin/bash

exec {people}<<eoPeopleList
    Alice        20020401
    Bob          20011109
    Charles      20050728
    Zoe          20030401
eoPeopleList
thisYear=$(date +%Y)
while read -ru $people name birthdate; do
    birthdays[10#${birthdate:4}]+="$name ($(( thisYear - ${birthdate::4} ))y), "
done
exec {people}<&-

mapfile -d\; -t months <<<";$(locale abmon)"

for day in "${!birthdays[@]}";do
    printf 'People who\47s birthday is %s %2d: %s\n' \
        ${months[${day::-2}]} $((10#${day: -2})) "${birthdays[day]%, }"
done

For strictly same output.

People who's birthday is Apr  1: Alice (22y), Zoe (21y)
People who's birthday is Jul 28: Charles (19y)
People who's birthday is Nov  9: Bob (23y)

3+ Avoid bash loop when you can

Here's a little sample: this script will recursively find all .png files in current directory, then list them with creation date, file size and picture dimensions, allong file names.

  • run find to create an array
  • run stat on each elements to map files creation times
  • run stat again to map files sizes
  • run file on each elements to map pictures sizes
  • print out all collected values
#!/bin/bash
#
# shellcheck disable=SC2059  # Don't use variables in the printf format string

# Collect all png file paths
mapfile -d '' -t allPng < <(
    find . ! -path ./.cache\* -type f -name '*.png' -print0
)

# Collect all file dates
mapfile -t values < <(stat -c '%Y' "${allPng[@]}")
# Prepare *format string*, while formating dates 
printf -v tempString '%(%a %d %b %Y %T)T  %%8s %%%%11s %%%%%%%%s\n' \
        "${values[@]}"

# Collect all file sizes
mapfile -t values < <(
    stat -c '%s' "${allPng[@]}" |
        numfmt --to=iec
)
printf -v tempString "$tempString" "${values[@]}"

# Collect all picture dimensions
mapfile -t values < <(
    file -b "${allPng[@]}" |
        sed 's/.*, \([0-9]\+\) x \([0-9]\+\), .*/\1x\2/;
                /[^0-9x]/s/.*/???/'
)
printf -v tempString "$tempString" "${values[@]}"

# Printout everything with file names
printf "$tempString" "${allPng[@]}"

If find could take some time to browse your entire directory, rest of script will be executed very quickly, as there are no loop.

Output could look like:

...
Tue 28 Nov 2023 12:04:07       14K     128x128 ./firefox/browser/chrome/icons/default/default128.png
Tue 28 Nov 2023 12:04:07      2.0K       32x32 ./firefox/browser/chrome/icons/default/default32.png
Tue 28 Nov 2023 12:04:07      3.4K       48x48 ./firefox/browser/chrome/icons/default/default48.png
Tue 28 Nov 2023 12:04:07       722       16x16 ./firefox/browser/chrome/icons/default/default16.png
Tue 28 Nov 2023 12:04:07      5.4K       64x64 ./firefox/browser/chrome/icons/default/default64.png
...

Comments

14

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}

Comments

13
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

Comments

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

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

2 Comments

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 *
...before calling something "tested" in shell, be sure to check the corner cases, which whitespace and globs are both among.
7

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

3 Comments

try the code highlighter in the editor to make your code look good.
Good to know, but this question is about bash.
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"
6

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"

Comments

5

How you loop through an array, depends on the presence of new line characters. With new line characters separating the array elements, the array can be referred to as "$array", otherwise it should be referred to as "${array[@]}". The following script will make it clear:

#!/bin/bash

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

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

Comments

4

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.

2 Comments

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 `('
@BernieReiter make sure you're using $ ./myscript.sh instead of $ sh myscript.sh and of course chmod +x myscript.sh
4

What I really needed for this was something like this:

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

For instance:

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

(Would kill all processes with vlc in their name)

Comments

3

Possible first line of every Bash script/session:

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

Use e.g.:

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

May consider: echo interprets -e as option here

Comments

3

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

Comments

2

I loop through an array of my projects for a git pull update:

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

2 Comments

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!
you should explain IFS variable behavior in your script
0

Below code declares an array of locales and then loop over them to fetch content from sitecore using curl command and write them in relevant file.

declare -a SUPPORTED_LOCALES=("en" "fr" "de" "nl" "it" "es" "pt")

for locale in "${SUPPORTED_LOCALES[@]}";
do
  curl -s "http://sitecoreurl.com?sc_lang=$locale" > assets/sitecore/indexpage_$locale.json
done

Tip As common in many languages, in bash the array declaration dont need comma as a separator. So if you happen to define it like

declare -a SUPPORTED_LOCALES=("en" ,"fr", "de", "nl", "it", "es", "pt")

This will create array of only one element which is "en" ,"fr", "de", "nl", "it", "es", "pt" This was weird for me, but took a while to understand whats happening.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.