How to Check if a File Exists in Bash

Let's say you're creating a Bash script to save some data in a file. It's important to ensure that the file exists before you try to write to it. If not, your script may not work as expected. This is just one example. In your Bash scripting journey, you’ll likely come across many scenarios where you must verify the existence of a file before performing any operation on it.

In this blog post, we'll explore four different methods to check if a file exists in Bash. Let’s get started!

Prerequisites

To try out the scripts in this blog post, you need access to a Bash shell. You also need a text editor, such as "nano" or "vim", which come pre-installed by default in many Unix-like operating systems.

For the purpose of this blog post, I'll be using KodeKloud’s Ubuntu playground, which lets you access a pre-installed Ubuntu operating system in just one click. Best of all, you won't need to go through the hassle of installing any additional software— everything you need is already set up and ready to use.

Check if a File Exists in Bash Using the test Command

Before we learn how to use the test command, we need to do two things:

  1. Create the file we want to check the existence of.
  2. Create a script file

Run the following command to create a file named demo.txt (inside any directory you prefer):

touch /root/demo.txt

Now, let’s identify the full path of this file, which we'll need for our scripts in the upcoming sections. Run the following command:

realpath demo.txt

You’ll see the file path printed on your terminal, as shown below:

Next, let’s create a script file. Run the following command to create a file named file-checker.sh in the /usr/local/bin directory and open it in the "nano" text editor:

nano /usr/local/bin/file-checker.sh

Note: While you're free to create the file-checker.sh file in any directory of your choice, we're placing it in the /usr/local/bin directory for a specific reason. In most Linux distributions, this directory is included in the system's command path. This means we can run our script without making it executable.

After running the above script, you should see the "nano" text editor in your terminal, as shown below:

Next, add the following script in the editor:

#!/bin/bash

# Define the path to the target file that we're going to check
TARGET_FILE="/root/demo.txt"

# Use an if-else statement to check if the file exists.
if test -f "$TARGET_FILE"
then
    echo "$TARGET_FILE exists."
else
    echo "$TARGET_FILE does not exist."
fi

Before we run this file, let's take a look at the script we've written:

First, we define a variable named TARGET_FILE. In this case, TARGET_FILE is a string containing the path to the file we want to check.

Next, we have an if...then...else conditional statement. The purpose of this statement is to test a certain condition and perform different actions depending on whether the condition is true or false.

The condition we're testing in this script is test -f "$TARGET_FILE". This uses the built-in test command to check if the file specified by TARGET_FILE exists. The -f flag checks if "$TARGET_FILE" is a regular file.

Note: Regular files are different from other types of files that exist in Unix-like systems. For instance, a directory is technically a type of file, but it's not a regular file — it's a special file that contains a list of other files. Similarly, there are other special types of files, like symbolic links, sockets, and device files, which have specific functions in the operating system. So when we say the -f flag checks if the file is a "regular file", we mean it checks if the file is a typical data file, not a directory or some other type of special file.

test is a command that evaluates the condition given to it and returns a status. If it finds that the file exists, it returns a status of 0. The if statement sees this "success" status and executes the code in the then section, which is echo "$TARGET_FILE exists.". This prints a message saying that the file exists.

If the test command does not find the file, it returns a non-zero status. The if statement sees this "failure" status and skips to the else section, executing the code there, which is echo "$TARGET_FILE does not exist.". This prints a message saying that the file does not exist.

Now you understand what the script does and the purpose of the test command. Next, press ctrl + o to save your work. You'll see a message asking for the File Name to Write. Just press "enter" to agree with the name that's already there. After your file is saved, press ctrl + x to exit the "nano" editor.

Then run the following command:

bash  file-checker.sh

After running this command, the text /root/demo.txt exists. should be displayed on your terminal as shown below:

Check if a File Exists in Bash Using the [ command

The single bracket [ is a built-in shell command used to evaluate conditional expressions. It is a synonym for the test command. In other words, you could use test expression or [ expression ] interchangeably in your shell scripts. For example, test -f "$file" and [ -f "$file" ] do the same thing: they both check if "$file" exists and is a regular file.

Note: The test command, along with its equivalent [ syntax, is part of the Portable Operating System Interface (POSIX) standard. POSIX is a set of standards that maintain compatibility across various operating systems. Thus, any shell that adheres to the POSIX standard supports test and [ commands.

Now that you're familiar with the [ command, open the file file-checker.sh using the command nano /usr/local/bin/file-checker.sh. Once the file is opened, replace the existing script within it with the following:

#!/bin/bash

# Define the path to the target file that we're going to check.
TARGET_FILE="/root/demo.txt"

# Use an if-else statement to check if the file exists.
if [ -f "$TARGET_FILE" ]
then
    echo "$TARGET_FILE exists."
else
    echo "$TARGET_FILE does not exist."
fi

In the above script, all we have done is replaced test -f "$TARGET_FILE" with [ -f "$TARGET_FILE" ]. As discussed, these two forms serve the same purpose.

It's important to notice that there should be spaces before and after the brackets. Here’s why:

The [ is actually a command, much like any other command you would use in a terminal. The ] is an argument that signals the end of the [ command.

Like all commands, [ needs to be separated from its arguments by spaces. Without a space, the shell will consider [ together with its adjacent characters as a single word or command, and it will not be able to find and execute such a command.

For example, if you write [-f "$.TARGET_FILE"], the shell will interpret it as a single word and will look for a command named [-f "$TARGET_FILE"], which of course, does not exist.

The closing bracket ] is required to be separated by a space for similar reasons - to ensure it is recognized as a separate argument. If there were no space before the ], the shell would consider the last argument and ] as a single word, which would again lead to an error.

Once you've edited the script, you'll need to save your work and exit the "nano" editor. To save, press ctrl + o. You'll see a message asking for the File Name to Write. Just press "enter" to agree with the name that's already there. After your file is saved, you can leave "nano" by pressing ctrl + x.

Now, run the script with the following command:

bash file-checker.sh

You’ll see the text /root/demo.txt exists. displayed on your terminal as shown below:

Check if a File Exists in Bash Using the [[ Keyword

In Bash, [[ serves a purpose similar to [ (the "test" command), but it's not a traditional command or program. Instead, [[ is classified as a keyword in the shell's scripting language. Keywords in a scripting language are special words that are reserved for specific uses.

If you run the command type [[, you’ll see the text [[ is a shell keyword printed on your terminal, as shown below:

The [[ keyword in Bash is used to test conditions, like the [ command. However, because it's a keyword rather than a command, it has some differences and advantages. It has more flexibility and robustness than [, because it operates under special syntax rules.

Note: [[ is not part of the POSIX standard, and thus, it's not universally supported across all shell environments. It's specifically available in advanced shells like Bash, Zsh, and Korn shells.

Now that you understand what the [[ keyword is and how it works, open the file file-checker.sh using the command nano /usr/local/bin/file-checker.sh. Once the file is opened, replace the existing script within it with the following:

#!/bin/bash


# Define the path to the target file that we're going to check.
TARGET_FILE="/root/demo.txt"

# Use an if-else statement to check if the file exists.
if [[ -f "$TARGET_FILE" ]]
then
    echo "$TARGET_FILE exists."
else
    echo "$TARGET_FILE does not exist."
fi

In the above script, we have replaced the [ command with the [[ built-in keyword. The expression within the [[ ]] is evaluated, and based on that evaluation, a message is printed on the terminal indicating whether the file exists or not.

Now, run the script with the following command:

bash file-checker.sh

After running the command, you’ll see an output like this:

Check if a File Exists in Bash in One Line

In the preceding sections, we explored various approaches to check for the existence of a file using Bash scripts. We used the test command, the [ command, and the [[ keyword within if...then...else conditional statements. The scripts we wrote were concise, typically consisting of fewer than 10 lines.

It turns out that we can reduce our previous scripts to just a single line of code. Here's how you can do this:

First, let’s consider the test command:

#!/bin/bash

test -f /root/demo.txt && echo "File exists." || echo "File doesn’t exist."

Next, the [ command:

#!/bin/bash

[ -f /root/demo.txt ] && echo "File exists." || echo "File doesn’t exist."

Lastly, the [[ keyword:

#!/bin/bash

[[ -f /root/demo.txt ]] && echo "File exists." || echo "File doesn’t exist."

In each of these one-liner scripts, you'll notice we're using the && and || logical operators. These are known as the AND and OR operators, respectively.

The && operator will execute the command that follows it if and only if the previous command returns true. Conversely, the || operator will execute its following command if the previous command returns false. Therefore, if the file exists, the message File exists. is displayed. Otherwise, the message File doesn’t exist. is shown.

Now, replace the previous script inside the file-checker.sh file with these scripts and run them one by one. In each case, you will receive the message File exists., as shown below:

Note that these one-liners are useful for simple operations or commands where you only need to perform a single action based on a condition. They are concise and can be convenient for certain scenarios.

However, if you need to execute multiple operations on a file, the if...then...else construct is more appropriate. It allows you to define a code block with multiple commands, thus providing greater flexibility and allowing for more extensive scripting capabilities.

Conclusion

In this blog post, we explored various methods to check if a file exists in Bash: using the test command, the [ command, and the [[ keyword. We also discovered how these methods can be condensed into one-liners, allowing for concise and efficient scripts.

Use the method that best suits your requirements, whether it's a one-liner for simplicity or the if...then...else construct for more complex operations.

Looking to build a solid foundation in shell scripting? Check out this course from KodeKloud:

  • Shell Scripts for Beginners: In this course, you'll dive into the practical world of Linux shell scripting. Regardless of your programming experience, you'll master fundamental scripting concepts such as variables, loops, and control logic. Throughout the course, you'll get plenty of hands-on experience using our comprehensive labs. Not only that, you'll also receive immediate feedback on your scripts, which will help you improve and refine them.