views:

616

answers:

5

In a bash script, I have to check for the existence of several files.

I know an awkward way to do it, which is as follows, but that would mean that my main program has to be within that ugly nested structure:

if [ -f $FILE1 ]
then
if [ -f $FILE2 ]
then
  echo OK
  # MAIN PROGRAM HERE
fi
fi

The following version does not work:

([ -f $FILE1 ] && [ -f $FILE2 ]) ||  ( echo "NOT FOUND"; exit 1 )
echo OK

It prints

NOT FOUND
OK

Is there an elegant way to do this right?

UPDATE: See the accepted answer. In addition, in terms of elegance I like Jonathan Leffler's answer:

arg0=$(basename $0 .sh)
error()
{
    echo "$arg0: $@" 1>&2
    exit 1
}

[ -f $FILE2 ] || error "$FILE2 not found"
[ -f $FILE1 ] || error "$FILE1 not found"
+5  A: 

How about

if [[ ! ( -f $FILE1 && -f $FILE2 ) ]]; then
    echo NOT FOUND
    exit 1
fi

# do stuff
echo OK

See help [[ and help test for the options usable with the [[ style tests. Also read this faq entry.

Your version does not work because (...) spawns a new sub-shell, in which the exit is executed. It therefor only affects that subshell, but not the executing script.

The following works instead, executing the commands between {...} in the current shell.

I should also note that you have to quote both variables to ensure there is no unwanted expansion or word splitting made (they have to be passed as one argument to [).

[ -f "$FILE1" ] && [ -f "$FILE2" ] || { echo "NOT FOUND"; exit 1; }
Johannes Schaub - litb
fixed already :) difficult with logic in the evening :)
Johannes Schaub - litb
+2  A: 

I think you're looking for:

if [ -f $FILE1 -a -f $FILE2 ]; then
    echo OK
fi

See man test for more details on what you can put inside the [ ].

Greg Hewgill
A: 

the exit 1 is just exiting the subshell started with the parentheses

[ -f $1 ] && [ -f $2 ] || !(echo "NOT FOUND") || exit 1
echo OK

Clint
+3  A: 

You can list the files and check them in a loop:

file_list='file1 file2 wild*'
for file in $file_list; do
    [ -f $file ] || exit
done

do_main_stuff
Adam Liss
+1  A: 

I usually use a variant on:

arg0=$(basename $0 .sh)
error()
{
    echo "$arg0: $@" 1>&2
    exit 1
}

[ -f $FILE2 ] || error "$FILE2 not found"
[ -f $FILE1 ] || error "$FILE1 not found"

There's no particular virtue in making the shell script have a single exit point - no harm either, but fatal errors may as well terminate the script.

The only point of debate would be whether to diagnose as many problems as possible before exiting, or whether to just diagnose the first. On average, diagnosing just the first is a whole lot easier.

Jonathan Leffler