CrackOps
Interview PrepTracksLinuxAWS LabSoonInterview RoomSoon
Sign inStart free
CrackOps

Interview prep for the people who keep production up.

[email protected]
ProductInterview PrepTracksDashboardInterview Room
RolesDevOpsSREPlatformCloud
CompanyAboutContactPrivacyTerms
© CrackOps
Linux / Shell Scripting / bash: loops

bash: loops

Loops: for, while read, until and retry

Beginnerbashscriptingautomation

Loops repeat work: for walks over a list of words, files or numbers, while read processes a file or command output line by line, and until retries something until it succeeds. break and continue control the flow. Most automation (every host, every log, retry until healthy) is one of these three shapes.

Syntaxfor x in LIST; do ...; done while read -r line; do ...; done < FILE until CMD; do ...; done

Watch it run

Count errors in every rotated app log, generate a reload command per web server, and count attempts.

devops@devops-lab: ~/logs — zsh — 96×28
➜ ~/logs
Step 1 of 3
for f in app.log*; do printf '%-14s %s\n' "$f" "$(zgrep -c ERROR "$f")"; done

app.log* is expanded to the matching file names once, then the body runs for each. zgrep reads both plain and gzipped files, so one command covers the rotated set.

Anatomy of a command

$ ;
keywordwhile

Repeat the body as long as the condition command exits 0.

How it works

for x in LIST expands LIST once, before the loop starts: words are split and globs like *.log are matched against the directory, and the body then runs once per resulting word with x set to it. A glob that matches nothing stays as the literal pattern unless shopt -s nullglob is set. while and until are driven by a command's exit status, checked before every iteration: while repeats on 0, until repeats on non-zero. read is that command in the common while read pattern: it reads stdin up to a newline, splits the line on IFS into the named variables and returns 1 at end of file. Every part of a pipeline runs in its own subshell, so in cat file | while read ...; done the loop is a child process and any variable it changes vanishes when it ends. Redirecting into the loop with < file or < <(cmd) keeps it in the current shell. break leaves the innermost loop and continue skips to the next iteration.

Options you'll actually use

for x in a b cLoop over a list of words
for f in *.logLoop over matching files (names with spaces are safe)
for i in {1..10}Brace range (fixed numbers; use seq or C-style for variables)
for ((i=0; i<n; i++))C-style counter loop
while read -r lineProcess input line by line; -r keeps backslashes
IFS=, read -r a bSplit each line on commas instead of whitespace
until CMDRepeat until CMD succeeds (retries, waiting for a port)
break / continueLeave the loop / skip to the next iteration
done < fileFeed a file into the loop without a subshell
done < <(cmd)Feed command output into the loop without a subshell
shopt -s nullglobA glob that matches nothing expands to nothing

Common mistakes

for f in $(ls /var/log/app)

Command output is split on spaces and newlines and then globbed, so unusual names break apart.

for f in /var/log/app/*; do ... "$f" ...; done
cat hosts | while read h; do count=$((count+1)); done; echo $count

The loop runs in a subshell because it's in a pipeline; its variables are lost afterwards.

while read -r h; do ...; done < hosts
while read line; do ssh "$line" uptime; done < hosts

ssh reads stdin too and swallows the rest of the host list, so only the first host is processed.

while read -r h; do ssh -n "$h" uptime; done < hosts # -n: stdin from /dev/null
until curl -f "$url"; do sleep 2; done

No limit: if the service never comes up, the pipeline hangs until a global timeout kills it.

Count attempts and exit 1 after N tries, or wrap it: timeout 120 bash -c 'until ...; do sleep 2; done'
for i in {1..$n}

Brace expansion happens before variables are expanded, so it produces the literal {1..5}.

for ((i = 1; i <= n; i++)) # or: for i in $(seq 1 "$n")

Interview questions

Practice

Try these on a Linux machine of your own. Each task has a hint and a solution.

  1. 1

    Print the line count of every .log file in ~/logs, one per line, using a for loop.

  2. 2

    Read ~/data/servers.txt (name ip state) and print only the names of servers that are not running.

  3. 3

    Write an until loop that waits for the file /tmp/ready to exist, checking every second, and create the file from another terminal (or in the background).

  4. 4

    Print the numbers 1 to 10 but skip 5 and stop after 8.

Related commands

bash: ifConditions: if, elif, else, test brackets and casebash: arraysArrays: lists and key-value maps in bashxargsBuild and run commands from standard inputbash: functionsFunctions: name a block, pass arguments, return a status
Previousbash: ifNextbash: functions
Linux / Shell Scripting / bash: loops

bash: loops

Loops: for, while read, until and retry

Beginnerbashscriptingautomation

Loops repeat work: for walks over a list of words, files or numbers, while read processes a file or command output line by line, and until retries something until it succeeds. break and continue control the flow. Most automation (every host, every log, retry until healthy) is one of these three shapes.

Syntaxfor x in LIST; do ...; done while read -r line; do ...; done < FILE until CMD; do ...; done

Watch it run

Count errors in every rotated app log, generate a reload command per web server, and count attempts.

devops@devops-lab: ~/logs — zsh — 96×28
➜ ~/logs
Step 1 of 3
for f in app.log*; do printf '%-14s %s\n' "$f" "$(zgrep -c ERROR "$f")"; done

app.log* is expanded to the matching file names once, then the body runs for each. zgrep reads both plain and gzipped files, so one command covers the rotated set.

Anatomy of a command

$ ;
keywordwhile

Repeat the body as long as the condition command exits 0.

How it works

for x in LIST expands LIST once, before the loop starts: words are split and globs like *.log are matched against the directory, and the body then runs once per resulting word with x set to it. A glob that matches nothing stays as the literal pattern unless shopt -s nullglob is set. while and until are driven by a command's exit status, checked before every iteration: while repeats on 0, until repeats on non-zero. read is that command in the common while read pattern: it reads stdin up to a newline, splits the line on IFS into the named variables and returns 1 at end of file. Every part of a pipeline runs in its own subshell, so in cat file | while read ...; done the loop is a child process and any variable it changes vanishes when it ends. Redirecting into the loop with < file or < <(cmd) keeps it in the current shell. break leaves the innermost loop and continue skips to the next iteration.

Options you'll actually use

for x in a b cLoop over a list of words
for f in *.logLoop over matching files (names with spaces are safe)
for i in {1..10}Brace range (fixed numbers; use seq or C-style for variables)
for ((i=0; i<n; i++))C-style counter loop
while read -r lineProcess input line by line; -r keeps backslashes
IFS=, read -r a bSplit each line on commas instead of whitespace
until CMDRepeat until CMD succeeds (retries, waiting for a port)
break / continueLeave the loop / skip to the next iteration
done < fileFeed a file into the loop without a subshell
done < <(cmd)Feed command output into the loop without a subshell
shopt -s nullglobA glob that matches nothing expands to nothing

Common mistakes

for f in $(ls /var/log/app)

Command output is split on spaces and newlines and then globbed, so unusual names break apart.

for f in /var/log/app/*; do ... "$f" ...; done
cat hosts | while read h; do count=$((count+1)); done; echo $count

The loop runs in a subshell because it's in a pipeline; its variables are lost afterwards.

while read -r h; do ...; done < hosts
while read line; do ssh "$line" uptime; done < hosts

ssh reads stdin too and swallows the rest of the host list, so only the first host is processed.

while read -r h; do ssh -n "$h" uptime; done < hosts # -n: stdin from /dev/null
until curl -f "$url"; do sleep 2; done

No limit: if the service never comes up, the pipeline hangs until a global timeout kills it.

Count attempts and exit 1 after N tries, or wrap it: timeout 120 bash -c 'until ...; do sleep 2; done'
for i in {1..$n}

Brace expansion happens before variables are expanded, so it produces the literal {1..5}.

for ((i = 1; i <= n; i++)) # or: for i in $(seq 1 "$n")

Interview questions

Practice

Try these on a Linux machine of your own. Each task has a hint and a solution.

  1. 1

    Print the line count of every .log file in ~/logs, one per line, using a for loop.

  2. 2

    Read ~/data/servers.txt (name ip state) and print only the names of servers that are not running.

  3. 3

    Write an until loop that waits for the file /tmp/ready to exist, checking every second, and create the file from another terminal (or in the background).

  4. 4

    Print the numbers 1 to 10 but skip 5 and stop after 8.

Related commands

bash: ifConditions: if, elif, else, test brackets and casebash: arraysArrays: lists and key-value maps in bashxargsBuild and run commands from standard inputbash: functionsFunctions: name a block, pass arguments, return a status
Previousbash: ifNextbash: functions