【问题标题】:How to bash copy all files in directory/sub-directories excluding those contained in a list while flattening the tree structure?如何在展平树结构的同时复制目录/子目录中的所有文件,不包括列表中包含的文件?
【发布时间】:2017-08-04 13:31:25
【问题描述】:

我有一个包含子目录和文件的目录(称为“源”)。使用 bash 我需要将在该目录及其每个子目录中找到的所有文件(并且只有文件,而不是目录)复制到不同的目录(称为“目标”)。目录树不得维护/必须展平。只有不包含在文本文件中的文件(称为“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 脚本,并且存在源文件,并且它们的文件名出现在 exclude.txt 文件中,则不应将它们复制到目标位置。

由于目录树结构得到维护,我尝试使用“cp”和“rsync”失败了。我也未能使用“查找”,因为在执行复制操作之前,我无法对照“excluded.txt”列表检查结果。

【问题讨论】:

  • 找到 /home/source/ -type f -exec cp -n {} /home/destination/ \;如果要避免覆盖目标文件夹中的现有文件,请使用 'cp -n'
  • @KuldeepSingh 我最初编写此代码 sn-p 作为答案,但我忘了考虑到 OP 有一个包含排除文件的文件
  • 使用 cp 命令的 -n 选项可以避免重写文件。除非在其他地方需要 exclude.txt
  • @KuldeepSingh 感谢您提出的解决方案!如果文件保留在目标目录中,它将起作用。但是,由于文件会定期删除,excluded.txt 列表会跟踪所有移动/复制的文件。

标签: bash tree find copy rsync


【解决方案1】:

@Aserre 提供的答案有助于找到此解决方案。他的解决方案适用于所有不包含空格的文件。在阅读了eval(评估/执行字符串)、string concatenationhow 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”的字符串并将其作为命令运行。

【讨论】:

    【解决方案2】:

    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
    • @Aserre 这非常有效。拥有排除列表可防止文件被重复复制(因为它们会定期从目标位置删除)。非常感谢!
    • @Aserre 所以,我刚刚确定此代码仅适用于不包含空格的文件名。如果文件名中有空格,例如“file 2.txt”,则在执行代码时会重复将文件复制到目标目录(如果原始副本已从目标目录中删除)。
    • @Aserre 感谢您迄今为止的帮助。不幸的是,这并没有解决这个问题。如果我在源目录中有两个文件,“file1.txt”和“file 2.txt”。 $(printf "! -name %s" "$(cat exclude.txt)" 命令的新输出在下一行显示! -name file1.txt,后跟'file 2.txt''file 2.txt' 应与 ! -name 位于同一行。我一直在寻找解决方案的论坛,这似乎比我预期的要困难。有什么想法吗?
    猜你喜欢
    • 2013-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-17
    • 2018-05-02
    • 1970-01-01
    • 2021-01-05
    相关资源
    最近更新 更多