There are two main issues in solving this problem. You need to find all files that have certain filename suffixes, given by a user, and you need to add them to a tar
archive.
The find
command has a -name
option that you rightly want to use, but it can only take a single filename pattern. Since the user of the script is giving us multiple filename suffixes, we would have to use as many -name
options as there are suffixes.
This means that we have to construct an array of several -name "PATTERN"
options, with -o
in-between each (signifying a logical "OR" between them). This would then be used with find
to search for filenames with any of the given filenames suffixes.
The following does that by modifying the array $@
:
#!/bin/sh
for suffix do
shift
set -- "$@" -o -name "*.$suffix"
done
shift # remove the very first "-o" from $@
find . -type f \( "$@" \)
This modifies the $@
array which, from the start, already contains the suffixes given on the command line. In the loop, we remove the front element from $@
and insert our words at the end of the array.
If calling this script as
sh script.sh sh txt c
it would construct a find
command that would be equivalent to
find . -type f \( -name '*.sh' -o -name '*.txt' -o -name '*.c' \)
This finds all relevant files. Now we just have to add them to the archive.
With GNU tar
(but not with e.g. BSD tar
), the r
action allows us to update or create an archive (BSD tar
only updates but will not create a new archive).
backup=./PATH/backup.tar
rm -f "$backup"
find . -type f \( "$@" \) -exec tar -r -v -f "$backup" {} +
This would create the archive ./PATH/backup.tar
with the relevant files.
The reason I don't use tar -c
is that when we call tar
from find
like this, tar
may be called more than once. If I used tar -c
to create a fresh new archive, that archive would be truncated each time tar
was called (which may be many times if find
finds many thousands of files). Using tar -r
instead, we just keep updating the archive instead.
So, the complete script wold possibly look something like this:
#!/bin/sh
backup=./PATH/backup.tar
if [ "$#" -eq 0 ]; then
echo 'No filename suffixes given' >&2
exit 1
fi
for suffix do
shift
set -- "$@" -o -name "*.$suffix"
done
shift # remove the very first "-o" from $@
rm -f "$backup"
find . -type f \( "$@" \) -exec tar -r -v -f "$backup" {} +
Note that the use of quotes in the above script is quite deliberate. It will make it possible to archive files with any allowed filename, including names that contains spaces, newlines and other unusual characters.
Related:
If using a find
implementation that has -print0
, you may also pass the found pathnames to GNU tar
as in the script below:
#!/bin/sh
backup=./PATH/backup.tar
if [ "$#" -eq 0 ]; then
echo 'No filename suffixes given' >&2
exit 1
fi
for suffix do
shift
set -- "$@" -o -name "*.$suffix"
done
shift # remove the very first "-o" from $@
find . -type f \( "$@" \) -print0 | tar -c -v -f "$backup" --null -T -
With -print0
, find
will output nul-delimited pathnames that GNU tar
will read with its --null -T -
options.
That last script as a bash
-specific script (using an array, names
for the -name
options):
#!/bin/bash
backup=./PATH/backup.tar
if [ "$#" -eq 0 ]; then
echo 'No filename suffixes given' >&2
exit 1
fi
names=( -name "*.$1" )
shift
for suffix do
names+=( -o -name "*.$suffix" )
done
find . -type f \( "${names[@]}" \) -print0 | tar -c -v -f "$backup" --null -T -