Warm tip: This article is reproduced from stackoverflow.com, please click
bash linux

Combining two negated file check conditions (-f and -s) seems to return incorrect result

发布于 2020-03-27 10:15:19

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'

Questioner
Piotr Panczyk
Viewed
136
KamilCuk 2019-07-03 22:08

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