/commands/bash/check-if-file-exists
bash check if a file exists ([ -f file ], [ -e ], [ -d ])
- tool
- Bash
- versions
- Bash 3.2+, POSIX sh
- os
- linux, macos, windows
if [ -f "$file" ]; then ...; fi checks whether $file exists and is a regular file. Use -d for a directory, -e for any kind of file system entry, and ! to negate ([ ! -f "$file" ]). Quote the variable and keep the spaces inside the brackets: [ is a command and ] its last argument.
The common tests
mkdir -p logs
echo "data" > config.ini
: > empty.txt
for path in config.ini empty.txt logs nope.txt "my file.txt"; do
printf '%-12s' "$path"
[ -e "$path" ] && printf ' exists' || printf ' missing'
[ -f "$path" ] && printf ' file'
[ -d "$path" ] && printf ' directory'
[ -s "$path" ] && printf ' non-empty'
[ -r "$path" ] && printf ' readable'
echo
done
if [ ! -f settings.json ]; then
echo "settings.json not found, using defaults"
fi
[[ -f config.ini && -d logs ]] && echo "config and logs present"Output
config.ini exists file non-empty readable empty.txt exists file readable logs exists directory readable nope.txt missing my file.txt missing settings.json not found, using defaults config and logs present
empty.txt exists but fails -s (size greater than zero), logs is a directory and not a file, and names with spaces work because "$path" is quoted.
Test operators
| Test | True when the path... |
|---|---|
| -e path | Exists (file, directory, device, socket...) |
| -f path | Exists and is a regular file |
| -d path | Exists and is a directory |
| -s path | Exists and has a size greater than zero |
| -r / -w / -x path | Is readable / writable / executable by the current user |
| -L path (or -h) | Is a symbolic link (even a broken one) |
| a -nt b / a -ot b | File a is newer / older than b (modification time) |
| ! test | Negates the test |
Short forms
[ -f "$file" ] || { echo "missing: $file" >&2; exit 1; } # stop the script if missing
[ -d "$dir" ] || mkdir -p "$dir" # create only if absent
test -f "$file" && source "$file" # test is the same as [ ]Pitfalls
- Missing spaces.
[-f "$file"]runs a command named[-fand fails with command not found;if [ -f "$file"]reports[: missing ']'. - Unquoted variables. If
$fileis empty,[ -f $file ]becomes[ -f ], which is true (a single non-empty argument). With spaces in the name it becomes several arguments and fails with too many arguments. Quote it, or use[[ ]]. - Race conditions. Checking and then using a file is not atomic; another process can delete it in between. For important work, just open the file and handle the error.
- Globs.
[ -f *.log ]fails withtoo many argumentswhen several files match; loop over them, or usecompgen -G "*.log" > /dev/nullto test whether any match.
Confirm it worked
Run [ -f path ]; echo $?: 0 means the test is true, 1 false, and 2 a syntax error in the test itself (see bash exit codes).
FAQ
What is the difference between [ ] and [[ ]]?
[ is the POSIX test command, so its arguments are split and globbed like any command: quote variables. [[ ]] is bash syntax that does no word splitting and adds pattern and regex matching; it does not exist in plain sh.
How do I check that a file does not exist?
Negate the test with !: if [ ! -f "$file" ]; then echo missing; fi. Note the spaces around ! and inside the brackets.
Does -f follow symbolic links?
Yes. -f, -d and -e test the target of a symlink, so a broken link fails all three. -L (or -h) tests whether the path itself is a symbolic link.