bash file-io scripting

How do I tell if a file does not exist in Bash?


This checks if a file exists:


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

How do I only check if the file does not exist?


  • 220

    I’ve found this list of bash conditional statements very useful.

    Mar 12, 2009 at 14:52

  • 11

    Being the very lazy person that I am, I would typically have used the following silly workaround construct: if [ -f $FILE ]; then; else; echo "File $FILE does not exist."; fi; Probably good that I found this question instead and learned to do it in a more proper way. 🙂

    – Alderath

    Jan 15, 2013 at 13:35

  • 7

    To be pendantic, you should say “regular file”, as most UNIX/POSIX docs refer generically to all types of file system entries a simply “files”, e.g., a symbolic link is a type of a file, as is a named pipe, regular file, directory, block special, character special, socket, etc.

    – kevinarpe

    Nov 9, 2013 at 8:51

  • 11

    @kevinarpe if you want to test whether something exists, use -e. -f won’t pick up directories, symlinks, etc.

    – Benubird

    Mar 24, 2015 at 9:38

  • 15

    To be safe, always use double quotes to correctly handle file names with whitespace, e.g., FILE=$1 -> FILE="$1" and if [ -f $FILE ]; -> if [ -f "$FILE" ];

    – kevinarpe

    Mar 24, 2015 at 11:37


The test command (written as [ here) has a “not” logical operator, ! (exclamation mark):

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


  • 254

    More succinctly: [ ! -f /tmp/foo.txt ] && echo “File not found!”

    Sep 29, 2010 at 12:09

  • 41

    I struggled a bit to find the right syntax for “if any of 2 files does not exist”. The following both work: if [ ! \( -f "f1" -a -f "f2" \) ] ; then echo MISSING; fi if [ ! -f "f1" ] || [ ! -f "f2" ] ; then echo MISSING; fi

    – mivk

    Feb 2, 2012 at 15:41

  • 175

    @DavidWinterbottom Even more succulently: [ -f /tmp/foo.txt ] || echo "File not found!"

    – David W.

    Jun 26, 2013 at 21:08

  • 29

    Parameter can be any one of the following: -e: Returns true value, if file exists -f: Return true value, if file exists and regular file -r: Return true value, if file exists and is readable -w: Return true value, if file exists and is writable -x: Return true value, if file exists and is executable -d: Return true value, if exists and is a directory

    – SD.

    Dec 19, 2013 at 13:41

  • 11

    There is an asymmetry in using ! -f with && versus using -f with ||. This has to do with the exit code returned by the non/existence check. If you need your line to always exit cleanly with exit code 0 (and sometimes you don’t want this constraint), the two approaches are not interchangeable. Alternatively, just use an if statement and you no longer have to worry about the exit code of your non/existence check.

    – Asclepius

    Aug 1, 2015 at 2:16


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:

if [ -e "$file" ]; then
    echo "File exists"
    echo "File does not exist"

A test expression can be negated by using the ! operator

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


  • 1

    @0x90 If you want, you are free to edit my post and add it to the list. I guess you mean: -n String – Check if the length of the string isn’t zero. Or do you mean file1 -nt file2 – Check if file1 is newer then file 2 (you can also use -ot for older then)

    – BlueCacti

    May 5, 2014 at 10:59

  • 1

    About -n: The unary operator -z tests for a null string, while -n or no operator at all returns True if a string is not empty. ~

    – BlueCacti

    May 5, 2014 at 11:06

  • 1

    why didn’t some add a function like function exists() { ⏎ if [ -e “$1” ]; then echo “$1 exists” else echo “$1 does not exist” fi }

    – Mz A

    Apr 12, 2019 at 8:30

  • @MzA because we need to make the code readable, nobody knows what is $1. So assign $1 as file and then use that file variable to do something else looks more readable than using an unknown argument $1

    – MaXi32

    Nov 11, 2020 at 3:39

  • Regarding the -G operator, the two are slightly different, as I understand it. going by the “SETUID & SETGID BITS” section of the chmod man-page, the two would give different values in the case of root user, would they not. I am referring specifically to the “unless user has appropriate permissions” part. Regardless, excellent answer. Bookmarking for this alone.

    – Nate T

    Sep 18, 2021 at 10:53


Negate the expression inside test (for which [ is an alias) using !:


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

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

Alternatively (less commonly used) you can negate the result of test using:

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

That syntax is described in “man 1 bash” in sections “Pipelines” and “Compound Commands“.


  • 4

    In bash, [ is a builtin. So the relevant information is rather obtained by help [… but this shows that [ is a synonym for the test builtin, hence the relevant information is rather obtained by help test. See also the Bash Conditional Expression section in the manual.

    Jun 29, 2013 at 10:05

  • @gniourf_gniourf: Yes, but the bash built-in [ command behaves very similarly to the external [ command, so either man test or man [ will give you a good idea of how it works.

    Sep 21, 2013 at 19:04

  • 10

    @KeithThompson except that the bash builtin [ has more switches than the external command [ found on my system… Generally speaking I believe it’s better to read the documentation specific to a given tool, and not the documentation specific to another vaguely related one. I might be wrong, though ;)

    Sep 21, 2013 at 21:54