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?
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).
You need to quote whitespace:
mytest='[ -f $file ]'
if $mytest; then echo yes; fi
However, this is extremely brittle and potentially insecure. See http://mywiki.wooledge.org/BashFAQ/050 for a detailed discussion and some better ways to accomplish something similar.
If you want to encapsulate a complex piece of code, a function is usually the way to go:
mytest () { [ -f "$file" ]; }
if mytest; then echo yes; fi
If you want to run the code once and store its result so you can examine it later, I would rephrase it like this:
if [ -f "$file" ]; then
mytest=true
else
mytest=false
fi
if $mytest; then echo yes; fi
A old one but left this here for reference for people that might need it. Not the most beautiful solution but it works in bash:
mytest=$( [ -f $file ] ; echo $? )
More portable, using the test command, and the backticks:
set mytest=`test -f $file ; echo $?`
In a subprocess (<!> system load), the condition is evaluated, and then the result echoed to the output that is captured by the variable $mytest.
mytest=/bin/true
is storing the string /bin/true
in the $mytest
variable.
mytest=[ -f $file ]
is setting the $mytest
variable to the value [
for the duration of the command -f $file ]
(which as your output indicates fails as there is no -f
command available).
mytest= [ -f $file ]
(like the above) sets the value of the $mytest
variable to blank for the duration of the [ -f $file ]
command (and returns whatever [
returns).
mytest= /bin/false
this is the same as the above case only the command being run is /bin/false
.
If you want to store the return code from a command in a variable you can do
/bin/true
ret=$?
if you want to store the output from a command in a variable you can do
out=$(/bin/true)
(though with /bin/true
that variable will be empty as it outputs no text.
For your case you want the former $?
model.
Also, using set -x
(and/or set -v
) in your scripts might have helped you diagnose this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With