将 `for file in` 转换为 `find`,以便我的脚本可以递归应用

arv*_*vil 7 debian find shell-script recursive for

我有一个想法,即运行 bash 脚本来检查某些条件并使用ffmpeg将我目录中的所有视频从任何格式转换为.mkv它,它运行良好!

问题是,我不知道for file in循环不能递归地工作(/sf/ask/324721211/

但我几乎不理解“管道”,我期待看到一个例子并清除一些不确定性。

我想到了这个场景,我认为这对我的理解有很大帮助。

假设我有这个 bash 脚本片段:

for file in *.mkv *avi *mp4 *flv *ogg *mov; do
target="${file%.*}.mkv"
    ffmpeg -i "$file" "$target" && rm -rf "$file"
done
Run Code Online (Sandbox Code Playgroud)

它的作用是,对于当前目录,搜索任何*.mkv *avi *mp4 *flv *ogg *mov然后声明输出具有其扩展名,.mkv然后删除原始文件,然后输出应保存到原始视频所在的同一文件夹中。

  1. 如何将其转换为递归运行?如果我使用find,在哪里声明变量$file?你应该在哪里申报$target?都是find真的单行吗?我真的需要将文件传递给一个变量$file,因为我仍然需要运行条件检查。

  2. 并且,假设(1)成功,如何确保满足“然后输出应保存到原始视频所在的同一文件夹”的要求?

cuo*_*glm 6

使用 POSIX 查找:

find . \( -name '*.mkv' -o -name '*avi' -o -name '*mp4' -o -name '*flv' -o \
          -name '*ogg' -o -name '*mov' \) -exec sh -c '
  for file do
    target="${file%.*}.mkv"
    echo ffmpeg -i "$file" "$target"
  done' sh {} +
Run Code Online (Sandbox Code Playgroud)

替换echo为您要使用的任何命令。

如果你有 GNU find 或 BSD find,你可以使用-regex

find . -regex '.*\.\(mkv\|avi\|mp4\|flv\|ogg\|mov\)'
Run Code Online (Sandbox Code Playgroud)


roa*_*ima 5

你有这个代码:

for file in *.mkv *avi *mp4 *flv *ogg *mov; do
target="${file%.*}.mkv"
    ffmpeg -i "$file" "$target" && rm -rf "$file"
done
Run Code Online (Sandbox Code Playgroud)

它在当前目录中运行。要将其转换为递归过程,您有几个选择。最简单的(IMO)是find按照您的建议使用。for 的语法find非常“非 UNIX-like”,但这里的原则是每个参数都可以与 AND 或 OR 条件一起应用。在这里,我们要说“如果这个文件名匹配或者那个文件名匹配那么打印它”。文件名模式被引用,以便 shell 无法获取它们(请记住,shell 负责扩展所有未引用的模式,所以如果您有一个未引用的模式,*.mp4并且您janeeyre.mp4在当前目录中有一个,shell 将替换*.mp4为匹配,并且find会看到-name janeeyre.mp4而不是您想要的-name *.mp4;如果*.mp4匹配多个名称...)。括号以 为前缀,\以防止 shell 尝试将它们作为子 shell 标记(如果愿意,我们可以引用括号代替:)'('

find . \( -name '*.mkv' -o -name '*avi' -o -name '*mp4' -o -name '*flv' -o -name '*ogg' -o -name '*mov' \) -print
Run Code Online (Sandbox Code Playgroud)

需要将其输出输入到while循环的输入中,该循环依次处理每个文件:

while IFS= read file    ## IFS= prevents "read" stripping whitespace
do
    target="${file%.*}.mkv"
    ffmpeg -i "$file" "$target" && rm -rf "$file"
done
Run Code Online (Sandbox Code Playgroud)

现在剩下的就是用管道将这两个部分连接在一起,|以便 的输出find成为while循环的输入。

在您测试此代码时,我建议您在ffmpeg和 前rm加上前缀,echo以便您可以查看执行的内容 - 以及使用哪些路径。

这是最终结果,包括echo我推荐用于测试的语句:

find . \( -name '*.mkv' -o -name '*avi' -o -name '*mp4' -o -name '*flv' -o -name '*ogg' -o -name '*mov' \) -print |
    while IFS= read file    ## IFS= prevents "read" stripping whitespace
        do
            target="${file%.*}.mkv"
            echo ffmpeg -i "$file" "$target" && echo rm -rf "$file"
        done
Run Code Online (Sandbox Code Playgroud)

  • @TheWolf:如果 `xargs` 和 `find` 没有 `-0` 选项,你仍然有问题。让 `find` 使用 `-exec` 完成所有工作是更好的解决方案。 (2认同)