How to check if a file exists in Bash
Check if a file exists in Bash with [ -f "$file" ], test for directories with -d, and check readability/writability with -r and -w. Copy-paste examples.
Tldr
Use `[ -e "$file" ]` to check that a path exists at all, `[ -f "$file" ]` to require it be a regular file, and `[ -d "$file" ]` to require a directory. Always quote the variable to handle spaces and empty values safely.
Intro
Bash provides a family of file test operators for existence, type, and permission checks. Picking the right one avoids both false positives (matching a directory when you wanted a file) and quoting bugs.
Steps
Name
Check that any path exists
Text
-e is true for any existing filesystem entry: regular file, directory, symlink, device, etc.
Name
Check for a regular file specifically
Text
-f is true only for regular files, not directories or special files. This is the most common check before reading a file.
Name
Check for a directory
Text
-d is true for directories. Combine with -e checks to give clearer error messages.
Name
Check readability and writability
Text
-r and -w check the effective permissions for the current user, which is more reliable than parsing ls -l output.
Name
Handle symlinks correctly
Text
-e follows symlinks and reports false for a broken (dangling) symlink. Use -L to detect that the path is a symlink regardless of whether the target exists.
Faq
Q
What is the difference between -e and -f?
A
-e is true for anything that exists at that path (file, directory, device, symlink). -f narrows that to regular files only, so it is what you want before opening a file for reading.
Q
Why does [ -f "$file" ] fail even though ls shows the file?
A
Common causes are an unquoted variable containing spaces splitting into multiple arguments, a trailing newline from command substitution, or the variable actually holding a relative path resolved from a different working directory. Quote the variable and print it with `declare -p file` to check.
Q
Can I check multiple files in one condition?
A
Yes: `if [ -f "$a" ] && [ -f "$b" ]; then ...` or use [[ ]] with && the same way. Avoid `-a` inside a single [ ] test — it is deprecated and ambiguous.