Why your attempt failed and how to fix it
You can accumulate a list of file names returned by
find, but only if there are no newlines in file names, and you need to take precautions.
When you write an unquoted command substitution or variable expansion, like
$LISTOFFILES here, the shell performs word splitting and globbing (wildcard expansion) on the result.
Rule: always put double quotes around command substitutions and variable expansions.
Exception: if you understand why you need to leave off the double quotes, and why it's safe to do so.
Here, you need to leave off the double quotes, because word splitting is what splits the string into newline-separated bits. But by default it also splits at other whitespace characters. So you need to make that safe, and turn off globbing as well. You accomplish the former by setting the
IFS variable to a single newline and the latter by invoking
tar cvf backup.tar $LISTOFFILES
Note that there are no backslashes involved. Backslashes are used when you input filenames directly, not when they're produced by commands like
The usual way to use
Rather than parse the output of
find, you should make it invoke the command you want to run on the files, through the
-exec action. This takes care of the case where there are so many files that the maximum command line length is exceeded: the program is invoked as many timed as necessary.
Here, this is a little difficult because you can't invoke
tar -c multiple times (it would start over from a new empty archive at each invocation). But see Tar up all PDFs in a directory, retaining directory structure
If there aren't too many files, and you're running ksh93 or bash ≥4 or zsh, then use the
** wildcard to recurse into subdirectories. You need to set some shell options first:
set -o globstar # on ksh93
shopt -s globstar -s extglob # on bash 4
setopt ksh_glob # on zsh
tar cvf backup.tar ~/**/*.@(PDF|pdf|ODT|odt)
In zsh, with no particular settings, you can write that pattern as
setopt extglob, you can write it as
Or you can use pax:
pax -w -x ustar -s '/\.pdf$/&/' -s '/\.PDF$/&/' -s '/\.odt$/&/' -s '/\.ODT$/&/' -s '/.*//' ~ >backup.tar
If you don't want the path prefix
There are ways with
pax, but the simplest approach is to change to the directory you want to archive first.
'; set -f; cd ~ && tar cvf /path/to/backup.tar $(find . …) )
( cd ~ && pax -w … . ) >backup.tar