如何 bash 复制 directory/sub-directories 中的所有文件,不包括列表中包含的文件,同时展平树结构?

How to bash copy all files in directory/sub-directories excluding those contained in a list while flattening the tree structure?

我有一个包含子目录和文件的目录(名为 'source')。使用 bash 我需要将在此目录及其每个子目录中找到的所有文件(并且只有文件,而不是目录)复制到不同的目录(称为 'destination')。目录树不能 maintained/must 被压扁。只有不包含在文本文件中的文件(称为 'excluded.txt')必须被复制。

源输入示例:

/home/source/AAA/file1.xyz 
/home/source/AAA/GGG/file2.xyz
/home/source/BBB/file3.tuv
/home/source/BBB/HHH/file4.tuv

目标输出示例:

/home/destination/file1.xyz
/home/destination/file2.xyz
/home/destination/file3.tuv
/home/destination/file4.tuv

一旦文件被复制,四个以上的文件名(file1.xyz,等等)被添加到excluded.txt(每个文件名在一个新行)。这些文件将定期从目标目录中删除。

如果再次执行 bash 脚本,并且存在源文件,如果它们的文件名出现在 excluded.txt 文件中,则不应将它们复制到目标。

我尝试 "cp" 和 "rsync" 失败了,因为目录树结构被维护了。我使用 "find" 也失败了,因为在执行复制操作之前我无法根据 "excluded.txt" 列表检查结果。

find应该是用于递归搜索的工具

find /home/source -type f $(printf "! -name %s" "$(cat exclude.txt)") -exec cp -n {} /home/destination \;

解释:

  • find /home/source :要搜索的根目录路径。搜索是递归的。
  • -type f : 只检索文件
  • $(printf "! -name %s " $(cat exclude.txt)) :将写入 ! -name file1 ! -name file2 ...,列出所有要排除的文件
  • -exec cp -n {} /home/destination :为每个找到的项目执行的操作。 {} 表示找到的项目。

@Aserre 提供的答案有助于找到此解决方案。他的解决方案适用于所有不包含空格的文件。在阅读了有关 eval (evaluating/executing strings), string concatenation, and how to read entire lines into variables 的内容后,我能够成功编写并执行以下代码。

while read -r line
do
    name="$line"
    exclude="$exclude ! -name \"$name\""
done < "/mnt/destination/exclude.txt"
cmd1="find \"/home/source\" -type f "
cmd2=" -exec cp -n {} \"/home/destination\" \;"
result=$cmd1$exclude$cmd2
eval $result

解释(归功于@Aserre):

  • while read -r line : 遍历 exclude.txt 中的每一行。 “-r”标志导致反斜杠被视为该行的一部分。
  • name="$line" :excluded.txt 中的整行存储在名为 "name".
  • 的新字符串中
  • exclude="$exclude ! -name \"$name\"" :将 ! -name "file1" ! -name "file2" ! -name... 存储在名为 "exclude" 的新字符串中。该字符串是要排除的所有文件的列表,每个文件前面都有 ! -name。每个引号前都需要反斜杠。
  • cmd1= :将以下 2 个命令存储到一个名为 "cmd1".
  • 的字符串中
  • find /home/source :要搜索的根目录路径。搜索是递归的。
  • -type f : 仅检索文件。
  • -exec cp -n {} /home/destination :为每个找到的项目执行的操作。 {} 表示找到的项目。
  • cmd2= :将上一个命令存储到一个名为 "cmd2".
  • 的字符串中
  • result=$cmd1$exclude$cmd2 : 连接所有 3 个字符串。
  • eval $result : 将名为 "result" 和 运行 的字符串作为命令。