This is part of the Semicolon&Sons Code Diary - consisting of lessons learned on the job. You're in the bash category.
Last Updated: 2024-11-23
Depending on what you wish to do, there are various ways to iterate in shell.
(All of these assume one iteration for each item in the list.)
# Notice there are no commas in the bash array
debuggers=(binding.pry binding.remote_pry debugger)
# 1. Notice that the `$` comes OUTSIDE the debugger variable
# 2. Be aware that `for debugger in debuggers` would fail because it is an
# array.
for debugger in "${debuggers[@]}"
do
echo "$debugger"
done
for number in 1 2 3 4 5; do
echo $number
done
numbers="1 2 3 4 5"
# If you are using `zsh`, this option MUST be set, otherwise only a single
# iteration will occur. This is because ZSH, unlike bash, does not split when
# expanding variables
setopt shwordsplit
# Note the necessity of NOT wrapping $numbers in quotes
for number in $numbers; do
echo $number
done
# Unset this option to restore normal behavior.
unsetopt shwordsplit
This would output:
1
2
3
4
5
By constrast, here is what happens without the shwordsplit
bits in zsh
1 2 3 4 5
And while we're here, if you want an uncomplicated way to put something loopable in a string variable, a good solution is to put newline characters into the string. For example
tables="products\nusers\norders"
for $table in $tables; do
echo $table
done
will print:
products
users
orders
cat Gemfile | while read -r line; do
echo "$line"
done
What NOT to do:
# This contains one entry for every WORD on each line - i.e. far too many
iterations.
for file in $(cat Gemfile); do
check_for_debuggers "$file"
done
for i in {0..3}
do
echo "Number: $i"
done
This one preserves exit statuses by not running in subshells
changed_files() {
...
}
main() {
for file in $(changed_files); do
stop_if_debuggers "$file"
done
echo "I should not be executed"
exit 0
}
By contrast, if we did the following, then the while loop is in a subshell, therefore the overall program will not respect exit codes from this subshell and halt as expected.
changed_files | while read -r file; do
stop_if_debuggers $file
done
I wanted to upload part of my local .env
file that was in the past buffer to
Heroku without manually invoking the CLI line heroku config:set X
once per
item.
More abstractly, I wanted to iterate based on lines within my paste buffer. How was this done?
while read line
do
heroku config:set $line --remote staging
done <<<'X_STRIPE_KEY=pk_test_51..
X_STRIPE_SECRET=sk_test_51H...
X_STRIPE_WEBHOOK_SIGNING_SECRET=whsec_9U...'
Notice:
while read line
system was used. This puts the variable in $line
.<<<'my string'
9U...'
) If, instead, it were on the next line, then I would get a blank
fourth entry when iterating.