Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions examples/bash/04-nullglob-empty-match.bad.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env bash
#
# BAD: without nullglob, an unmatched pattern remains the literal string
# "*.txt", so the loop runs once for a file that does not exist.
# expect-shellcheck: none
set -euo pipefail

main() (
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT
cd "$tmp"
Comment thread
posidoni marked this conversation as resolved.

for file in *.txt; do
printf 'found: %s\n' "$file"
done
)

main "$@"
18 changes: 18 additions & 0 deletions examples/bash/04-nullglob-empty-match.good.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env bash
#
# GOOD: nullglob expands an unmatched pattern to zero words, so an empty
# directory produces no phantom filename.
set -euo pipefail

tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT

main() (
cd "$tmp"
Comment thread
posidoni marked this conversation as resolved.

shopt -s nullglob
files=(*.txt)
printf 'matched %s file(s)\n' "${#files[@]}"
)

main "$@"
1 change: 1 addition & 0 deletions examples/bash/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ safe to run and declares the ShellCheck code it triggers.
| `01-mapfile-not-subshell` | `mapfile -t` reads lines safely | `arr=($(...))` word-splits | `SC2207` |
| `02-array-not-scalar` | `"${arr[@]}"` expands all elements | bare `$arr` = first only | `SC2128` |
| `03-quote-args` | `"$@"` forwards args intact | unquoted `$@` re-splits | `SC2068` |
| `04-nullglob-empty-match` | `nullglob` makes an empty match produce zero words | an unmatched glob stays literal | none |
16 changes: 16 additions & 0 deletions reference/bash.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,22 @@ fi

See the runnable pairs in [`examples/bash/`](../examples/bash/).

## Globbing and empty matches

By default, a glob that matches nothing remains unchanged. In an empty
directory, `for file in *.txt` therefore runs once with the literal string
`*.txt`, even though no file exists.

- Enable `nullglob` when no matches should produce zero words: `shopt -s
nullglob`. This is useful for loops and arrays that should simply stay empty.
- Enable `failglob` when no matches should be an error instead. This is useful
when the missing input signals a broken assumption.

Both options are Bash-specific and affect subsequent expansions in the current
shell, so enable them deliberately and keep their scope narrow. See the Bash
manual's [Filename Expansion](https://www.gnu.org/software/bash/manual/bash.html#Filename-Expansion)
section and the runnable pair in [`examples/bash/`](../examples/bash/).

## Safe temp files

Create with `mktemp`, remove with an `EXIT` trap set immediately afterward. Keep
Expand Down
Loading