hit counter

How to check if a file exists in Linux bash scripts

Linux laptop with a bash prompt
fatmawati ahmad zaenuri/Shutterstock.com

When a Linux bash script relies on certain files or directories being present, it cannot simply assume that they are. It must be checked whether they are definitely present. Here’s how.

Don’t assume anything

When you write a script, you can’t make assumptions about what’s on a computer and what’s not. This is doubly true if the script is to be distributed and run on many different computers. Sooner or later, the script will run on a computer that doesn’t match your assumptions, and the script will fail or run unpredictably.

Everything we create or cherish on a computer is stored in a file of a specific format, and all of those files reside in one directory. Scripts can read, write, rename, delete, and move files and directories—all things you can do on the command line.

The advantage you have as a human is that you can see the contents of a directory and know whether a file exists or not – or whether the expected directory exists at all. If a script fails while manipulating files, the consequences can be serious and harmful.

Bash provides a comprehensive set of tests that you can use to detect files and directories and test for many of their attributes. Integrating these into scripts is easy, but the benefits in terms of robustness and fine-tuning are significant.

TIED TOGETHER: How to use double bracket conditional tests in Linux

The range of tests

By combining the if statement with the appropriate test from a large collection of file and directory tests, we can easily determine if a file exists, is executable or writable, and more.

  • -b: Returns true if the file is a special block file.
  • -c: Returns true if the file is character specific.
  • -i.e: Returns true if the “file” is a directory.
  • -e: Returns true if the file exists.
  • -f: Returns true if the file exists and is a regular file.
  • -G: Returns true if the file has that setgid permission set (chmod g+).
  • -H: Returns true if the file is a symbolic link.
  • -L: Returns true if the file is a symbolic link.
  • -k: Returns true if its sticky bit is set (chmod +t).
  • – p: Returns true if the file is a named pipe.
  • -r: Returns true if the file is readable.
  • -s: Returns true if the files exist and is not empty.
  • -S: Returns true if the file is a socket.
  • -t: Returns true if the file descriptor is opened in a terminal.
  • -u: Returns true if the file has that setuid permission set (chmod u+).
  • -w: Returns true if the file is writable.
  • -x: Returns true if the file is executable.
  • -O: Returns true if owned by you.
  • -G: Returns true if owned by your group.
  • -N: Returns true if the file has changed since it was last read.
  • !: The logical NOT operator.
  • &&: The logical AND operator.
  • ||: The logical OR operator.

The list starts with -b because the -a test is obsolete and has been replaced by the -e Test.

TIED TOGETHER: How to Use SUID, SGID and Sticky Bits on Linux

Using the tests in scripts

The generic file test if statement is a simple script construct. The comparison inside the double brackets ” [[ ]] “ uses the -f test to see if a regular file with that name exists.

Copy the text of this script into an editor and save it in a file called “script1.sh” and use it chmod to make executable.

#!/bin/bash

if [[ -f $1 ]] 

then 

  echo "The file $1 exists." 

else 

  echo "The file $1 cannot be found." 

fi

You must pass the name of the file to the script on the command line.

chmod +x script1.sh

Make a script executable with chmod

You must do this with each script if you want to try the other examples from the article.

Let’s try the script on a simple text file.

./script1.sh test-file.txt

Running script1.sh on a regular file

The file exists and the script correctly reports that fact. If we delete the file and try again, the test should fail and the script should tell us.

./script1.sh test-file.txt

Running script1.sh on a file that doesn't exist

In a real-world situation, your script would need to take all reasonable actions. Maybe it flags the error and stops. Maybe it creates the file and continues. It can copy something from a backup directory to replace the missing file. It all depends on the purpose of the script. But at least the script is now able to make the decision based on knowing if the file exists or not.

That -f flag tests whether the file exists and is a “normal” file. In other words, it’s not something that looks like a file but isn’t, like . B. a device file.

We’ll use ls to check if the /dev/random file exists and then see what the script does with it.

ls -lh /dev/random
./script /dev/random

Running script1.sh on a device file

Since our script is testing for regular files and /dev/random is a device file, the test will fail. Very often, to find out if a file exists, you must carefully choose which test to use, or you must use multiple tests.

This is script2.sh, which tests for normal files and for character device files.

#!/bin/bash

if [[ -f $1 ]]
then
  echo "The file $1 exists."
else
  echo "The file $1 is missing or not a regular file."
fi

if [[ -c $1 ]]
then
  echo "The file $1 is a character device file."
else
  echo "The file $1 is missing or not a special file." 
fi

When we run this script on the device /dev/random file, the first test fails as expected and the second test succeeds. It recognizes the file as a device file.

./script2.sh /dev/random

Running script2.sh on a character device file

Actually, it recognizes it as a character device file. Some device files are block device files. As it stands, our script can’t handle it.

./script2.sh /dev/sda

Running scrip2.sh on a block device file

We can use logic OR operator and add another test in the second if statement. This time whether the file is a character device file or a block device file, the test returns true. This is script3.sh.

#!/bin/bash

if [[ -f $1 ]]
then
  echo "The file $1 exists."
else
  echo "The file $1 is missing or not a regular file."
fi

if [[ -c $1 || -b $1 ]]
then
  echo "The file $1 is a character or block device file."
else
  echo "The file $1 is missing or not a special file." 
fi

This script recognizes both character device and block device files.

./script3.sh /dev/random
./script3.sh /dev/sda

script3.sh correctly handles character and block device files

If it is important to you to differentiate between the different types of device files, you can use nested ones if Testify. This is script4.sh.

#!/bin/bash

if [[ -f $1 ]]
then
  echo "The file $1 exists."
else
  echo "The file $1 is missing or not a regular file."
fi

if [[ -c $1 ]]
then
  echo "The file $1 is a character device file."
else
  if [[ -b $1 ]]
  then
    echo "The file $1 is a block device file." 
  else
    echo "The file $1 is missing or not a device file."
  fi
fi

This script recognizes and categorizes both character device and block device files.

./script4.sh /dev/random
./script4.sh /dev/sda

script8.sh correctly identifies character and block device files

The logical AND operator allows us to test for multiple characteristics at once. This is script5.sh. It checks if a file exists and The script has been read and write permissions for it.

#!/bin/bash

if [[ -f $1 && -r $1 && -w $1 ]]
then
  echo "The file $1 exists and we have read/write permissions."
else
  echo "The file $1 is missing, not a regular file, or we can't read/write to it."
fi

We run the script on a file that we own and one that we own root.

./script5.sh .bashrc
./script5.sh /etc/fstab

script5.sh checks whether a file exists and whether read and write permissions are set

To test if a directory exists, use the -d Test. This is script6.sh. It is part of a backup script. First it checks whether the directory given on the command line exists or not. It uses the logical NOT operator ! in which if statement test.

#!/bin/bash

if [[ ! -d $1 ]]
then
  echo "Creating backup directory:" $1
  mkdir $1

  if [[ ! $? -eq 0 ]]
  then
    echo "Couldn't create backup directory:" $1
    exit
  fi
else
  echo "Backup directory exists."
fi

# continue with file backup
echo "Backing up to: "$1

If the directory does not exist, it will be created. When the directory build files are present, the script exits. If the directory creation is successful or the directory already exists, the script continues with its backup actions.

We run the script and then check with ls and the -d (directory) option whether the backup directory exists.

./script6.sh Documents/project-backup
ls -d Documents/project-backup

script6.sh detects if a directory exists

The backup directory has been created. If we run the script again, it should report that the directory already exists.

./script6.sh

script6.sh Reuse of an existing directory

The script finds the directory and proceeds to perform the backup.

Test, don’t accept

Sooner or later assumptions will lead to bad things happening. Test first and react accordingly.

Knowledge is power. Use tests to give your scripts the knowledge they need.

TIED TOGETHER: How to let Linux scripts know they are running in virtual machines

Leave a Comment