我不是脚本编写或查找命令方面的专业人士(只是警告)。我正在尝试创建一个脚本,可以将我的所有 mp3 播客移动到一个文件夹,然后我可以将其移动到我的手机。
所以我制作的脚本需要移动所有 mp3(即使是那些具有奇怪路径的)。我创建了一个脚本,如下所示,它似乎*按照我的意图进行,但它不断给出错误,如下所示:
find /home/jason/gPodder/ -name '*.mp3' -exec bash " cp '{}' /home/jason/gPodder/mp3/ " \;
Which returns:
bash: cp '/home/jason/gPodder/Downloads/The Documentary/TheDocumentary20170823-GoingGreenInTheOilState.mp3' /home/jason/gPodder/mp3/ : No such file or directory
但如果我复制上面的 bash“命令”,它就没有问题。请您帮我理解脚本中的错误是什么。
谢谢
答案1
作为@Kusalananda 说,您缺少-c
能够使用内联脚本的功能。
但即便如此,也永远不要将 嵌入{}
到 shell 代码中,这将是一个任意命令注入漏洞(例如'$(reboot)'.mp3
用您的示例调用的文件)。相反,将其作为内联脚本的参数(假设您确实需要一个内联脚本,并且这cp
只是一个示例)。
find ... -exec sh -c 'cp "$1" /home/jason/gPodder/mp3' sh {} \;
(您也不需要bash
仅仅为此。您的sh
意愿也可以)。
或者更好的是,一次传递多个参数cp
:
find ... -exec sh -c 'cp "$@" /home/jason/gPodder/mp3' sh {} +
使用 GNU cp
,您还可以做到:
find ... -exec cp -t /home/jason/gPodder/mp3 {} +
答案2
不要让它变得比需要的更复杂。
find /home/jason/gPodder/ -name '*.mp3' -exec cp {} /home/jason/gPodder/mp3/ \;
或者可能
find /home/jason/gPodder/ -type f -name '*.mp3' -exec cp {} /home/jason/gPodder/mp3/ \;
只考虑常规文件。
仅当目标文件夹中不存在同名文件时才复制文件,某些cp
实现支持-n
该选项,或者您可以使用-i
重定向自的标准输入/dev/null
(但要注意,如果目标类型为目录,则这些不起作用)或者做类似的事情:
find /home/jason/gPodder/ -type f -name '*.mp3' \
! -execdir test -e /home/jason/gPodder/mp3/{} \; \
-exec cp {} /home/jason/gPodder/mp3/ \;
-execdir
是 POSIX 标准的扩展find
,但受到许多现代find
实现的支持。它的执行方式与此类似-exec
,但使用找到的内容的父目录作为其工作目录来执行命令。此外,{}
将是找到的东西的基本名称(file.mp3
或者./file.mp3
取决于find
实现)而不是它的完整路径。看man find
。
该错误源于bash "somecommand"
.bash
以这种方式调用时,bash
期望somecommand
是脚本文件的路径,而不是一系列 shell 命令,并且它无法找到具有您指定名称的脚本。
要执行任意一系列 shell 命令,您需要bash -c "somecommand"
.
但是,在这种情况下,只需使用-exec cp
上面或中的方法即可斯特凡·查泽拉斯的回答。
答案3
其他人指出了报价和执行问题。
我建议您不要使用 find -exec,而是查找文件列表,并将该列表通过管道传输到 xargs 和 cp。使用 print0 选项将帮助您避开文件和目录名称中的任何“有趣”字符
例如
find sourcepath -name \*.mp3 -print0 \
| xargs -0 cp -t destinationpath
这样做的好处是 xargs 将从 find 中收集多个文件名并将它们同时传递给 cp,因此每个 cp 实例可以复制 20 或 100 个文件,而不是为每个文件启动 1 次 cp 调用。 xargs 会为您将足够的文件名放入 cp 命令行中。
您还可以将 -P n 选项添加到 xargs,它将并行启动多个 cp 进程。
前提是您的文件与目标 mp3 文件夹位于同一文件系统上。您可以将 -l 添加到 cp ,它会将文件硬链接到目标,而不是复制它们。
例如
find sourcepath -name \*.mp3 -print0 \
| xargs -0 -P 5 \
cp -l -t destinationpath