Skip to content

/commands/bash/check-if-file-exists

bash check if a file exists ([ -f file ], [ -e ], [ -d ])

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash)updated
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

bash
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

TestTrue when the path...
-e pathExists (file, directory, device, socket...)
-f pathExists and is a regular file
-d pathExists and is a directory
-s pathExists and has a size greater than zero
-r / -w / -x pathIs readable / writable / executable by the current user
-L path (or -h)Is a symbolic link (even a broken one)
a -nt b / a -ot bFile a is newer / older than b (modification time)
! testNegates the test

Short forms

bash
[ -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 [-f and fails with command not found; if [ -f "$file"] reports [: missing ']'.
  • Unquoted variables. If $file is 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 with too many arguments when several files match; loop over them, or use compgen -G "*.log" > /dev/null to 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.

Sources