[bash] How do I tell if a regular file does not exist in Bash?

I've used the following script to see if a file exists:

#!/bin/bash

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

What's the correct syntax to use if I only want to check if the file does not exist?

#!/bin/bash

FILE=$1     
if [ $FILE does not exist ]; then
   echo "File $FILE does not exist."
fi

This question is related to bash file-io scripting

The answer is


It's worth mentioning that if you need to execute a single command you can abbreviate

if [ ! -f "$file" ]; then
    echo "$file"
fi

to

test -f "$file" || echo "$file"

or

[ -f "$file" ] || echo "$file"

The test thing may count too. It worked for me (based on Bash Shell: Check File Exists or Not):

test -e FILENAME && echo "File exists" || echo "File doesn't exist"

This shell script also works for finding a file in a directory:

echo "enter file"

read -r a

if [ -s /home/trainee02/"$a" ]
then
    echo "yes. file is there."
else
    echo "sorry. file is not there."
fi

[[ -f $FILE ]] || printf '%s does not exist!\n' "$FILE"

Also, it's possible that the file is a broken symbolic link, or a non-regular file, like e.g. a socket, device or fifo. For example, to add a check for broken symlinks:

if [[ ! -f $FILE ]]; then
    if [[ -L $FILE ]]; then
        printf '%s is a broken symlink!\n' "$FILE"
    else
        printf '%s does not exist!\n' "$FILE"
    fi
fi

You can negate an expression with "!":

#!/bin/bash
FILE=$1

if [ ! -f "$FILE" ]
then
    echo "File $FILE does not exist"
fi

The relevant man page is man test or, equivalently, man [ -- or help test or help [ for the built-in bash command.


To test file existence, the parameter can be any one of the following:

-e: Returns true if file exists (regular file, directory, or symlink)
-f: Returns true if file exists and is a regular file
-d: Returns true if file exists and is a directory
-h: Returns true if file exists and is a symlink

All the tests below apply to regular files, directories, and symlinks:

-r: Returns true if file exists and is readable
-w: Returns true if file exists and is writable
-x: Returns true if file exists and is executable
-s: Returns true if file exists and has a size > 0

Example script:

#!/bin/bash
FILE=$1

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

Bash File Testing

-b filename - Block special file
-c filename - Special character file
-d directoryname - Check for directory Existence
-e filename - Check for file existence, regardless of type (node, directory, socket, etc.)
-f filename - Check for regular file existence not a directory
-G filename - Check if file exists and is owned by effective group ID
-G filename set-group-id - True if file exists and is set-group-id
-k filename - Sticky bit
-L filename - Symbolic link
-O filename - True if file exists and is owned by the effective user id
-r filename - Check if file is a readable
-S filename - Check if file is socket
-s filename - Check if file is nonzero size
-u filename - Check if file set-user-id bit is set
-w filename - Check if file is writable
-x filename - Check if file is executable

How to use:

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

A test expression can be negated by using the ! operator

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

The simplest way

FILE=$1
[ ! -e "${FILE}" ] && echo "does not exist" || echo "exists"

There are three distinct ways to do this:

  1. Negate the exit status with bash (no other answer has said this):

    if ! [ -e "$file" ]; then
        echo "file does not exist"
    fi
    

    Or:

    ! [ -e "$file" ] && echo "file does not exist"
    
  2. Negate the test inside the test command [ (that is the way most answers before have presented):

    if [ ! -e "$file" ]; then
        echo "file does not exist"
    fi
    

    Or:

    [ ! -e "$file" ] && echo "file does not exist"
    
  3. Act on the result of the test being negative (|| instead of &&):

    Only:

    [ -e "$file" ] || echo "file does not exist"
    

    This looks silly (IMO), don't use it unless your code has to be portable to the Bourne shell (like the /bin/sh of Solaris 10 or earlier) that lacked the pipeline negation operator (!):

    if [ -e "$file" ]; then
        :
    else
        echo "file does not exist"
    fi
    

I prefer to do the following one-liner, in POSIX shell compatible format:

$ [ -f "/$DIR/$FILE" ] || echo "$FILE NOT FOUND"

$ [ -f "/$DIR/$FILE" ] && echo "$FILE FOUND"

For a couple of commands, like I would do in a script:

$  [ -f "/$DIR/$FILE" ] || { echo "$FILE NOT FOUND" ; exit 1 ;}

Once I started doing this, I rarely use the fully typed syntax anymore!!


You can also group multiple commands in the one liner

[ -f "filename" ] || ( echo test1 && echo test2 && echo test3 )

or

[ -f "filename" ] || { echo test1 && echo test2 && echo test3 ;}

If filename doesn't exit, the output will be

test1
test2
test3

Note: ( ... ) runs in a subshell, { ... ;} runs in the same shell. The curly bracket notation works in bash only.


You can do this:

[[ ! -f "$FILE" ]] && echo "File doesn't exist"

or

if [[ ! -f "$FILE" ]]; then
    echo "File doesn't exist"
fi

If you want to check for file and folder both, then use -e option instead of -f. -e returns true for regular files, directories, socket, character special files, block special files etc.


envfile=.env

if [ ! -f "$envfile" ]
then
    echo "$envfile does not exist"
    exit 1
fi

In

[ -f "$file" ]

the [ command does a stat() (not lstat()) system call on the path stored in $file and returns true if that system call succeeds and the type of the file as returned by stat() is "regular".

So if [ -f "$file" ] returns true, you can tell the file does exist and is a regular file or a symlink eventually resolving to a regular file (or at least it was at the time of the stat()).

However if it returns false (or if [ ! -f "$file" ] or ! [ -f "$file" ] return true), there are many different possibilities:

  • the file doesn't exist
  • the file exists but is not a regular file (could be a device, fifo, directory, socket...)
  • the file exists but you don't have search permission to the parent directory
  • the file exists but that path to access it is too long
  • the file is a symlink to a regular file, but you don't have search permission to some of the directories involved in the resolution of the symlink.
  • ... any other reason why the stat() system call may fail.

In short, it should be:

if [ -f "$file" ]; then
  printf '"%s" is a path to a regular file or symlink to regular file\n' "$file"
elif [ -e "$file" ]; then
  printf '"%s" exists but is not a regular file\n' "$file"
elif [ -L "$file" ]; then
  printf '"%s" exists, is a symlink but I cannot tell if it eventually resolves to an actual file, regular or not\n' "$file"
else
  printf 'I cannot tell if "%s" exists, let alone whether it is a regular file or not\n' "$file"
fi

To know for sure that the file doesn't exist, we'd need the stat() system call to return with an error code of ENOENT (ENOTDIR tells us one of the path components is not a directory is another case where we can tell the file doesn't exist by that path). Unfortunately the [ command doesn't let us know that. It will return false whether the error code is ENOENT, EACCESS (permission denied), ENAMETOOLONG or anything else.

The [ -e "$file" ] test can also be done with ls -Ld -- "$file" > /dev/null. In that case, ls will tell you why the stat() failed, though the information can't easily be used programmatically:

$ file=/var/spool/cron/crontabs/root
$ if [ ! -e "$file" ]; then echo does not exist; fi
does not exist
$ if ! ls -Ld -- "$file" > /dev/null; then echo stat failed; fi
ls: cannot access '/var/spool/cron/crontabs/root': Permission denied
stat failed

At least ls tells me it's not because the file doesn't exist that it fails. It's because it can't tell whether the file exists or not. The [ command just ignored the problem.

With the zsh shell, you can query the error code with the $ERRNO special variable after the failing [ command, and decode that number using the $errnos special array in the zsh/system module:

zmodload zsh/system
ERRNO=0
if [ ! -f "$file" ]; then
  err=$ERRNO
  case $errnos[err] in
    ("") echo exists, not a regular file;;
    (ENOENT|ENOTDIR)
       if [ -L "$file" ]; then
         echo broken link
       else
         echo does not exist
       fi;;
    (*) syserror -p "can't tell: " "$err"
  esac
fi

(beware the $errnos support was broken with some versions of zsh when built with recent versions of gcc).


This code also working .

#!/bin/bash
FILE=$1
if [ -f $FILE ]; then
 echo "File '$FILE' Exists"
else
 echo "The File '$FILE' Does Not Exist"
fi

If you want to use test instead of [], then you can use ! to get the negation:

if ! test "$FILE"; then
  echo "does not exist"
fi

To reverse a test, use "!". That is equivalent to the "not" logical operator in other languages. Try this:

if [ ! -f /tmp/foo.txt ];
then
    echo "File not found!"
fi

Or written in a slightly different way:

if [ ! -f /tmp/foo.txt ]
    then echo "File not found!"
fi

Or you could use:

if ! [ -f /tmp/foo.txt ]
    then echo "File not found!"
fi

Or, presing all together:

if ! [ -f /tmp/foo.txt ]; then echo "File not found!"; fi

Which may be written (using then "and" operator: &&) as:

[ ! -f /tmp/foo.txt ] && echo "File not found!"

Which looks shorter like this:

[ -f /tmp/foo.txt ] || echo "File not found!"

You should be careful about running test for an unquoted variable, because it might produce unexpected results:

$ [ -f ]
$ echo $?
0
$ [ -f "" ]
$ echo $?
1

The recommendation is usually to have the tested variable surrounded by double quotation marks:

#!/bin/sh
FILE=$1

if [ ! -f "$FILE" ]
then
   echo "File $FILE does not exist."
fi

sometimes it may be handy to use && and || operators.

Like in (if you have command "test"):

test -b $FILE && echo File not there!

or

test -b $FILE || echo File there!

Examples related to bash

Comparing a variable with a string python not working when redirecting from bash script Zipping a file in bash fails How do I prevent Conda from activating the base environment by default? Get first line of a shell command's output Fixing a systemd service 203/EXEC failure (no such file or directory) /bin/sh: apt-get: not found VSCode Change Default Terminal Run bash command on jenkins pipeline How to check if the docker engine and a docker container are running? How to switch Python versions in Terminal?

Examples related to file-io

Python, Pandas : write content of DataFrame into text File Saving response from Requests to file How to while loop until the end of a file in Python without checking for empty line? Getting "java.nio.file.AccessDeniedException" when trying to write to a folder How do I add a resources folder to my Java project in Eclipse Read and write a String from text file Python Pandas: How to read only first n rows of CSV files in? Open files in 'rt' and 'wt' modes How to write to a file without overwriting current contents? Write objects into file with Node.js

Examples related to scripting

What does `set -x` do? Creating an array from a text file in Bash Windows batch - concatenate multiple text files into one Raise error in a Bash script How do I assign a null value to a variable in PowerShell? Difference between ${} and $() in Bash Using a batch to copy from network drive to C: or D: drive Check if a string matches a regex in Bash script How to run a script at a certain time on Linux? How to make an "alias" for a long path?