如何 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" 和 运行 的字符串作为命令。
我有一个包含子目录和文件的目录(名为 '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" 和 运行 的字符串作为命令。