![tar“无法统计:目录中没有这样的文件”传递数组变量时](https://linux22.com/image/11844/tar%E2%80%9C%E6%97%A0%E6%B3%95%E7%BB%9F%E8%AE%A1%EF%BC%9A%E7%9B%AE%E5%BD%95%E4%B8%AD%E6%B2%A1%E6%9C%89%E8%BF%99%E6%A0%B7%E7%9A%84%E6%96%87%E4%BB%B6%E2%80%9D%E4%BC%A0%E9%80%92%E6%95%B0%E7%BB%84%E5%8F%98%E9%87%8F%E6%97%B6.png)
我正在尝试编写一个 bash 脚本,该脚本将在目录中创建文件的存档(tar)。像这样调用 bash 脚本 (./backup.bash pdf txt bak) 时,需要将文件扩展名作为参数传入。我正在使用数组来存储这些参数。 (ls | grep -i {array}) 正在查找目录中与输入数组的文件扩展名匹配的所有文件,并将列出找到的文件。 (find . -type)正在使用这些扩展名来查找与扩展名关联的文件。 (tar cvf) 正在工作目录中创建名为 backup.tar 的备份文件。我在 tar 命令末尾列出了该数组,但现在想起来也许我可以将 find 命令通过管道传输到 tar 命令中。
my_array=([$@])
ls | grep -i \{my_array[$@]}
find . -type f \( -name "my_array[$@]" \)
tar cvf ./PATH/backup.tar my_array[$@]
答案1
解决这个问题有两个主要问题。您需要查找具有用户指定的特定文件名后缀的所有文件,并且需要将它们添加到存档中tar
。
该find
命令有一个-name
您想要使用的选项,但它只能采用单个文件名模式。由于脚本的用户为我们提供了多个文件名后缀,因此我们必须使用-name
与后缀一样多的选项。
这意味着我们必须构造一个由多个-name "PATTERN"
选项组成的数组,-o
每个选项之间都有一个中间值(表示它们之间的逻辑“OR”)。然后,这将用于find
搜索具有任何给定文件名后缀的文件名。
下面通过修改数组来实现这一点$@
:
#!/bin/sh
for suffix do
shift
set -- "$@" -o -name "*.$suffix"
done
shift # remove the very first "-o" from $@
find . -type f \( "$@" \)
这会修改$@
数组,该数组从一开始就已经包含命令行上给出的后缀。在循环中,我们删除前面的元素$@
并将单词插入到数组的末尾。
如果将此脚本称为
sh script.sh sh txt c
它将构造一个find
相当于的命令
find . -type f \( -name '*.sh' -o -name '*.txt' -o -name '*.c' \)
这会找到所有相关文件。现在我们只需将它们添加到存档中即可。
对于 GNU tar
(但不是例如 BSD tar
),该r
操作允许我们更新或创建存档(BSDtar
只更新但不会创建新存档)。
backup=./PATH/backup.tar
rm -f "$backup"
find . -type f \( "$@" \) -exec tar -r -v -f "$backup" {} +
./PATH/backup.tar
这将创建包含相关文件的存档。
我不使用的原因tar -c
是,当我们这样调用时,tar
可能会被调用多次。如果我过去创建一个全新的存档,则每次调用该存档都会被截断(如果找到数千个文件,则可能会被截断很多次)。相反,我们只是不断更新存档。find
tar
tar -c
tar
find
tar -r
所以,完整的脚本可能看起来像这样:
#!/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" {} +
请注意,上述脚本中引号的使用是经过深思熟虑的。它将可以使用任何允许的文件名来归档文件,包括包含空格、换行符和其他不常见字符的名称。
有关的:
如果使用find
具有 的实现-print0
,您还可以将找到的路径名传递给 GNU,tar
如下面的脚本所示:
#!/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 -
使用-print0
,将输出 GNU将使用其选项读取的find
nul 分隔路径名。tar
--null -T -
最后一个脚本作为bash
特定脚本(使用数组names
作为-name
选项):
#!/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 -
答案2
使用zsh
和 GNUtar
或bsdtar
:
#! /bin/zsh -
set -o extendedglob
output=file.tar.gz
printf '%s\0' **/*.(${(j:|:)~${(b)@}})~$output(D.) |
tar --null -cf - -T - | xz > $output
${(b)@}
:引用位置参数以防止它们被视为模式${(j:|:)...}
: 将结果单词与|
${~var}
:将扩展视为通配模式(现在看起来就像jpg|gif|\*
位置参数是jpg
,gif
,*
)**/
:任何级别的子目录pattern~$output
:从 glob 扩展中排除输出文件本身(D.)
: glob 限定符:包含隐藏文件并仅选择常规文件。