while 循环 - done < 命令而不是 done < 文件

kev*_*öze 4 bash

为了将子文件夹中的所有文件移动到当前文件夹中,我使用了这个脚本

while read f
do
    mv "$f" .
done < file_list
Run Code Online (Sandbox Code Playgroud)

这个伟大的工程,但我不得不产生file_list

find . -name *.avi > file_list
Run Code Online (Sandbox Code Playgroud)

我想要的是将命令直接添加到我的 while 循环中

while read f
do
    mv "$f" .
done < find . -name *.avi
Run Code Online (Sandbox Code Playgroud)

但是 bash 告诉我 -bash: syntax error near unexpected token `.'

将 find 命令通过管道传输到我的 while 循环中的简单解决方案是什么?

ter*_*don 20

您在这里不需要任何循环,find可以为您完成:

find . -name '*.avi' -exec mv {} . \;
Run Code Online (Sandbox Code Playgroud)


Prv*_*dav 8

您还可以使用进程替换。

while IFS= read -r f
do
    mv -- "$f" .
done < <(find . -name '*.avi' )
Run Code Online (Sandbox Code Playgroud)

在像 一样的 shell 中bash,与管道方法相比,它的优点是不在子 shell 中运行循环,因此您在循环中执行的变量分配不会在之后丢失。在bash(或zsh)中,您宁愿这样做:

while IFS= read <&3 -rd '' f 
do
    mv -- "$f" .
done 3< <(find . -name '*.avi' -print0)
Run Code Online (Sandbox Code Playgroud)

使用 NUL 分隔符能够处理任意文件名,使用 fd 3 而不是 0mv可能会提示用户并读取标准输入上的答案,find如果您使用 0 ,这将是输出。

或者使用find命令本身

find . -name '*.avi' -exec mv -t . {} +
Run Code Online (Sandbox Code Playgroud)

使用+意味着我们一次传递许多参数,mv从而不必为mv每个文件运行一次调用。-t是一个 GNU 扩展。对于其他mv实现,您可以将其更改为:

find . -name '*.avi' -exec sh -c 'exec mv "$@" .' sh {} +
Run Code Online (Sandbox Code Playgroud)


Arc*_*mar 7

正确的方法是管道

find . -name '*.avi' |
while read f
do
    mv "$f" .
done 
Run Code Online (Sandbox Code Playgroud)

第一个命令“ find . -name *.avi”的结果将提供给第二个命令。

这是调用管道(来自符号|)。您可以将管道视为临时文件,例如

find . -name '*.avi' > file1.tmp
while read f
do
    mv "$f" .
done < file1.tmp
rm file1.tmp
Run Code Online (Sandbox Code Playgroud)

正如指出的带有空格或换行符的文件名可能会导致错误。

如果唯一的目的是移动文件,请使用@Terdon 的命令。

此外,您必须引用,*.avi否则它会在第二次运行时中断。

find: paths must precede expression: `foo.avi' 
find: possible unquoted pattern after predicate `-name'?
Run Code Online (Sandbox Code Playgroud)

  • 好吧,正确的方法是使用 `-print0` 和 `while IFS= read -r -d ''; do...` 没有这些,循环将在带有换行符的文件名上失败。 (4认同)
  • 真的。但是,如果您以 _proper_ 的方式称呼它,则最好保持安全:) (4认同)