How to execute certain commands if a file does NOT exist?

if [ ! -e "$path" ]
then
    touch -- "$path"
fi

A simpler version would be to simply touch -- "$path" - it creates the file if it doesn't exist, and just updates the access and modification times if it does exist. The double dash (--) ensures that you can create the file even if it starts with a dash, and the quotes around $path are necessary.


Don't do that, it's not only subject to race conditions, but also the [ -e /path/file ] checks if you can do a stat(2) on a file, so it will return false for different reasons, not only because the file cannot exist.

An example is a symlink to a file that doesn't exist or a file in a directory you don't have search permission to.

A much better approach is to use the right flags to the open(2) system call here, that is O_CREAT|O_EXCL. That way, the open() fails if the file didn't already exist without you having to do the check several millions of CPU clock ticks earlier.

With a Bourne-like shell:

if (set -C && : > "$file") 2> /dev/null; then
  print '%s\n' "$file has been created
else
  print '%s\n' "It hasn't, possibly because it was already there"
fi

(set -C is to enable the O_EXCL flag).

Also, why would you want to create an empty file? Chances are you want to store something in that file. Then, just do it:

set -C
{
  echo blah
  other-commands that-generate-the-content
} > "$file"

Then, that command group is only executed if the file didn't exist already (and it was possible to create it).

If you want to test for file existence, write it at least:

[ -e "$file" ] || [ -L "$file" ]

or

ls -d -- "$file" > /dev/null 2>&1

if you care about it potentially being a symlink. That will still return false if the file does exist but you don't have the right to verify it.


Now, if you want a longer and historical answer about testing for file existence:

Initially, the test command (in Unix v7 where it first appeared) had no -e (nor -h/-L option or -a unary) option.

The way to test for file existence was with ls. ls (with -d) lists the file and reports an error (and returns a false exit status) if it can't look up the file for a reason or another. Unix initially didn't have symlinks, but when they were introduced, ls was modified to do a lstat(2) on a file instead of a stat(2). That is, in case of symlink ls returns information about the symlink file itself, not the file at the path the symlink points to.

An option to test (aka [) for testing for file "existence" was first introduced in the Korn shell test builtin. That was -a, not -e. -a is for accessible (I believe) which is a more accurate term than existing.

I don't know when or what introduced -e, possibly POSIX. POSIX says that -e was chosen over -a to avoid the possible confusion with the -a binary operator (for and).

In any case both -a and -e attempt a stat(2) on the file, not a lstat(2). That is:

[ -e "$file" ]

is equivalent to:

ls -Ld -- "$file" > /dev/null 2>&1

So, strictly speaking, it returns true if, at the time the test was done, it was possible to lookup the path after resolving the symlinks, and if the stat(2) fails, the reason for the failure is ignored.

stat may fail if the file doesn't exist (ENOENT), that is if the file doesn't exist or it exists but is a symlink to a file that doesn't exist, but also for plenty other reasons. Looking at the possible error codes of stat(2) gives a few ideas:

  • EACCESS: during resolution of the path (and that can be any path component and in the path of any symlink), you don't have search permission for one directory component (note that you may still have access to the file via another path).
  • ELOOP: impossible to resolve the path because of too many symlinks resolved to get there.
  • ENOTDIR. For instance on /etc/passwd/foo or a symlink to it.

Checking if a file exists is a very common task. To search for relevant questions, use the search bar (top right). I got lots of results with the terms "bash script file exists"

In any case, you want either the test builtin or its semantic equivalent [ ]:

[ ! -e your_file ] && >your_file

or

test ! -e your_file && >your_file

which will first test that your_file does not (!) exist (-e) and creates it if that's the case.

For more information on the different tests you can run (other than -e), you can type:

help -m test | less

You can use this:

if [ ! -e "$file" ]; then
       touch file
else
        ...
fi

Tags:

Shell

File