This checks if a file exists:
#!/bin/bash
FILE=$1
if [ -f $FILE ]; then
echo "File $FILE exists."
else
echo "File $FILE does not exist."
fi
How do I only check if the file does not exist?
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. :)
-e
. -f won't pick up directories, symlinks, etc.
FILE=$1
-> FILE="$1"
and if [ -f $FILE ];
-> if [ -f "$FILE" ];
The test
command (written as [
here) has a "not" logical operator, !
(exclamation mark):
if [ ! -f /tmp/foo.txt ]; then
echo "File not found!"
fi
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:
#!/bin/bash
file=./file
if [ -e "$file" ]; then
echo "File exists"
else
echo "File does not exist"
fi
A test expression can be negated by using the !
operator
#!/bin/bash
file=./file
if [ ! -e "$file" ]; then
echo "File does not exist"
else
echo "File exists"
fi
-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)
The unary operator -z tests for a null string, while -n or no operator at all returns True if a string is not empty.
~ ibm.com/developerworks/library/l-bash-test/index.html
file
and then use that file
variable to do something else looks more readable than using an unknown argument $1
-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.
Negate the expression inside test
(for which [
is an alias) using !
:
#!/bin/bash
FILE=$1
if [ ! -f "$FILE" ]
then
echo "File $FILE does not exist"
fi
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" ]
then
echo "File $FILE does not exist"
fi
That syntax is described in "man 1 bash" in sections "Pipelines" and "Compound Commands".
[
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.
[
command behaves very similarly to the external [
command, so either man test
or man [
will give you a good idea of how it works.
[
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 ;)
[[ -f $FILE ]] || printf '%s does not exist!\n' "$FILE"
Also, it's possible that the file is a broken symbolic link, or a non-regular file, like e.g. a socket, device or fifo. For example, to add a check for broken symlinks:
if [[ ! -f $FILE ]]; then
if [[ -L $FILE ]]; then
printf '%s is a broken symlink!\n' "$FILE"
else
printf '%s does not exist!\n' "$FILE"
fi
fi
-a
can't be negated in single bracket conditional expressions. Avoid -a
altogether, I suggest.
It's worth mentioning that if you need to execute a single command you can abbreviate
if [ ! -f "$file" ]; then
echo "$file"
fi
to
test -f "$file" || echo "$file"
or
[ -f "$file" ] || echo "$file"
I prefer to do the following one-liner, in POSIX shell compatible format:
$ [ -f "/$DIR/$FILE" ] || echo "$FILE NOT FOUND"
$ [ -f "/$DIR/$FILE" ] && echo "$FILE FOUND"
For a couple of commands, like I would do in a script:
$ [ -f "/$DIR/$FILE" ] || { echo "$FILE NOT FOUND" ; exit 1 ;}
Once I started doing this, I rarely use the fully typed syntax anymore!!
[
or test
built-in would test for file existence of the argument by default (as opposed to -e
)? Would that not be ambiguous? AFAIK (and AIUI the section "CONDITIONAL EXPRESSIONS") the only thing that is tested with your approach is that the argument is not empty (or undefined), which is, in this case, a tautology (let $DIR = ''
and $FILE = ''
, then the argument is still '//'
).
ls /foo
, result ls: cannot access /foo: No such file or directory
. [ /foo ] && echo 42
, result 42
. GNU bash, version 4.2.37(1)-release (i486-pc-linux-gnu).
-f
option, at the moment I wrote this answer. Obviously you could always use -e
, if your not sure it will be a regular file. Additionally In all my scripts I quote these constructs, I must have just submitted this without adequate proofing.
[ $condition ] && if_true || if_false
is error-prone. In any event, I find [ ! -f "$file" ] && if_not_exists
easier to read and understand than [ -f "$file" ] || if_not_exists
.
To test file existence, the parameter can be any one of the following:
-e: Returns true if file exists (regular file, directory, or symlink)
-f: Returns true if file exists and is a regular file
-d: Returns true if file exists and is a directory
-h: Returns true if file exists and is a symlink
All the tests below apply to regular files, directories, and symlinks:
-r: Returns true if file exists and is readable
-w: Returns true if file exists and is writable
-x: Returns true if file exists and is executable
-s: Returns true if file exists and has a size > 0
Example script:
#!/bin/bash
FILE=$1
if [ -f "$FILE" ]; then
echo "File $FILE exists"
else
echo "File $FILE does not exist"
fi
You can do this:
[[ ! -f "$FILE" ]] && echo "File doesn't exist"
or
if [[ ! -f "$FILE" ]]; then
echo "File doesn't exist"
fi
If you want to check for file and folder both, then use -e
option instead of -f
. -e
returns true for regular files, directories, socket, character special files, block special files etc.
[
utility here though.
-f
) and directories are just two of many different types of files. There are also sockets, symlinks, devices, fifos, doors... [ -e
will test for file existence (of any type including regular, fifo, directory...) after symlink resolution.
-e
, he needs -f
.
You should be careful about running test
for an unquoted variable, because it might produce unexpected results:
$ [ -f ]
$ echo $?
0
$ [ -f "" ]
$ echo $?
1
The recommendation is usually to have the tested variable surrounded by double quotation marks:
#!/bin/sh
FILE=$1
if [ ! -f "$FILE" ]
then
echo "File $FILE does not exist."
fi
[ ... ]
.
In
[ -f "$file" ]
the [
command does a stat()
(not lstat()
) system call on the path stored in $file
and returns true if that system call succeeds and the type of the file as returned by stat()
is "regular".
So if [ -f "$file" ]
returns true, you can tell the file does exist and is a regular file or a symlink eventually resolving to a regular file (or at least it was at the time of the stat()
).
However if it returns false (or if [ ! -f "$file" ]
or ! [ -f "$file" ]
return true), there are many different possibilities:
the file doesn't exist
the file exists but is not a regular file (could be a device, fifo, directory, socket...)
the file exists but you don't have search permission to the parent directory
the file exists but that path to access it is too long
the file is a symlink to a regular file, but you don't have search permission to some of the directories involved in the resolution of the symlink.
... any other reason why the stat() system call may fail.
In short, it should be:
if [ -f "$file" ]; then
printf '"%s" is a path to a regular file or symlink to regular file\n' "$file"
elif [ -e "$file" ]; then
printf '"%s" exists but is not a regular file\n' "$file"
elif [ -L "$file" ]; then
printf '"%s" exists, is a symlink but I cannot tell if it eventually resolves to an actual file, regular or not\n' "$file"
else
printf 'I cannot tell if "%s" exists, let alone whether it is a regular file or not\n' "$file"
fi
To know for sure that the file doesn't exist, we'd need the stat()
system call to return with an error code of ENOENT
(ENOTDIR
tells us one of the path components is not a directory is another case where we can tell the file doesn't exist by that path). Unfortunately the [
command doesn't let us know that. It will return false whether the error code is ENOENT, EACCESS (permission denied), ENAMETOOLONG or anything else.
The [ -e "$file" ]
test can also be done with ls -Ld -- "$file" > /dev/null
. In that case, ls
will tell you why the stat()
failed, though the information can't easily be used programmatically:
$ file=/var/spool/cron/crontabs/root
$ if [ ! -e "$file" ]; then echo does not exist; fi
does not exist
$ if ! ls -Ld -- "$file" > /dev/null; then echo stat failed; fi
ls: cannot access '/var/spool/cron/crontabs/root': Permission denied
stat failed
At least ls
tells me it's not because the file doesn't exist that it fails. It's because it can't tell whether the file exists or not. The [
command just ignored the problem.
With the zsh
shell, you can query the error code with the $ERRNO
special variable after the failing [
command, and decode that number using the $errnos
special array in the zsh/system
module:
zmodload zsh/system
ERRNO=0
if [ ! -f "$file" ]; then
err=$ERRNO
case $errnos[err] in
("") echo exists, not a regular file;;
(ENOENT|ENOTDIR)
if [ -L "$file" ]; then
echo broken link
else
echo does not exist
fi;;
(*) syserror -p "can't tell: " "$err"
esac
fi
(beware the $errnos
support was broken with some versions of zsh
when built with recent versions of gcc
).
There are three distinct ways to do this:
Negate the exit status with bash (no other answer has said this): if ! [ -e "$file" ]; then echo "file does not exist" fi Or: ! [ -e "$file" ] && echo "file does not exist" Negate the test inside the test command [ (that is the way most answers before have presented): if [ ! -e "$file" ]; then echo "file does not exist" fi Or: [ ! -e "$file" ] && echo "file does not exist" Act on the result of the test being negative (|| instead of &&): Only: [ -e "$file" ] || echo "file does not exist" This looks silly (IMO), don't use it unless your code has to be portable to the Bourne shell (like the /bin/sh of Solaris 10 or earlier) that lacked the pipeline negation operator (!): if [ -e "$file" ]; then : else echo "file does not exist" fi
! [
and [ !
?
! [
is POSIX for shell pipelines 2.9.2 (any command) Otherwise, the exit status shall be the logical NOT of the exit status of the last command
and [ !
is POSIX for test ! expression True if expression is false. False if expression is true.
So, both are POSIX, and in my experience, both are extensively supported.
!
keyword which was introduced by the Korn shell. Except maybe for Solaris 10 and older, you're unlikely to come across a Bourne shell these days though.
envfile=.env
if [ ! -f "$envfile" ]
then
echo "$envfile does not exist"
exit 1
fi
To reverse a test, use "!". That is equivalent to the "not" logical operator in other languages. Try this:
if [ ! -f /tmp/foo.txt ];
then
echo "File not found!"
fi
Or written in a slightly different way:
if [ ! -f /tmp/foo.txt ]
then echo "File not found!"
fi
Or you could use:
if ! [ -f /tmp/foo.txt ]
then echo "File not found!"
fi
Or, presing all together:
if ! [ -f /tmp/foo.txt ]; then echo "File not found!"; fi
Which may be written (using then "and" operator: &&) as:
[ ! -f /tmp/foo.txt ] && echo "File not found!"
Which looks shorter like this:
[ -f /tmp/foo.txt ] || echo "File not found!"
The test
thing may count too. It worked for me (based on Bash Shell: Check File Exists or Not):
test -e FILENAME && echo "File exists" || echo "File doesn't exist"
test
thing may count too.": What does it count?
This code also working .
#!/bin/bash
FILE=$1
if [ -f $FILE ]; then
echo "File '$FILE' Exists"
else
echo "The File '$FILE' Does Not Exist"
fi
The simplest way
FILE=$1
[ ! -e "${FILE}" ] && echo "does not exist" || echo "exists"
This shell script also works for finding a file in a directory:
echo "enter file"
read -r a
if [ -s /home/trainee02/"$a" ]
then
echo "yes. file is there."
else
echo "sorry. file is not there."
fi
read -p "Enter file name: " -r a
to prompt as well as read. It does use quotes around the variable; that's good, but should be explained. It might be better if it echoed the file name. And this checks that the file exists and is not empty (that's the meaning of -s
) whereas the question asks about any file, empty or not (for which -f
is more appropriate).
sometimes it may be handy to use && and || operators.
Like in (if you have command "test"):
test -b $FILE && echo File not there!
or
test -b $FILE || echo File there!
If you want to use test
instead of []
, then you can use !
to get the negation:
if ! test "$FILE"; then
echo "does not exist"
fi
!
even when using [
!
You can also group multiple commands in the one liner
[ -f "filename" ] || ( echo test1 && echo test2 && echo test3 )
or
[ -f "filename" ] || { echo test1 && echo test2 && echo test3 ;}
If filename doesn't exit, the output will be
test1
test2
test3
Note: ( ... ) runs in a subshell, { ... ;} runs in the same shell.
Success story sharing
if [ ! \( -f "f1" -a -f "f2" \) ] ; then echo MISSING; fi
if [ ! -f "f1" ] || [ ! -f "f2" ] ; then echo MISSING; fi
[ -f /tmp/foo.txt ] || echo "File not found!"
-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
! -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 anif
statement and you no longer have to worry about the exit code of your non/existence check.