I want to write a script that loops through 15 strings (array possibly?) Is that possible?
for databaseName in listOfNames
then
# Do something
end
I want to write a script that loops through 15 strings (array possibly?) Is that possible?
for databaseName in listOfNames
then
# Do something
end
## 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"
)
"${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.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.
for year in $(seq 2000 2013).DATABASES="a b c d e f".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++)); doNone 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
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.$i won't contain a glob, because it's the loop counter in this example and so you control its value.for i in "${!array[@]}";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
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.
"${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.List=( "first item" "second item" ) will be broken into first, item, second, item as well).ls output, in contravention of best practices.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" 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
IFS='\n', or for MacOS IFS='\r')#!/bin/bash at the top of the script file indicates the execution environment.Other Sources (while read loop)
IFS. (For everyone, IFS lets one specify a specific delimiter, which allows other whitespace to be included in strings without being separated into substrings).$databaseName just contains the whole list, thus does only a single iteration.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.)
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
for i in "${!arr_variable[@]}"; do printf '%d: %s\n' "$i" "${arr_variable[i]}"; doneThis is also easy to read:
FilePath=(
"/tmp/path1/" #FilePath[0]
"/tmp/path2/" #FilePath[1]
)
#Loop
for Path in "${FilePath[@]}"
do
echo "$Path"
done
IFS=$'\n' This may work for other solutions too in this scenario.( Have a loog at new chapter 3 2025-06-06 !)
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.
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
#!/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 '...'.
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'.
List of array keysman 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".
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)
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)
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.
find to create an arraystat on each elements to map files creation timesstat again to map files sizesfile on each elements to map pictures sizes#!/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
...
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}
for k in "${array[@]}"
do
echo $k
done
# For accessing with the echo command: echo ${array[0]}, ${array[1]}
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 *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
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"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
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.
$ 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 `('$ ./myscript.sh instead of $ sh myscript.sh and of course chmod +x myscript.shI 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
IFS variable behavior in your scriptBelow 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.