bash loop for while - ghdrako/doc_snipets GitHub Wiki

WHILE loop

Repeats a block of code as long as a specified condition is true.

while [ condition ]; do
# Code to execute while the condition is true
done

Examples

while true
do
  curl -I -vk https://blaataap.com
  sleep 1
done
count=1
while [ $count -le 5 ]; do
  echo "Iteration $count"
  ((count++))
done

Using external commands

while ! ping -c1 google.com > /dev/null; do # Repeat until 'ping' succeeds
  echo "Waiting for internet connection..."
  sleep 5
done

User Input Validation

while true; do # Use 'break' to exit within the loop
  read -p "Enter a number greater than 10: " number
  if [ $number -gt 10 ]; then
    break
  fi
  echo "Invalid input, please try again."
done

Monitoring

while : # ':' is always true - use with care!
do
  disk_usage=$(df -h / | awk '{print $5}' | tail -n1 | cut -d '%' -f 1)
  if [ $disk_usage -gt 85 ]; then
    echo "Low disk space alert!" | mail -s "Disk Warning" [email protected]
  fi
  sleep 300 # Check every 5 minutes
done
while true; do
if ps -ef | grep -q "my_long_running_process.py"; then
echo "$(date) - Process is running"
else
echo "$(date) - Process has stopped! Sending alert..."
# ... Your notification actions here ...
fi
sleep 60 # Check every minute
done

Reading Files Line-by-Line

while read -r line; do
  echo "Processing line: $line"
done < input_file.txt

while read is extremaly slow is that the shell is required to make a system call for every byte. It cannot read a large buffer from the pipe, because the shell must not read more than one line from the input stream and therefore must compare each character against a newline. Use awk,sed,pert for processing large files

One liner while

$ while true; do foo ; done
$ while sleep 30; do mv *.prc arch; ls -l|wc -l; done
$ while :; do foo; sleep 2; done
$ while [ 1 ]; do foo; sleep 2; done
$ nohup bash -c "while true; do foo ; done

UNTIL loop

Until: The loop body runs while the condition remains false. Great for retry logic. Repeats a block of code until a specified condition becomes true.

count=1
until [ $count -gt 5 ]; do
echo "Iteration $count"
((count++))
done
attempts=0
until [ $attempts -ge 5 ]; do # 'until' is like 'while' but the logic is inverted
  rsync -avz /data/ remote_host:/backup/
  if [ $? -eq 0 ]; then
    echo "Backup successful!"
    break
  fi
  attempts=$(( attempts + 1 ))
  echo "Backup failed. Retrying. (Attempt $attempts of 5)"
  sleep 30
done

until [ ]; do foo; sleep 2; done
until ((0)); do foo; sleep 2; done

Note that in contrast to while, until would execute the commands inside the loop as long as the test condition has an exit status which is not zero.

FOR loop

for name [ [ in [ word ... ] ] ; ] do list ; done
       The  list of words following in is expanded, generating a list of items.  The variable name is set
       to each element of this list in turn, and list is executed each time.  If the in word is  omitted,
       the  for  command  executes  list  once  for each positional parameter that is set (see PARAMETERS
       below).  The return status is the exit status of the last command that executes.  If the expansion
       of  the  items  following  in  results  in an empty list, no commands are executed, and the return
       status is 0.

loop iterates over a list of items

for item in [LIST]
do
  [COMMANDS]
done
for element in Hydrogen Helium Lithium Beryllium
do
  echo "Element: $element"
done

OutputElement: Hydrogen
Element: Helium
Element: Lithium
Element: Beryllium
for num in 1 2 3 4 5; do
    echo "$num"
done
#list of values can include variables as well as literals:
for person in $ME $3 Pat ${RA[2]} Sue; do
    echo $person
done
# show file name and content
for fn in *; do printf "::::::\n$fn\n:::::\n"; cat "$fn"; done

Loop over a number range

seq

for i in $(seq 1 10)
# generating a sequence of floating-point style numbers
for VAL in $(seq 2.1 0.3 3.2); do
     echo $VAL
done
Output:
2.1
2.4
2.7
3.0

{START..END} - more efficient then seq

for i in {0..3}
do
  echo "Number: $i"
done

OutputNumber: 0
Number: 1
Number: 2
Number: 3

{START..END..INCREMENT}

for i in {0..20..5}
do
  echo "Number: $i"
done

OutputNumber: 0
Number: 5
Number: 10
Number: 15
Number: 20

When either of the first two terms start with a zero in a {start..end..inc} brace expansion it will force each output value to be the same width - using zeros to pad them on the left side in bash v4.0 or newer. So {098..100} will result in: 098 099 100 whereas {98..0100} will pad to four characters, resulting in: 0098 0099 0100.

# generate 5 filenames like log01.txt through log05.txt you could write:
for FN in log{01..5}.txt ; do
    # do something with the filenames here
    echo $FN
done

Loop over array elements

BOOKS=('In Search of Lost Time' 'Don Quixote' 'Ulysses' 'The Great Gatsby')

for book in "${BOOKS[@]}"; do
  echo "Book: $book"
done

OutputBook: In Search of Lost Time
Book: Don Quixote
Book: Ulysses
Book: The Great Gatsby

The construct: ${namelist[@]} is bash syntax for listing all the values of a bash array.

Loop over associative arrays - hash

The construct: ${hash[@]} works fine for the values of the key/value pairs. To loop over the keys (i.e., indices) of the hash, add an exclamation point. The construct ${!hash[@]} can be used, as shown in this code snippet:

# we want a hash (i.e., key/value pairs)
declare -A hash
# read in our data
while read key value; do
    hash[$key]="$value"
done
# show us what we've got, though they won't
# likely be in the same order as read
for key in "${!hash[@]}"; do
    echo "key $key ==> value ${hash[$key]}"
donealternate example:
# we want a hash (i.e., key/value pairs)
declare -A hash
# read in our data: word and # of occurrences
while read word count; do
    let hash[$word]+="$count"
done
# show us what we've got, though the order
# is based on the hash, i.e. we don't control it
for key in "${!hash[@]}";do
    echo "word $key count = ${hash[$key]}"
done

The C-style Bash for loop

for ((INITIALIZATION; TEST; STEP))
do
  [COMMANDS]
done
for ((i = 0 ; i <= 1000 ; i++)); do
  echo "Counter: $i"
done

OutputCounter: 0
Counter: 1
Counter: 2
...
Counter: 998
Counter: 999
Counter: 1000
for ((a=1; a <= 3; a++))
do
    echo $a
done
# => 1
# => 2
# => 3
# for loop will iterate over the parameters to the script. That is, it will use $1 then $2, then $3, and so on, as the values for value.
for ((;;)); do
    printf 'forever'
done
# for loop will iterate over the parameters passed to the function.
# Inside a function definition the parameters $1, $2, etc. are
function listem {
    for arg; do
        echo "arg to func: '$arg'"
    done
    echo "Inside func: \$0 is still: '$0'"

Loop over script parameters or function parameters

#for loop will iterate over the parameters to the script. That is, it will use $1 then $2, then $3,
echo "$value"
    # do more stuff with $value...
done
#Inside a function definition the parameters $1, $2, etc. are the parameters to the function
#for loop will iterate over the parameters
function listem {
    for arg; do
        echo "arg to func: '$arg'"
    done
    echo "Inside func: \$0 is still: '$0'"
}

Other

Renaming files with spaces in the filename

for file in *\ *; do
  mv "$file" "${file// /_}"
done

Changing file extension

for file in *.jpeg; do
    mv -- "$file" "${file%.jpeg}.jpg"
done
for arg in $(cat /some/file)
for pic in $(find . -name '*.jpg')
for val in $(find . -type d | LC_ALL=C sort)
# They can also be used to act on files..
# This will run the command `cat` on file1 and file2
for Variable in file1 file2
do
    cat "$Variable"
done
# ..or the output from a command
# This will `cat` the output from `ls`.
for Output in $(ls)
do
    cat "$Output"
done

Break Statement

Terminates the loop prematurely.

for i in {1..10}; do
if [ $i -eq 5 ]; then
break
fi
echo "Iteration $i"
done
for element in Hydrogen Helium Lithium Beryllium; do
  if [ "$element" == 'Lithium' ](/ghdrako/doc_snipets/wiki/-"$element"-==-'Lithium'-); then
    break
  fi
  echo "Element: $element"
done

echo 'All Done!'

Continue Statement

Skips the rest of the loop's code and moves to the next iteration.

for i in {1..5}; do
  if [ $i -eq 3 ]; then
    continue
  fi
  echo "Iteration $i"
done
for i in {1..5}; do
  if [ "$i" == '2' ](/ghdrako/doc_snipets/wiki/-"$i"-==-'2'-); then
    continue
  fi
  echo "Number: $i"
done