Reputation: 41163
I have a bash program that will write to an output file. This file may or may not exist, but the script must check permissions and fail early. I can't find an elegant way to make this happen. Here's what I have tried.
set +e touch $file set -e if [ $? -ne 0 ]; then exit;fi
I keep set -e
on for this script so it fails if there is ever an error on any line. Is there an easier way to do the above script?
Upvotes: 34
Views: 100798
Reputation: 26086
Why complicate things?
file=exists_and_writeable
if ! [ -e "$file" ] ; then
touch "$file"
fi
if ! [ -w "$file" ] ; then
printf 'cannot write to %s\n' "$file"
exit 1
fi
Or, more concisely,
{ [ -e "$file" ] || touch "$file"; } && \
! [ -w "$file" ] && printf 'cannot write to %s' "$file" && exit 1
Upvotes: 42
Reputation: 5592
Why must the script fail early? By separating the writable test and the file open() you introduce a race condition. Instead, why not try to open (truncate/append) the file for writing, and deal with the error if it occurs? Something like:
$ echo foo > output.txt
$ if [ $? -ne 0 ]; then die("Couldn't echo foo")
As others mention, the "noclobber" option might be useful if you want to avoid overwriting existing files.
Upvotes: 3
Reputation: 107739
Open the file for writing. In the shell, this is done with an output redirection. You can redirect the shell's standard output by putting the redirection on the exec
built-in with no argument.
set -e
exec >shell.out # exit if shell.out can't be opened
echo "This will appear in shell.out"
Make sure you haven't set the noclobber
option (which is useful interactively but often unusable in scripts). Use >
if you want to truncate the file if it exists, and >>
if you want to append instead.
If you only want to test permissions, you can run : >foo.out
to create the file (or truncate it if it exists).
If you only want some commands to write to the file, open it on some other descriptor, then redirect as needed.
set -e
exec 3>foo.out
echo "This will appear on the standard output"
echo >&3 "This will appear in foo.out"
echo "This will appear both on standard output and in foo.out" | tee /dev/fd/3
(/dev/fd
is not supported everywhere; it's available at least on Linux, *BSD, Solaris and Cygwin.)
Upvotes: 2
Reputation: 140237
Rather than check $?
on a different line, check the return value immediately like this:
touch file || exit
As long as your umask
doesn't restrict the write bit from being set, you can just rely on the return value of touch
Upvotes: 29
Reputation: 36999
You can use -w
to check if a file is writable (search for it in the bash man page).
if [[ ! -w $file ]]; then exit; fi
Upvotes: 4