Issue
I'm writing a script where I need to use the output of a file test in several places, including inside a shell function. I would like to assign the file existence to a shell variable, like this: file_exists=[ -f $myfile ]
.
Just to make sure that I've got my bases covered, I start by touching a file, and testing its existance:
file='a'
touch $file
if [ -f $file ]
then
echo "1 -- '$file' exists"
fi
Output:
1 -- 'a' exists
The file was created successfully -- no surprises, but at least I know that I'm not dealing with any permissions issues or anything.
Next I test to make sure that I can store a boolean expression in a variable:
mytest=/bin/true
if $mytest
then
echo "2 -- \$mytest is true"
fi
Output:
2 -- $mytest is true
So I've got the basics covered -- conditional expressions should emit the same output as /bin/true
or /bin/false
... but that's not what I'm seeing:
mytest=[ -f $file ]
if $mytest
then
echo "3 -- \$mytest is true [expect true]"
else
echo "3 -- \$mytest is false [expect true]"
fi
This fails with the following error:
-f: command not found
I get the same error message if i use test -f $file
rather than [ -f $file ]
.
If I put a space in front of the [
, the error goes away...
mytest= [ -f $file ]
if $mytest
then
echo "4 -- \$mytest is true [expect true]"
else
echo "4 -- \$mytest is false [expect true]"
fi
The output appears to be correct:
4 -- $mytest is true [expect true]
... but if I remove the file, I should get the opposite result:
rm $file
mytest= [ -f $file ]
if $mytest
then
echo "5 -- \$mytest is true [expect false]"
else
echo "5 -- \$mytest is false [expect false]"
fi
... and I don't:
5 -- $mytest is true [expect false]
To be fair, I expected the space to mess with the truth value:
mytest= /bin/false
if $mytest
then
echo "6 -- \$mytest is true [expect false]"
else
echo "6 -- \$mytest is false [expect false]"
fi
Outputs:
6 -- $mytest is true [expect false]
So, how do I store the output from the test
builtin in a shell variable?
Solution
As others have documented here, using the string "true" is a red herring; this is not an appropriate way to store boolean values in shell scripts, as evaluating it means dynamically invoking a command rather than simply inspecting the stored value using shell builtins hardcoded in your script.
Instead, if you really must store an exit status, do so as a numeric value:
[ -f "$file" ] # run the test
result=$? # store the result
if (( result == 0 )); then # 0 is success
echo "success"
else # nonzero is failure
echo "failure"
fi
If compatibility with set -e
is desired, replace the first two lines of the above with:
result=0
[ -f "$file" ] || result=$?
...as putting the test on the left-hand side of ||
marks it as "checked", suppressing errexit
behavior. (That said, see BashFAQ #105 describing the extent to which set -e
harms predictable, portable behavior; I strongly advise against its use).
Answered By - Charles Duffy Answer Checked By - Terry (WPSolving Volunteer)