When a shell task must touch a group of files, the risky part is often the match set rather than the loop syntax. A broad glob, an unmatched pattern, or an unquoted path variable can make a Bash script skip names with spaces, process literal text, or touch files outside the intended group.
Bash expands the glob in the for loop header before the loop body starts. Each matching path is assigned to the loop variable in turn, and quoting that variable keeps a filename with spaces as one argument when the command inside the loop runs.
The sample script counts lines in logs/*.log while a text file in the same directory remains untouched. It enables nullglob before the loop so an empty match set is skipped; without that option, Bash leaves an unmatched pattern such as missing/*.log as literal text for the loop to process.
Related: How to create and run a Bash script
Related: How to use command substitution in Bash
Related: How to use glob options in Bash
$ mkdir -p logs $ printf 'one\ntwo\n' > logs/app.log $ printf 'one\n' > logs/db.log $ printf 'one\ntwo\n' > 'logs/web access.log' $ printf 'skip\n' > logs/readme.txt
#!/usr/bin/env bash set -euo pipefail shopt -s nullglob for logfile in logs/*.log; do lines=$(wc -l < "$logfile") printf "%s:%s\n" "$logfile" "$lines" done
Set nullglob before the loop that uses the pattern. The path variable is quoted in both commands so a log file with spaces in its name is still handled as one argument.
$ bash -n count-logs.sh
No output means Bash did not find a parse error.
$ bash count-logs.sh logs/app.log:2 logs/db.log:1 logs/web access.log:2
$ mkdir -p archive $ mv logs/*.log archive/
$ bash count-logs.sh
No output means nullglob removed the unmatched pattern before the loop started.
$ rm -rf archive count-logs.sh logs