find -exec 在带有变量扩展的 bash 脚本中

Mat*_*hew 14 bash find shell-script posix

我正在尝试在 bash 脚本中运行类似于以下命令的命令。它应该搜索所有子文件夹$sourcedir并将特定类型的所有文件复制到$targetdir.

#!/bin/bash

# These are set as arguments to the script, not hard-coded
sourcedir="/path/to/sourcedir"
targetdir="/path/to/targetdir"

find "$sourcedir" -type f -name "*.type" -exec sh -c 'cp "$1" "$2/`basename "$1"`"' "{}" "$targetdir" \;
Run Code Online (Sandbox Code Playgroud)

这似乎非常接近,除了{}没有被传递$2-exec sh -c ...

我想尽可能接近“正确的方式”,并容忍文件名中的特殊字符(特别是单引号字符)。

编辑:我看到有人建议使用xargs或参数链接。我的印象是这仅适用于有限数量的参数。例如,如果我有数以千计的 .jpg 文件,我试图从多个画廊目录复制到一个巨大的幻灯片目录中,那么链接参数的解决方案是否仍然有效?

编辑 2:我的问题是我_-exec命令中的第一个选项 sh 之前缺少一个。对于任何对如何使 find 命令工作感到好奇的人,添加_,一切都会好起来的:

find "$sourcedir" -type f -name "*.type" -exec sh -c 'cp "$1" "$2"' _ "{}" "$targetdir" \;
Run Code Online (Sandbox Code Playgroud)

我已经接受了下面的答案,因为它完成了相同的任务,但更高效和优雅。

Sie*_*geX 6

您需要将{}参数作为参数传递给 shell,然后循环遍历每个 arg。

find "$sourcedir" -type f -name "*.type" -exec sh -c 'for f; do cp "$f" "$0"; done' "$targetdir" {} +
Run Code Online (Sandbox Code Playgroud)

注意:这样做的方式是,shell 的第一个 arg 是 shell 的名称,我们可以通过将名称作为 the 传递$targetdir,然后使用$0shell 脚本中的特殊参数来访问该目标目录来利用这一点。


J. *_*ker 6

您想将特定类型的文件复制到特定目录?这最好用 完成xargs,你甚至不需要那个sh。这是一种更合适的方式来做到这一点,也应该更有效地运行。

find "$sourcedir" -type f -name "*.type" | xargs cp -t targetdir
Run Code Online (Sandbox Code Playgroud)

如果您需要处理特殊文件名,请NULL用作分隔符

find "$sourcedir" -type f -name "*.type" -print0 | xargs -0 cp -t "$targetdir"
Run Code Online (Sandbox Code Playgroud)


use*_*own 5

如果你不相信 xargs 教会:

find "$sourcedir" -type f -name "*.mp3" -exec cp -t "$targetdir" {} +
Run Code Online (Sandbox Code Playgroud)

解释:

cp -t a b c d 
Run Code Online (Sandbox Code Playgroud)

将 b、c 和 d 复制到目标目录 a。

-exec cmd {} +
Run Code Online (Sandbox Code Playgroud)

一次对一大堆文件调用命令,而不是一个接一个地调用(这是标准的,如果您使用";"代替+)。这就是为什么必须将 targetdir 拉到前面并将其明确标记为目标的原因。

这适用于 gnu-find 并且可能不适用于 find 的其他实现。当然,它也依赖于 -t -flag。

TechZilla 当然是最正确的,因为sh它不需要调用 cp。

如果您不使用xargs,这在大多数情况下与 结合使用find是不必要的,您就无需学习-print0-0标志。