如何在Linux中将“find”的结果导入“mv”命令进行文件移动

62

我该如何将 Linux 中“查找”命令的结果管道传输到不同的目录中?这是我目前的进展。

find ./ -name '*article*' | mv ../backup

但还不正确(我收到一个错误,缺少文件参数,因为我没有指定文件,因为我试图从管道中获取它)


好的,它不是完全重复,但也有一个答案 :) - xxxvodnikxxx
6个回答

92
find ./ -name '*article*' -exec mv {}  ../backup  \;

或者

find ./ -name '*article*' | xargs -I '{}' mv {} ../backup

当我运行你的第二个命令时,它显示 xargs: invalid option -- I。这里出了什么问题?我看在 man 页面上它是一个有效的选项。 - user1015214
@user1015214 - 这个页面上的四个命令都没有起作用吗? - Amit
如果有帮助的话,我在Windows机器上的git Bash中。它似乎正在运行Linux命令,但我猜它没有完整的库? - user1015214
@user1015214 - 可能git bash有限制。由于您使用的是Windows机器,我建议在Powershell中使用Move-Item Cmdlet。您可以执行Move-Item *article* \full\path\to\backup - Amit

43

xargs 常用于此操作,而 Linux 上的 mv 命令有一个 -t 选项以方便操作。

find ./ -name '*article*' | xargs mv -t ../backup

如果你的find支持-exec ... +而不是-exec ... \;,那么你可以等效地执行以下操作:
find ./ -name '*article*' -exec mv -t ../backup {} +

-t选项是GNU扩展,因此在没有GNU coreutils的系统上不可移植(尽管我见过的每个正规Linux都有该扩展,可能除了Busybox)。为了完全符合POSIX可移植性,当然可以自己编写替代方法,比如:

find ./ -name '*article*' -exec sh -c 'mv "$@" "$0"' ../backup {} +

在这里,我们无耻地滥用了方便的事实,即在sh -c 'commands'之后的第一个参数最终成为$0中的“脚本名称”参数,因此我们甚至不需要进行shift操作。

可能还可以参考https://mywiki.wooledge.org/BashFAQ/020


当我尝试了你的第一个建议时,它显示“无效选项--t”。当我尝试了你的第二个建议时,它显示“-exec命令未找到”,所以我猜它不被支持。 - user1015214
听起来你并不是在Linux上,因为GNU版本的这些工具已经支持很长时间了。我找不到一个不支持-execfind命令,可能是你输错了命令? - tripleee
实际上,Busybox 也支持 mv -t - tripleee
请注意,如果您使用此方法,您的目录名称中不能包含空格。 - exclowd
@exclowd find -exec 变体特别强大,可以应对各种与不寻常文件名相关的问题。xargs 变体稍微弱一些,但链接的常见问题解答中有更复杂的变体,需要 GNU find(基本上只适用于 Linux)。简而言之,find ... -print0 | xargs -r0 mv -t ../backup - tripleee

3

我发现在一个文件夹中有数千个文件非常有用:

ls -U | head -10000 | egrep '\.png$' | xargs -I '{}' mv {} ./png

将前1万个文件中的所有png文件移动到名为“png”的子文件夹中。

1
正则表达式没有 egrep(也称为更现代的 grep -E 语法),而且你可以将 head 分解出来:grep -m 10000 '\.png$'。更一般地说,你应该避免在脚本中使用 ls - tripleee
也许可以使用 printf '%s\0' *.png | xargs -r0 mv -t png/ 来避免使用 ls 时出现的各种问题,以及使用 printf '%s\0' *.png | grep -z -m 10000 ^ | xargs -r0 mv -t png/ 来巧妙地使用 grep -z -m items 作为 head 的替代品,因为它可以处理空字符终止的输入。我之前发布的链接详细解释了这些内容。我对于改进一个提倡使用 ls 的答案持有不同意见;但是请随意更新,从这些评论中包含任何或所有信息。顺便说一下,xargs -0 只在 GNU 系统上可用(应该可以在 Linux 上工作,但不是完全可移植的)。 - tripleee

0

这里有几个解决方案。

find . -type f -newermt "2019-01-01" ! -newermt "2019-05-01" \
    -exec mv {} path \;**

或者

find path -type f -newermt "2019-01-01" ! -newermt "2019-05-01" \
    -exec mv {} path \;

或者

find /Directory/filebox/ -type f -newermt "2019-01-01" \
    ! -newermt "2019-05-01" -exec mv {} ../filemove/ \;

反斜杠 + 换行符只是为了可读性;您可以使用单行代码代替。

0
mv $(find . -name '*article*') ../backup

1
这个命令不正确。它显示“缺少文件操作数”(missing file operand)。(我在../backup之前使用了-t来定义目标目录) - Farbod Shahinfar
它对我有效。你的备份目录应该放回你的搜索目录。 - Vencat
2
这容易生成无效的文件名。用命令替换生成源文件名列表本质上是有问题的。如果你在简单的情况下测试它,它看起来像是工作的,但如果你的文件名中有空格或shell元字符,它会出现严重的错误。https://mywiki.wooledge.org/BashPitfalls 上的几个条目都是这个常见问题的变体。 - tripleee

-1

xargs 是你的好帮手(当你有多个操作要执行时)!

而且按照我展示的方式使用它,也会给你带来很好的控制。

find ./ -name '*article*' | xargs -n1 sh -c "mv {}  <path/to/target/directory>"

说明:

  1. -n1

每个操作向前考虑的行数

  1. sh -c

要执行的 shell 命令,根据先前的条件给出相应的行

  1. "mv {} /target/path"

移动命令将使用两个参数-

1)来自操作 1 的行,即 {},值会自动替换

2)移动命令的目标路径,如指定

注意:为了允许 shell 命令接收来自 xargs 的参数中的任意数量的空格或参数,“双引号”被指定


1
为什么需要-n1sh -c包装器的目的是什么?与其他旧的类似答案相比,这只会带来缺点。 - tripleee
谢谢您的反馈。我已经使用这个语法有一段时间了。您能告诉我它的缺点吗?这只会帮助我更好地了解。 - nitinr708
1
如果你只循环一个参数,那么sh -c是无用的。另一方面,-n 1基本上也使得xargs无用;如果你每次只移动一个文件,那就直接运行find -exec。然而,一次只处理一个文件会非常浪费资源;你应该尽量减少子进程的数量。之前的回答实现了各种优化以达到这个目标,有些更积极和稳健,有些则不然。 - tripleee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接