285

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

However, this throws an error

[-e: command not found

when I try to run the resulting test_controller script using the ./test_controller command. What is wrong with the code?

Kurt Peek
  • 52,165
  • 91
  • 301
  • 526
  • 4
    Possible duplicate of [How do I tell if a regular file does not exist in Bash?](https://stackoverflow.com/questions/638975/how-do-i-tell-if-a-regular-file-does-not-exist-in-bash) – jww Mar 31 '18 at 22:01
  • 6
    You must set one or more whitespace between opening square bracket "[" and option "-e" same as between filename and closing square bracket "]" – Konstantin Yaniv Jul 24 '18 at 13:35
  • 2
    Related: [Why do we need a space around square brackets in Bash if](https://stackoverflow.com/q/9581064/6862601). – codeforester Jul 23 '20 at 22:02

7 Answers7

536

You're missing a required space between the bracket and -e:

#!/bin/bash
if [ -e x.txt ]
then
    echo "ok"
else
    echo "nok"
fi
scohe001
  • 15,110
  • 2
  • 31
  • 51
chris01
  • 10,921
  • 9
  • 54
  • 93
  • 17
    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. – Kurt Peek Oct 17 '16 at 09:57
  • 8
    This also works using `if [ -e "$1" ]` (filename input argument). – Edward Oct 20 '18 at 20:53
  • 11
    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" – Nick Cuevas Apr 09 '19 at 20:37
  • 2
    Is `-e` POSIX shell compliant? – Aaron Franke Aug 04 '22 at 23:35
  • @NickCuevas where did you get the idea that -e isn't supported by sh's [? It's always worked for me. I don't see any errors about -e being an unrecognized *argument* it says unrecognized *command* in op's error. – Rick Jul 26 '23 at 12:16
  • @AaronFranke yes `-e` is POSIX compliant. Here's the doc: https://www.unix.com/man-page/posix/1p/test/ – Rick Jul 26 '23 at 12:18
72

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
Philip Kirkbride
  • 21,381
  • 38
  • 125
  • 225
  • 19
    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. – Tigran May 21 '19 at 20:44
  • 5
    @Tigran That's a good distinction to know, in my case doesn't matter, but thanks for pointing out. – lucaswxp Jul 30 '20 at 17:30
24

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

<host>:~/Documents/thisfolderexists filevariable="filesexists.json"

We also hava a path variable to make things really flexible

<host>:~/Documents/thisfolderexists pathtofile=".."

<host>:~/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?

<host>:~/Documents/thisfolderexists [ -e $pathtofile/$filevariable ]; echo $?

0

It does. Magic.

However, what would happen, if the file variable got accidentally be evaluated to nuffin'

<host>:~/Documents/thisfolderexists filevariable=""

<host>:~/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'

<host>:~/Documents/thisfolderexists filevariable="filesexists.json"

<host>:~/Documents/thisfolderexists test -f $pathtofile/$filevariable; echo $?

0

So the file exists...

<host>:~/Documents/thisfolderexists filevariable=""

<host>:~/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)
Lefty G Balogh
  • 1,771
  • 3
  • 26
  • 40
9

Internally, the rm command must test for file existence anyway,
so why add another test? Just issue

rm filename

and it will be gone after that, whether it was there or not.
Use rm -f is you don't want any messages about non-existent files.

If you need to take some action if the file does NOT exist, then you must test for that yourself. Based on your example code, this is not the case in this instance.

T G
  • 445
  • 3
  • 7
4

If you're using a NFS, "test" is a better solution, because you can add a timeout to it, in case your NFS is down:

time timeout 3 test -f 
/nfs/my_nfs_is_currently_down
real    0m3.004s <<== timeout is taken into account
user    0m0.001s
sys     0m0.004s
echo $?
124   <= 124 means the timeout has been reached

A "[ -e my_file ]" construct will freeze until the NFS is functional again:

if [ -e /nfs/my_nfs_is_currently_down ]; then echo "ok" else echo "ko" ; fi

<no answer from the system, my session is "frozen">
FCA69
  • 185
  • 1
  • 5
2

You could also uses stat :

stat /
  File: /
  Size: 4096            Blocks: 8          IO Block: 4096   directory
Device: fd01h/64769d    Inode: 2           Links: 26
Access: (0755/drwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2009-01-01 02:00:00.000000000 +0200
Modify: 2009-01-01 02:00:00.000000000 +0200
Change: 2009-01-01 02:00:00.000000000 +0200
 Birth: -

On a path that doesn't exist, you will get:

stat /aaa
stat: cannot stat '/aaa': No such file or directory
android developer
  • 114,585
  • 152
  • 739
  • 1,270
0
read -p "enter filename:"filename
If [ -e $filename ]
Then
     echo "file exist"
else
     echo "file doesn't exist"
ChrisGPT was on strike
  • 127,765
  • 105
  • 273
  • 257
R0cher
  • 1
  • 1
    While possibly correct, a code-only answer helps the person who asked the question, it doesn't do them or future visitors any good. See [Is there any benefit in code-only answers?](https://meta.stackexchange.com/a/148274/183937) – Rohit Gupta Apr 26 '23 at 05:14
  • Bash is case sensitive... please test simple code when submitting it. – General Grievance Apr 28 '23 at 12:07