How to Check if a File or Directory Exists in Bash

By 

Updated on

6 min read

Bash Check if a File Exists

When writing shell scripts, you may find yourself in a situation where you need to perform an action based on whether a file exists or not.

In Bash, you can use the test command to check whether a file exists and determine the type of the file.

The test command takes one of the following syntax forms:

txt
test EXPRESSION
[ EXPRESSION ]
[[ EXPRESSION ]]

If you want your script to be portable, prefer the [ form, which is available in POSIX shells. The [[ (double bracket) form is supported by Bash, Zsh, and Ksh, but it is not part of POSIX sh.

Check if File Exists

When checking if a file exists, the most commonly used FILE operators are -e and -f. The first one will check whether a file exists regardless of the type, while the second one will return true only if the FILE is a regular file (not a directory or a device).

To check whether a path exists regardless of its type (file, directory, socket, and so on), use -e:

sh
FILE=/etc/resolv.conf
if [ -e "$FILE" ]; then
    echo "$FILE exists."
fi

The most readable option when checking whether a file exists or not is to use the test command in combination with the if statement . Any of the snippets below will check whether the /etc/resolv.conf file exists:

sh
FILE=/etc/resolv.conf
if test -f "$FILE"; then
    echo "$FILE exists."
fi
sh
FILE=/etc/resolv.conf
if [ -f "$FILE" ]; then
    echo "$FILE exists."
fi
sh
FILE=/etc/resolv.conf
if [[ -f "$FILE" ]]; then
    echo "$FILE exists."
fi

If you want to perform a different action based on whether the file exists or not, use the if/then construct:

sh
FILE=/etc/resolv.conf
if [ -f "$FILE" ]; then
    echo "$FILE exists."
else
    echo "$FILE does not exist."
fi
Info
Always use double quotes to avoid issues when dealing with files containing whitespace in their names.

You can also use the test command without the if statement. The command after the && operator will only be executed if the exit status of the test command is true.

sh
FILE=/etc/resolv.conf
test -f "$FILE" && echo "$FILE exists."
sh
[ -f "$FILE" ] && echo "$FILE exists."
sh
[[ -f "$FILE" ]] && echo "$FILE exists."

If you want to run a series of commands after the && operator, enclose the commands in curly brackets separated by ; or &&:

sh
[ -f "$FILE" ] && { echo "$FILE exists."; cp "$FILE" /tmp/; }

For a quick one-liner that only runs when the file exists, use &&:

sh
[ -f "$FILE" ] && echo "$FILE exists."

If you need one action when the file exists and another when it does not, use an if statement instead:

sh
if [ -f "$FILE" ]; then
    echo "$FILE exists."
else
    echo "$FILE does not exist."
fi

Check if Directory Exists

The flag -d allows you to test whether a file is a directory or not.

For example, to check whether the /etc/docker directory exists, you would use:

sh
FILE=/etc/docker
if [ -d "$FILE" ]; then
    echo "$FILE is a directory."
fi
sh
[ -d "$FILE" ] && echo "$FILE is a directory."

To run different commands depending on whether the directory exists, use an if/else block:

sh
FILE=/etc/docker
if [ -d "$FILE" ]; then
    echo "$FILE is a directory."
else
    echo "$FILE does not exist or is not a directory."
fi

To check whether a directory does not exist, negate the expression with !:

sh
FILE=/etc/docker
if [ ! -d "$FILE" ]; then
    echo "$FILE does not exist."
fi

Check if File Does Not Exist

Like many other languages, the test expression can be negated using the ! (exclamation mark) logical not operator:

sh
FILE=/etc/docker
if [ ! -f "$FILE" ]; then
    echo "$FILE does not exist."
fi

The same check as a one-liner:

sh
[ ! -f "$FILE" ] && echo "$FILE does not exist."

Check if File Is Readable, Writable, or Executable

To test permissions, use -r, -w, and -x:

sh
FILE=/usr/local/bin/script.sh

if [ -r "$FILE" ]; then
    echo "$FILE is readable."
fi
sh
if [ -w "$FILE" ]; then
    echo "$FILE is writable."
fi
sh
if [ -x "$FILE" ]; then
    echo "$FILE is executable."
fi

Check if File Is Not Empty

Use -s to check for a file that exists and has nonzero size:

sh
FILE=/var/log/syslog

if [ -s "$FILE" ]; then
    echo "$FILE is not empty."
fi

Use -L to check for a symbolic link:

sh
FILE=/etc/localtime

if [ -L "$FILE" ]; then
    echo "$FILE is a symbolic link."
fi

Check if Multiple Files Exist

To test whether multiple files exist, combine separate checks with &&:

sh
if [ -f /etc/resolv.conf ] && [ -f /etc/hosts ]; then
    echo "Both files exist."
fi
sh
if [[ -f /etc/resolv.conf && -f /etc/hosts ]]; then
    echo "Both files exist."
fi

Equivalent variants without using the IF statement:

sh
[ -f /etc/resolv.conf ] && [ -f /etc/hosts ] && echo "Both files exist."
sh
[[ -f /etc/resolv.conf && -f /etc/hosts ]] && echo "Both files exist."

File Test Operators

The test command includes the following FILE operators that allow you to test for particular types of files:

  • -b FILE - True if the FILE exists and is a special block file.
  • -c FILE - True if the FILE exists and is a special character file.
  • -d FILE - True if the FILE exists and is a directory.
  • -e FILE - True if the FILE exists and is a file, regardless of type (node, directory, socket, etc.).
  • -f FILE - True if the FILE exists and is a regular file (not a directory or device).
  • -G FILE - True if the FILE exists and has the same group as the user running the command.
  • -h FILE - True if the FILE exists and is a symbolic link.
  • -g FILE - True if the FILE exists and has set-group-id (sgid) flag set.
  • -k FILE - True if the FILE exists and has a sticky bit flag set.
  • -L FILE - True if the FILE exists and is a symbolic link.
  • -O FILE - True if the FILE exists and is owned by the user running the command.
  • -p FILE - True if the FILE exists and is a pipe.
  • -r FILE - True if the FILE exists and is readable.
  • -S FILE - True if the FILE exists and is a socket.
  • -s FILE - True if the FILE exists and has nonzero size.
  • -u FILE - True if the FILE exists and set-user-id (suid) flag is set.
  • -w FILE - True if the FILE exists and is writable.
  • -x FILE - True if the FILE exists and is executable.

Conclusion

For scripts that run across different shells, the single-bracket [ form is the portable choice. Double brackets [[ add useful extras like pattern matching and regex but work only in Bash, Ksh, and Zsh. For the full operator reference, run man test or help test (for the built-in version) in your terminal.

Tags

Linuxize Weekly Newsletter

A quick weekly roundup of new tutorials, news, and tips.

About the authors

Dejan Panovski

Dejan Panovski

Dejan Panovski is the founder of Linuxize, an RHCSA-certified Linux system administrator and DevOps engineer based in Skopje, Macedonia. Author of 800+ Linux tutorials with 20+ years of experience turning complex Linux tasks into clear, reliable guides.

View author page