I want to check if a file is a file and exists and if it is not empty, so ended up using a combined check with -f
and -s
. I want to return up front if the file does not exist or is empty so I am negating both checks.
To test the scenario where my file name returned empty string and I am passing a path to a directory I am trying this:
if [[ ! -f "/path/to/dir/" ]] && [[ ! -s "/path/to/dir/" ]];
then echo "Does not exists"; else echo "Exists"; fi
Exists
Above returns 'Exist' which seems incorrect.
-f
check alone is correct:
if [[ ! -f "/path/to/dir/" ]]; then echo "Does not exists";
else echo "Exists"; fi
Does not exists
The combined check but without negating each is correct as well:
if [[ -f "/path/to/dir/" ]] && [[ -s "/path/to/dir/" ]];
then echo "Exists"; else echo "Does not exists"; fi
Does not exists
Not sure if I am doing something wrong or if there is some strangeness in Bash when combining negated conditions with logical and &&
?
Edit 1: As suggested tried with notation where both conditions are in the same set of brackets:
if [[ ! -f "/opt/gmdemea/smartmap_V2/maps/" && ! -s "/opt/gmdemea/smartmap_V2/maps/" ]]; then echo "Does not exists"; else echo "Exists"; fi
Exists
But that does not change the behavior.
Edit 2:
From man page it seems that in this situation -s
should be enough but when passed existing directory path it returns true (Bash version: 4.1.2(1)-release):
if [[ -s "/opt/gmdemea/smartmap_V2/maps/" ]]; then echo "Exists"; else echo "Does not exists"; fi
Exists
It returns 'Exists' while it is not a file so should be going to else clause returning 'Does not exists'
Having x AND y
, then nagating it you get: NOT (x AND y)
. This is equal to (NOT a) OR (NOT b)
. It is not equal to (NOT x) AND (NOT y)
.
I want to check if a file is a file and exists and if it is not empty
If you want to check if a file is a regular file and if it is not empty, then you do:
[[ -f path ]] && [[ -s path ]]
The negation would be (each line is equal) (note De Morgan's law):
! ( [[ -f path ]] && [[ -s path ]] )
[[ ! -f path || ! -s path ]]
Which you can write also as (each line is equal):
! [[ -f path && -s path ]]
[[ ! ( -f path && -s path ) ]]
[[ ! -f path ]] || [[ ! -s path ]]
# or using `[` test and `-a` and `-o`:
! [ -f path -a -s path ]
[ ! -f path -o ! -s path ]
[ ! \( -f path -a -s path \) ]
So just:
if [[ ! -f "/path/to/dir/" || ! -s "/path/to/dir/" ]]; then
echo "The /path/to/dir is not a regular file or size is nonzero"
else
echo "The path /path/to/dir is a regular file and it's size is zero"
fi
I'm side-eyeing the slash on the end of
/path/to/dir/
and thinking it's worth comment, but having a hard time articulating this morning. Maybe add a note at the bottom as an aside to clarify that if the slash is there, it is a directory?@Kamil Cuk mybe you are using different Bash version (I am on 4.1.2(1)-release) and -s is not enough. If I pass existing directory path like this:
if [[ -s "/opt/gmdemea/smartmap_V2/maps/" ]]; then echo "Exists"; else echo "Does not exists"; fi
it returns 'Exists' while it is not a file so should be going to else clause and return 'Does not exist'.@PiotrPanczyk you are right. I think it's treating dir as a file with zero size... : /
@EdMorton I think because theoretically
[[ a ]] || [[ b ]]
will run two processes while[[ a || b ]]
will run only one. Note that theb
is not processed ifa
is false in both cases. Less processes usually result in faster scripts. And it's less to type. I admit, I didn't give that statement enough thought (but I think I stand by it anyway and will still use&&
||
inside[[
in my scripts)@KamilCuk I understood '-s' same as you and used in alone in the code and it got me into troubles. That's why started to investigate this.