tar“无法统计:目录中没有这样的文件”传递数组变量时

tar“无法统计:目录中没有这样的文件”传递数组变量时

我正在尝试编写一个 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可能会被调用多次。如果我过去创建一个全新的存档,则每次调用该存档都会被截断(如果找到数千个文件,则可能会被截断很多次)。相反,我们只是不断更新存档。findtartar -ctarfindtar -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将使用其选项读取的findnul 分隔路径名。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和 GNUtarbsdtar

#! /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 限定符:包含隐藏文件并仅选择常规文件。

相关内容