如何查找没有后缀版本的文件?

bfo*_*ine 5 bash find files

我有几百万个.jpg文件,我想.jpg.webp为每个文件生成一个版本 ( foo.jpg-> foo.jpg.webp)。为此,我需要找到所有.jpg以 There\xe2\x80\x99s 结尾的文件,没有.jpg.webp版本。

\n

现在,我这样做:

\n
find "$path" -type f -iname "*.jpg" |\n  while read -r image_path; do\n      if [ ! -f "$image_path.webp" ]; then\n        echo "$image_path"\n      fi\n  done |\n  # treat only 10000 files per run\n  head -n 10000 |\n  ...\n
Run Code Online (Sandbox Code Playgroud)\n

但是,因为 I\xe2\x80\x99m 使用管道,所以这会创建一个子 shell。我想知道是否有更有效的方法来做到这一点,特别是因为我生成的 WebP 图像越多,脚本花费在过滤路径来查找候选者上的时间就越多。是否有某种方法可以find仅使用来做到这一点?

\n

I\xe2\x80\x99m 使用 Ubuntu 20.04。文件分布在子目录中。

\n

Mar*_*ler 8

我会做以下事情:

  1. 查找所有后缀(即*.jpg.webp)文件,将它们放入排序列表中,删除后缀
  2. 查找所有不带后缀(即*.jpg)的文件,将它们放入第二个排序列表中
  3. 比较两个列表,删除第一个列表中的条目。
  4. 在由此产生的“设置差异”列表上操作您的转换。

所以,像

#!/bin/bash
comm -z -1 -3 \
   <(find -name '*.jpg.webp' -print0 | sed 's/\.webp\x0/\x0/g' | sort -z) \
   <(find -name '*.jpg'      -print0 | sort -z) \
| parallel -0 gm convert '{}' '{}.webp'
Run Code Online (Sandbox Code Playgroud)

假设您使用 GraphicsMagickgm进行转换(根据我的经验,速度和可靠性方面比 ImageMagick 更可取convert),并假设您parallel安装了 GNU(如果没有,xargs可能也可以工作)。

  • 这是因为你的“sh”可能是“bash”的符号链接,所以它不是“sh”,而是在 POSIX 模式下运行的“bash”,这不是实际 POSIX shell 的准确表示。Dash 因“语法错误:“(”意外”而失败。 (4认同)

cas*_*cas 7

尝试这样的事情:

find "$path" -type f -iname "*.jpg" -exec \
  sh -c 'for f; do [ -e "$f.webp" ] || echo "$f" ; done' find-sh {} +
Run Code Online (Sandbox Code Playgroud)

它会执行sh尽可能少的次数(取决于 find 找到的 .jpg 文件的数量),受到 ARG_MAX(Linux 上大约 200 万字节)的限制,并通过将while read ...所有文件名作为命令行参数传递来避免极其缓慢的循环。请参阅为什么使用 shell 循环处理文本被认为是不好的做法?为什么循环查找的输出是不好的做法?

为了有效地处理这些文件的批次,我会将输出重定向到一个文件,然后将其分成 10,000 个批次(或您需要的任意数量),例如使用split -l 10000.

注意:如果您的任何 .jpg 文件名包含换行符,那么您需要使用 NUL 作为它们之间的分隔符,否则使用换行符作为分隔符。要使用 NUL 分隔符,请替换echo "$f"printf "%s\0" "$f". 顺便说一句,split支持 NUL 分隔的输入-t '\0'

处理批次的脚本应读取文件名,并.jpg.webp在运行生成版本所需的任何内容之前再次检查相应的文件是否不存在(如果在生成列表后生成一个文件).jpg.webp

如果必须使用 NUL 作为文件名分隔符,那么最简单的方法是使用readarray(AKA mapfile)将整个批次的列表读入数组并迭代文件名数组。或者使用 awk 或 perl 来处理文件名。

实际上,即使使用换行符作为分隔符,使用数组也比 while-read 循环更好。


小智 6

这听起来像是一份工作make。它只会生成丢失的文件,或者修改时间比生成文件的文件早的文件。

.PHONY: all
all: $(addsuffix .webp,$(shell find . -name '*.jpg'))

%.jpg.webp: %.jpg
    cwebp $< -o $@   #Some command that generates $@ from $<
Run Code Online (Sandbox Code Playgroud)

将其保存到名为 的文件中Makefile,然后运行make
或者make -j $(nproc)运行与逻辑核心一样多的并行作业。或者选择一个明确的数字,也许是物理核心的数量,以留下一些空闲的逻辑核心用于其他工作。)

如果任何文件或子目录的名称中包含空格,这将会中断。

%.jpg.webp: %.jpg是一种模式规则

  • 我从未尝试过使用 Make 来扩展至数百万个单词的变量。可能是一个有趣的实验... (3认同)