我需要同时使用 find 和 mv
要求:需要找到所有文件*.xml但排除deposit.xml并将其移动到另一个文件夹(相当简单)
我的尝试:
mv `find ./*.xml '!' -type d | fgrep -v deposit.xml` ./Archive
如果有任何文件,这工作得很好,如果不存在文件,则会抛出以下错误
mv: missing destination file operand after `./Archive'
Try `mv --help' for more information.*
答案1
解析ls
or的输出通常是一个坏主意find
,因为您无法区分哪些字符是文件名的一部分以及哪些字符是分隔符。如果您不控制文件名,这可能是一个安全漏洞,允许攻击者伪造文件名,导致您的脚本执行任意代码。看为什么我的 shell 脚本会因为空格或其他特殊字符而卡住?。不过,在您的情况下可能没问题,特别是当您的文件似乎是某种源代码树时,文件名通常不带“异国情调”字符,特别是不带空格。
尽管如此,使用强大的命令实际上并不困难。核心思想是进行find
调用mv
而不是解析find
.以下命令.xml
以递归方式从当前目录和子目录移动:
find . ! -type d -name '*.xml' ! -name deposit.xml -exec mv {} ./Archive \;
为了稍微加快速度,您可以mv
通过运行中间 shell 对 的调用进行分组。
find . ! -type d -name '*.xml' ! -name deposit.xml -exec sh -c 'mv "$@" "$0"' ./Archive {} +
如果您只想从当前目录移动文件,而不遍历子目录,那么您需要指示find
不要递归。如果您的find
实现支持该-maxdepth
选项:
find . -maxdepth 1 ! -type d -name '*.xml' ! -name deposit.xml -exec sh -c 'mv "$@" "$0"' ./Archive {} +
否则:
find . -name . -o -type d -prune -o -name '*.xml' ! -name deposit.xml -exec sh -c 'mv "$@" "$0"' ./Archive {} +
如果您的 shell 是 ksh,则可以使用它的扩展 glob。这比使用 更容易find
,但便携性较差。移动当前目录中的文件:
mv !(deposit).xml Archive/
在 ksh93 中,也可以从子目录移动文件:
set -o globstar
mv **/!(deposit).xml Archive/
在 bash 中,您!(…)
也可以使用该模式,但您需要shopt -s extglob
先运行。要使用该**/
模式,您需要shopt -s globstar
先运行,并且要注意,直到 bash 4.2,**/
除了目录之外,还会遍历目录的符号链接。
shopt -s extglob globstar
mv **/!(deposit).xml Archive/
!(…)
在 zsh 中,您可以在运行后使用该模式setopt ksh_glob
。或者,您可以在运行后使用 zsh 的语法setopt extended_glob
。无论哪种方式都可以使用递归遍历语法。
setopt extglob
mv **/(^deposit).xml Archive/
答案2
使用“查找”命令:
find ./*.xml -type f ! -name "deposit.xml" -exec mv {} ./Archive ';'
将“find”与“xargs”命令一起使用:
find ./*.xml -type f ! -name "deposit.xml" | xargs -I {} mv {} ./Archive
使用“for”循环和“find”命令。
for f in $(find ./*.xml -type f ! -name "deposit.xml"); do mv ${f} ./Archive; done
另外,根据GNU/Linux 系统的联机帮助页,“mv”命令具有“-t | --target-directory”选项。所以你应该能够将它与“xargs”一起使用,如下所示:
find ./*.xml -type f ! -name "deposit.xml" | xargs mv -t ./Archive
我还没有测试过,因为我使用的是 FreeBSD 系统。
答案3
问题是您不想显示错误消息,或者您不想在没有 .xml 的情况下运行该命令?
在第一种情况下重定向输出2>/dev/null
并且不会显示错误消息。
在第二种情况下运行
for i in $(find ./*.xml '!' -type d | fgrep -v deposit.xml);do mv $i ./Archive;done
答案4
find . ! -name "deposit.xml" -name "*.xml" -exec mv {} ./temp \;
find 有 -exec 选项,使用它我们可以使用结果文件作为操作数执行命令并使用 '! -name' 选项来排除文件。