Shell script file existence test fails for broken symbolic link - shell

This Bourne shell script fails to detect the existence of a broken symbolic link. It returns false and doesn't echo yet /usr/bin/firefox.real is a file that exists but as a broken symbolic link. Why?
FIREFOX="/usr/bin/firefox.real"
[ -e "$FIREFOX" ] && echo "exists"

The reason is that internally, bash will call fstat(), not lstat() when you test with -e, so it checks the file itself, not the symbolic link.

Use -h to check for existence of a link (even broken):
[ -h "$FIREFOX" ] && echo "exists"
As per man test:
-h FILE
FILE exists and is a symbolic link (same as -L)

Related

Command is_dir not found

I'm trying to check if a folder exists. If it doesn't, I create it.
I have this code:
if [ $(is_dir "$contaniningdir/run") = "NO"]; then
mkdir "$containingdir/run"
fi
However, I'm getting:
is_dir: command not found
So how what's the correct way of doing this?
You should use
if [ ! -d "$DIRECTORY" ]; then
# your mkdir and other stuff ...
fi
as per this question/answer.. Another relevant question/answer is here.
One of the comments also mentions an important notice:
One thing to keep in mind: [ ! -d "$DIRECTORY" ] will be true either
if $DIRECTORY doesn't exist, or if does exist but isn't a directory.
For more you should probably check that other question's page.
is_dir is a PHP function that you probably mixed with bash unintentionally :)
bash is capable of checking for the existence of a directory without external commands:
if [ ! -d "${containingdir}/run" ]; then
mkdir "${containingdir}/run"
fi
! is negation, -d checks if the argument exists and is a directory

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
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?
You're missing a required space between the bracket and -e:
#!/bin/bash
if [ -e x.txt ]
then
echo "ok"
else
echo "nok"
fi
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
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)
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.
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">
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

What does -f mean in bash

I was looking at how to use runit to run gunicorn. I was looking at the bash file and I don't know what -f $PID does in
#!/bin/sh
GUNICORN=/usr/local/bin/gunicorn
ROOT=/path/to/project
PID=/var/run/gunicorn.pid
APP=main:application
if [ -f $PID ]; then rm $PID; fi
cd $ROOT
exec $GUNICORN -c $ROOT/gunicorn.conf.py --pid=$PID $APP
Google is useless in this case because searching for flags is useless
Google is useless in this case because searching for flags is useless
Fortunately, the Bash Reference Manual is available online, at http://www.gnu.org/software/bash/manual/bashref.html. It's the first hit when you Google for "Bash manual". ยง6.4 "Bash Conditional Expressions" says:
-f file
True if file exists and is a regular file.
-f - file is a regular file (not a directory or device file)
Check this out for all file test operators:
http://tldp.org/LDP/abs/html/fto.html
The [ is the same as the command test which allows you to test certain things. Try help test to find out what the flags are. Things to be careful with are spaces - the [ needs a space after it.
-f checks if the file exists and is a regular file.
[ -f "$var" ]
Checks if $var is an existing file (regular file). Symbolic link passes this test too.

what does this bash if statement mean?

What does the following bash script mean:
if [ -d "directory name" -a ! -L "directory name" ]; then
# do something
fi
I can understand up to here:
if [ -d "directory name"
but I'm lost after that. Extra consideration if, in addition to explanation, docs that explain -a ! -L
The -L operator tests whether its argument is a symbolic link. It can also be written as -h.
The ! is the logical negation operator, and -a is logical "and".
So this:
if [ -d "directory name" -a ! -L "directory name" ]; then
means "if whatever is a directory and is not a symbolic link". (-d will return true if the target is a symbolic link to a directory.
The [ syntax is actually a synonym for the test command. Either man test or info test on your system should show you the documentation. [ is also a built-in command in bash, so info bash will also show you the documentation; search for
`test'
-d is "directory exists," but you seem to know that.
-a is "logical and."
! is "expression is false"
-L is "file exists and is a symbolic link (same as -h)"
So in english this would read
If "directory name" exists and is a directory and "directory name" exists and is not a symobolic link, then...
The documentation you want is man test.
I believe the second portion is:
...and (-a) "directory name" is not (!) a symbolic link (-L)
The -a is an extension to the POSIX standard version of test to serve as a boolean AND operator. Its use is discouraged by the standard itself (see the Application Usage section) in favor of two separate test commands using the regular shell && operator.
if [ -d "directory name" ] && [ ! -L "directory name" ]; then
# do something
fi
The linked page also defines the -d, ! and -L operators:
-d pathname
True if pathname resolves to an existing directory entry for a directory. False if pathname cannot be resolved, or if pathname resolves to an existing directory entry for a file that is not a directory.
! expression
True if expression is false. False if expression is true.
-L pathname
True if pathname resolves to an existing directory entry for a symbolic link. False if pathname cannot be resolved, or if pathname resolves to an existing directory entry for a file that is not a symbolic link. If the final component of pathname is a symbolic link, that symbolic link is not followed.
[ -d FILE ] - True if FILE exists and is a directory.
[ -a File] - True if FILE exists.
[ -L FILE ] - True if FILE exists and is a symbolic link.
[ ! EXPR ] - True if EXPR is false.

Difference between file tests in Bash

I am troubleshooting an existing Bash script and in the script it has two tests:
if [ ! -s <file_location> ] ; then
# copy the file to the file_location
if [ -s <file_location> ] ; then
# operate on the file
fi
fi
According to the Bash Tutorial, -s tests if the file is not of zero size. Would it be better to replace the ! -s test with a -e ? I could understand the second, nested test being a -s but the first one looks like it could be replaced with -e. What is the advantage here of ! -s vs -e? Am I missing something?
If the file exists but is empty, -e would pass, but the file would likely be useless. Using ! -s ensures that the file is present and contains useful content.

Resources