I\'ve seen several answers on SO about how to append to a file if it exists and create a new file if it doesn\'t (echo \"hello\" >> file.txt
) or overwrite
Assuming the file is either nonexistent or both readable and writable, you can try to open it for reading first to determine whether it exists or not, e.g.:
command 3<file 3<&- >>file
3<&-
may be omitted in most cases as it's unexpected for a program to start reading from file descriptor 3
without redirecting it first.
Proof of concept:
$ echo hello 3<file 3<&- >>file
bash: file: No such file or directory
$ ls file
ls: cannot access 'file': No such file or directory
$ touch file
$ echo hello 3<file 3<&- >>file
$ cat file
hello
$
This works because redirections are processed from left to right, and a redirection error causes the execution of a command to halt. So if file
doesn't exist (or is not readable), 3<file
fails, the shell prints an error message and stops processing this command. Otherwise, 3<&-
closes the descriptor (3
) associated with file
in previous step, >>file
reopens file
for appending and redirects standard output to it.
For example:
if [ -f "filename" ]; then
echo "hello" >>filename
fi
Just check:
if [ -f file.txt ]; then
echo "hello" >> file.txt
else
echo "No file.txt" >&2
exit 1
fi
There's no way in bash
to alter how >>
works; it will always (try to) create a file if it doesn't already exist.
I think a simple if
as proposed in the other answers would be best. However, here are some more exotic solutions:
dd
dd
can do the check and redirection in one step
echo hello | dd conv=nocreat of=file.txt
Note that dd
prints statistics to stderr. You can silence them by appending 2> /dev/null
but then the warning file does not exist
goes missing too.
When you do these kind of redirections very often, then a reusable function would be appropriate. Some examples:
Run echo
and redirect only if the file exists. Otherwise, raise the syntax error -bash: $(...): ambiguous redirect
.
ifExists() { [ -f "$1" ] && printf %s "$1"; }
echo hello >> "$(ifExists file.txt)"
Always run echo
, but print a warning and discard the output if the file does not exist.
ifExists() {
if [ -f "$1" ]; then
printf %s "$1"
else
echo "File $1 does not exist. Discarding output." >&2
printf /dev/null
fi
}
echo hello >> "$(ifExists file.txt)"
Please note that ifExists
cannot handle all file names. If you deal with very unusual filenames ending with newlines, then the subshell $( ...)
will remove those trailing newlines and the resulting file will be different from the one specified. To solve this problem you have to use a pipe.
Always run echo
, but print a warning and discard the output if the file does not exist.
appendIfExists() {
if [ -f "$1" ]; then
cat >> "$1"
else
echo "File $1 does not exist. Discarding output." >&2
return 1
fi
}
echo hello | appendIfExists file.txt