How to Check if a File Exists in Bash (With Code Examples)

Andrei Dumitrescu
Andrei Dumitrescu
hero image
Want a career in tech?

Take our career path quiz to find the best fit for you and get a personalized step-by-step roadmap 👇

Take The 3-Minute QuizTake The 3-Minute Quiz

Have you ever had a script crash right in the middle because it couldn't find a file?

It’s frustrating, isn't it?

In DevOps, this can lead to bigger issues, especially if your script relies on that missing file to perform critical tasks. That's why learning how to check if a file exists in Bash is such an essential skill.

The good news?

By adding one simple file check to your script, you can save hours of troubleshooting, so no more guesswork or wasted time tracking down why something broke. 

Which is why in this guide, I’ll break down how to check if a file exists in Bash, handle different file types, and make sure your scripts can gracefully deal with missing files.

So let’s get into it…

Sidenote: Want to dive deeper into Bash?

You'll learn Shell Scripting fundamentals, master the command line, and get the practice and experience you need to go from beginner to being able to get hired as a DevOps Engineer, SysAdmin, or Network Engineer!

With that out of the way, let's get into this guide...

How to check if a file exists using Bash conditionals

When you're writing a Bash script, one of the first things you'll want to know is if a file exists, as this can make or break the script, especially if the file is critical to its operation.

Thankfully, Bash gives you a simple way to check for a file with an if statement and the -e flag.

For example

#!/bin/bash
if [ -e /path/to/your/file ]; then
    echo "File exists."
else
    echo "File does not exist."
fi

So what's happening here?

Well, the -e flag checks whether the file or directory exists at the path you've specified, without distinguishing between the two. This makes it more general compared to -f (regular file) and -d (directory), which check for specific types.

If Bash finds the file, it runs the code in the then block, which in this case simply echoes that the file exists. If the file isn't found, Bash jumps to the else block and lets you know the file isn't there.

Handy right?

A quick note on [ ] vs [[ ]]

The examples above use the classic [ ] test syntax, which works everywhere, including in non-Bash shells. However, if you know you're writing a Bash script (not a portable POSIX shell script), it's worth using [[ ]] instead:

#!/bin/bash
if [[ -e /path/to/your/file ]]; then
    echo "File exists."
else
    echo "File does not exist."
fi

Why?

Well, simply because [[ ]] is a Bash keyword rather than a regular command, so it doesn't perform word-splitting or filename expansion on unquoted variables. That means it's more forgiving if you forget to quote a variable holding a file path, which is a very common source of bugs. More on this later, but back to checking if files exist. 

Let’s go ahead and make sure this works smoothly in all situations by using absolute paths in our scripts. I recommend doing this because relative paths can cause problems if the script runs from a different directory, and Bash may not find the file where you expect it to be. But by sticking to absolute paths like /home/user/file.txt, you ensure Bash always knows where to look.

You'll also want to keep an eye on permissions. A file might exist, but your script may not be able to read, write to, or execute it because of access issues. To avoid this, check the file's permissions using -r for readable, -w for writable, and -x for executable, and you won't run into those frustrating "permission denied" errors.

For example

#!/bin/bash
if [[ -r /path/to/your/file ]]; then
    echo "File is readable."
else
    echo "File is not readable."
fi

if [[ -w /path/to/your/file ]]; then
    echo "File is writable."
else
    echo "File is not writable."
fi

if [[ -x /path/to/your/file ]]; then
    echo "File is executable."
else
    echo "File is not executable."
fi

In this example, -r checks if the file is readable, -w checks if it's writable, and -x checks if it's executable. That last one is especially useful before your script tries to run another script or binary. There's nothing worse than a script that finds a file, confirms it exists, and then fails anyway because it can't execute it!

This basic check sets you up for more complex file handling, helping you build scripts that can reliably handle files and avoid unexpected problems.

How to handle different file types and conditions

Sometimes, just knowing that a file exists isn't enough, because you might also need to figure out what kind of file it is, and whether it actually has anything in it.

  • Is it a regular file you can edit?

  • A directory that holds other files?

  • Or maybe it's a symbolic link pointing somewhere else?

  • Does it actually contain data, or is it empty?

Depending on the type, your script might need to handle it differently.

Bash makes this easy with a few handy flags:

  • -e checks if the file exists, no matter what type

  • -f checks if it's a regular file (like a text or config file)

  • -d tells you if it's a directory

  • -L checks if it's a symbolic link

  • -s checks if the file exists and has a size greater than zero

That last one is easy to overlook, but it's important to know about, because a log file or a completed download can technically "exist" as an empty placeholder while still being useless to your script.

The good news is that -s catches that case:

#!/bin/bash
if [[ -s /path/to/your/logfile ]]; then
    echo "Log file exists and has content."
else
    echo "Log file is missing or empty."
fi

#!/bin/bash
target="/path/to/your/target"

if [[ -L "$target" ]]; then
    echo "This is a symbolic link."
elif [[ -d "$target" ]]; then
    echo "This is a directory."
elif [[ -f "$target" ]]; then
    echo "This is a regular file."
else
    echo "File type unknown or does not exist."
fi

So how does this work?

Well:

  • If the path points to a symbolic link, that's checked first, since a link could otherwise be misread as whatever it points to

  • If it's a directory, the elif block handles it

  • And if it's a regular file, you get the appropriate response

This flexibility allows your script to adapt based on what kind of file it's dealing with.

Why does this matter?

Well, imagine you're automating a task like backing up files.

If your script mistakenly treats a directory as a regular file, things could go wrong fast. But by checking file types ahead of time, you make sure the script behaves exactly as expected - whether it's copying files, managing directories, or handling symbolic links.

TL;DR

Adding these checks to your scripts helps prevent errors and keeps everything running smoothly, no matter what type of file you're working with.

What to do if a file doesn't exist

Now, obviously, there will be times when your script runs and doesn't find a file. So, what do we do in this situation?

Well, here's how you can handle it:

#!/bin/bash
if [[ ! -e /path/to/your/file ]]; then
    echo "File does not exist. Creating file..."
    touch /path/to/your/file
else
    echo "File exists."
fi

In this example, we're using the ! -e condition to check if the file doesn't exist. If it's missing, the script creates an empty file using the touch command to allow the process to keep going.

One thing worth flagging is that touch will fail if the parent directory doesn't exist, or if you don't have write permission there. So this works well when the file itself might be missing but its folder is fine. If there's a chance the whole directory path is missing too, you'll want to check that separately (or run mkdir -p first). 

Now this might be fine for some cases, like when you need to create logs or temporary files, but what if the missing file is critical for the rest of the process? Should the script just continue?

Well for critical files that the rest of the script depends on, it's often better to stop the process if the file isn't found. Continuing without it might cause errors down the line or even corrupt the entire process.

In such cases, you might want the script to either:

  • Log an error and stop execution

  • Send an alert so you can manually intervene

  • Or perform another action to handle the situation appropriately

For example

Let's say that you want to stop the script if a critical file isn't found:

#!/bin/bash
if [[ ! -e /path/to/your/critical-file ]]; then
    echo "Critical file not found. Exiting..."
    exit 1
else
    echo "File exists."
fi

In this case, the exit 1 command will stop the script if the critical file doesn't exist, preventing further execution. This approach is useful when missing the file would break the process or cause major issues later on.

Additionally, permissions are something you need to check. Even if a file is missing, your script might not have the right permissions to create or access it. This can be a bigger issue for critical files. You can add a check using the -w flag to ensure the directory is writable before trying to create the file.

TL;DR

By planning for both non-critical and critical files, your script can handle unexpected scenarios without causing failures, making file checks crucial when integrating them into larger automation processes.

Speaking of which…

How to use file checks in automation scripts

In tasks like automated backups, managing server configurations, or running health checks, file checks prevent critical errors caused by missing files.

For example

Here's how you can use a file check to automate a backup:

#!/bin/bash
backup_file="/path/to/backup/file"

if [[ -e "$backup_file" ]]; then
    echo "Backup file exists. Proceeding with backup..."
    # Add backup logic here, such as copying data to the backup file
else
    echo "Backup file not found. Creating a new backup file."
    touch "$backup_file"
    # Add logic to initialize the backup file, such as copying data or setting file permissions
fi

Here, the script first checks if the backup file is already in place. If it exists, the script moves forward with the backup process. You could, for example, start copying data to the file or updating it with the latest system information.

Notice that "$backup_file" is quoted. This is a habit worth building early because if a variable holding a path is ever empty, unset, or contains a space, an unquoted reference like -e $backup_file can do the wrong thing or throw an error, but quoting the variable protects you from that.

If the file doesn't exist, the script creates a new file and initializes it. After creating the file, you might want to copy initial data, set permissions, or run other initialization tasks to ensure the backup process can continue without any issues.

But file checks aren't just about backups. Imagine you're managing configuration files across multiple servers. Before rolling out updates, your script needs to confirm that those config files are present. If you skip this check, you could end up with broken configurations or inconsistent setups across your environment.

Another thing to watch for is race conditions. If multiple scripts or processes are running simultaneously, they might try to access or modify the same file at the same time, leading to unpredictable results. This is especially common in automation where two cron jobs kicking off at the same minute, or two instances of the same script triggered back to back.

One way to guard against this is with flock, which lets a script wait for (or fail if it can't get) exclusive access to a lock file before touching the real one:

#!/bin/bash
lockfile="/tmp/backup.lock"

(
    flock -n 200 || { echo "Another instance is already running. Exiting."; exit 1; }
    # Safe to work with the backup file here - no other instance holds the lock
    echo "Running backup..."
) 200>"$lockfile"

This wraps the risky section in a subshell that only proceeds once it holds the lock on backup.lock, so two overlapping runs of the same script can't step on each other.

By integrating these file checks into your automation scripts, you ensure that your processes stay reliable, flexible, and ready to handle anything thrown their way.

So what's next?

Checking if a file exists in Bash might seem like a small task, but it can prevent critical failures and keep your automation running smoothly. Whether it's managing backups or handling missing files, file checks ensure your scripts perform reliably and without interruption.

Now, it's time to put this into action. Add file checks to your Bash scripts and see firsthand how much more efficient and error-free your processes become. The more you practice, the more you'll refine your automation workflows.

Checking if a file exists in Bash might seem like a small task, but it can prevent critical failures and keep your automation running smoothly. Whether it’s managing backups or handling missing files, file checks ensure your scripts perform reliably and without interruption.

Now, it’s time to put this into action. Add file checks to your Bash scripts and see firsthand how much more efficient and error-free your processes become. The more you practice, the more you'll refine your automation workflows.

P.S.

Don’t forget, if you want to fast-track your Bash knowledge and get as much hands-on practice as possible, then check out my complete BASH scripting course:

You'll learn Shell Scripting fundamentals, master the command line, and get the practice and experience you need to go from beginner to being able to get hired as a DevOps Engineer, SysAdmin, or Network Engineer!

Plus, once you join, you'll have the opportunity to ask questions in our private Discord community from me, other students, and working DevOps professionals:

I guarantee that it's is the most comprehensive and up-to-date online resource to learn Bash Scripting. Plus, we'll give you the exact steps you need to take to get hired as a SysAdmin, DevOps Engineer, or Network Engineer no matter what level of experience you have!

Best articles. Best resources. Only for ZTM subscribers.

If you enjoyed this post and want to get more like it in the future, subscribe below. By joining the ZTM community of over 100,000 developers you’ll receive Web Developer Monthly (the fastest growing monthly newsletter for developers) and other exclusive ZTM posts, opportunities and offers.

No spam ever, unsubscribe anytime

Want more Bash content?

If you enjoyed this post, then check out my other Bash guides and tutorials!

You might like these courses

More from Zero To Mastery

How to Become a DevOps Engineer & Get Hired in 2026 preview
Popular
How to Become a DevOps Engineer & Get Hired in 2026
11 min read

Learn everything you need to know to become a DevOps Engineer in 2026 with this step-by-step guide!

How To Use Bash If Statements (With Code Examples) preview
How To Use Bash If Statements (With Code Examples)
16 min read

Looking to level up your Bash skills with logic? In this guide, I cover everything you need to know about if statements in Bash - with code examples!

Bash Interview Prep: 31 Essential Questions and Answers preview
Bash Interview Prep: 31 Essential Questions and Answers
28 min read

Ace your Bash interview! Explore 31 key questions with code examples that cover everything from basics to advanced topics. Get interview-ready now.