How to execute certain commands if a file does NOT exist?
I would like to check if a file exists, then if it does not, create it. Consider an equivalent of the following C code but with shell scripting.
Thank you, mate! Well, to be honest I thought this would be the correct part of Stack Exchange to answer this kind of questions.
It is the right place. Your question is on topic here, @Patrick shell scripting is explicitly on topic. SO shell scripting questions are very easily lost in the noise, you can ask them wherever you feel more comfortable.
@Patrick — I kind of take offense to your comment. I doubt your meant any but shell scripts are absolutely on topic here and I would suspect that you’ll get a much more thorough answer here on shell script questions then anywhere else. I see the dribble from time to time of SO Q&A’s that are shell scripting related and often roll my eyes at answers lacking real details, and mostly guesses or methods that ppl don’t truly understand.
4 Answers 4
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 [ ] :
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:
Note that [ ! -e your_file ] && >your_file will exit with code 1 if the file does exist but [ -e your_file ] || >your_file will always exit with code 0.
if [ ! -e "$file" ]; then touch file else . fi
race-condition-city, is there some way to use a flag, like touch -e file , which exits with 1 if the file already exists?
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.
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:
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:
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:
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.
Check if file exists [BASH]
Any ideas please? I will be glad for any help. P.S. I wish I could show the entire file without the risk of being fired from school for having a duplicate. If there is a private method of communication I will happily oblige. My mistake. Fas forcing a binary file into a wrong place. Thanks for everyone’s help.
4 Answers 4
Little trick to debugging problems like this. Add these lines to the top of your script:
The set -xv will print out each line before it is executed, and then the line once the shell interpolates variables, etc. The $PS4 is the prompt used by set -xv . This will print the line number of the shell script as it executes. You’ll be able to follow what is going on and where you may have problems.
Here’s an example of a test script:
#! /bin/bash export PS4="\$LINENO: " set -xv FILE1="$" # Line 6 if [ ! -e "$FILE1" ] # Line 7 then echo "requested file doesn't exist" >&2 exit 1 else echo "Found File $FILE1" # Line 12 fi
And here’s what I get when I run it:
$ ./test.sh .profile FILE1="$" 6: FILE1=.profile if [ ! -e "$FILE1" ] then echo "requested file doesn't exist" >&2 exit 1 else echo "Found File $FILE1" fi 7: [ ! -e .profile ] 12: echo 'Found File .profile' Found File .profile
Here, I can see that I set $FILE1 to .profile , and that my script understood that $ . The best thing about this is that it works on all shells down to the original Bourne shell. That means if you aren’t running Bash as you think you might be, you’ll see where your script is failing, and maybe fix the issue.
I suspect you might not be running your script in Bash. Did you put #! /bin/bash on the top?
script.sh [-g] [-p] [-r FUNCTION_ID|-d FUNCTION_ID] FILE
You may want to use getopts to parse your parameters:
#! /bin/bash USAGE=" Usage: script.sh [-g] [-p] [-r FUNCTION_ID|-d FUNCTION_ID] FILE " while getopts gpr:d: option do case $option in g) g_opt=1;; p) p_opt=1;; r) rfunction_id="$OPTARG";; d) dfunction_id="$OPTARG";; [?]) echo "Invalid Usage" 1>&2 echo "$USAGE" 1>&2 exit 2 ;; esac done if [[ -n $rfunction_id && -n $dfunction_id ]] then echo "Invalid Usage: You can't specify both -r and -d" 1>&2 echo "$USAGE" >2& exit 2 fi shift $(($OPTIND - 1)) [[ -n $g_opt ]] && echo "-g was set" [[ -n $p_opt ]] && echo "-p was set" [[ -n $rfunction_id ]] && echo "-r was set to $rfunction_id" [[ -n $dfunction_id ]] && echo "-d was set to $dfunction_id" [[ -n $1 ]] && echo "File is $1"
How to check if a file exists in a shell script
I’d like to write a shell script which checks if a certain file, archived_sensor_data.json , exists, and if so, deletes it. Following http://www.cyberciti.biz/tips/find-out-if-file-exists-with-conditional-expressions.html, I’ve tried the following:
[-e archived_sensor_data.json] && rm archived_sensor_data.json
when I try to run the resulting test_controller script using the ./test_controller command. What is wrong with the code?
You must set one or more whitespace between opening square bracket «[» and option «-e» same as between filename and closing square bracket «]»
7 Answers 7
You’re missing a required space between the bracket and -e :
#!/bin/bash if [ -e x.txt ] then echo "ok" else echo "nok" fi
I finally added two blank spaces, one after the opening square bracket and one before the closing one: [ -e archived_sensor_data.json ] && rm archived_sensor_data.json . The script seems to work now.
The main difference here is the fact that you are using «bash» scripting instead of «shell» scripting. Notice that the first line that you have added was #!/bin/bash, so you are telling the machine to use «bash» instead of sh. Because sh doesn’t recognize that argument «-e»
Here is an alternative method using ls :
(ls x.txt && echo yes) || echo no
If you want to hide any output from ls so you only see yes or no, redirect stdout and stderr to /dev/null :
(ls x.txt >> /dev/null 2>&1 && echo yes) || echo no
This code means: «if ls is successful, there is such file, otherwise, there is none». If ls failed, it does not mean that file is missing. It might be some other error. For example, create file in directory owned by root and try to do ls under regular user. It will fail with Permission denied , which is not equivalent that file does not exist.
The backdrop to my solution recommendation is the story of a friend who, well into the second week of his first job, wiped half a build-server clean. So the basic task is to figure out if a file exists, and if so, let’s delete it. But there are a few treacherous rapids on this river:
- Everything is a file.
- Scripts have real power only if they solve general tasks
- To be general, we use variables
- We often use -f force in scripts to avoid manual intervention
- And also love -r recursive to make sure we create, copy and destroy in a timely fashion.
Consider the following scenario:
We have the file we want to delete: filesexists.json
This filename is stored in a variable
:~/Documents/thisfolderexists filevariable="filesexists.json"
We also hava a path variable to make things really flexible
:~/Documents/thisfolderexists pathtofile=".." :~/Documents/thisfolderexists ls $pathtofile filesexists.json history20170728 SE-Data-API.pem thisfolderexists
So let’s see if -e does what it is supposed to. Does the files exist?
:~/Documents/thisfolderexists [ -e $pathtofile/$filevariable ]; echo $? 0
However, what would happen, if the file variable got accidentally be evaluated to nuffin’
:~/Documents/thisfolderexists filevariable="" :~/Documents/thisfolderexists [ -e $pathtofile/$filevariable ]; echo $? 0
What? It is supposed to return with an error. And this is the beginning of the story how that entire folder got deleted by accident
An alternative could be to test specifically for what we understand to be a ‘file’
:~/Documents/thisfolderexists filevariable="filesexists.json" :~/Documents/thisfolderexists test -f $pathtofile/$filevariable; echo $? 0
:~/Documents/thisfolderexists filevariable="" :~/Documents/thisfolderexists test -f $pathtofile/$filevariable; echo $? 1
So this is not a file and maybe, we do not want to delete that entire directory
man test has the following to say:
-b FILE FILE exists and is block special -c FILE FILE exists and is character special -d FILE FILE exists and is a directory -e FILE FILE exists -f FILE FILE exists and is a regular file . -h FILE FILE exists and is a symbolic link (same as -L)