比较旧目录和新目录并重新压缩更改的文件

Compare old vs new directory and recompress changed files

我 运行 一个生成静态网页的夜间工作。新文件存储在目录 new 中,旧文件移动到目录 old 中。生成页面后我 运行:

find new -type f -name "*.html" -exec zopfli {} \;

zopfli 提供更好的 gzip 压缩结果,但更 CPU 密集。因为我们谈论的是大约 30,000 页,所以这是一个问题。全部压缩需要6个小时

幸运的是,只有不到 1% 的静态页面真正发生了变化。所以我不必重新压缩它们。我可以将旧的 .gz 文件从 old 目录移动到 new 目录。要查看文件是否已更改,比较 .html 文件(oldnew 目录)的文件大小就足够了。如果它已更改,则必须对其进行压缩。有时文件会被删除或生成不在 old 目录中的新文件。

├── new
│   ├── a
│   │   ├── test.html
│   │   └── test2.html
│   ├── b
│   │   └── test.html
│   └── index.html
└── old
    ├── a
    │   ├── test2.html
    │   └── test2.html.gz
    ├── b
    │   ├── test.html
    │   └── test.html.gz
    ├── index.html
    └── index.html.gz

我可以用 Ruby 或 Perl 来做到这一点。但我想用 Bash shell 脚本来完成。那可能吗?我该怎么做?

考虑使用 GNU Parallel 并行压缩:

find new -type f -name "*.html" -print0 | parallel -0 zopfli

默认情况下,它会为每个 CPU 个核心 运行 一个作业,但您可以将 parallel -j 8 更改为 运行,例如 8 个作业并行

使用 parallel --barparallel --eta 作为进度条或 "Estimated Time of Arrival".

考虑将新旧文件系统放在不同的磁盘上,以减少争用。

使用cmp比较文件:

find new -name '*.html' -exec sh -c '
  for f; do
    if cmp -s "$f" "old/${f#*/}"; then
      echo cp "old/${f#*/}.gz" "${f%/*}"
    else
      echo zopfli "$f"
    fi
  done' _ {} +

如果其输出看起来不错,请删除 echos。

我建议删除 old 目录并使用修改时间戳找到要压缩的新文件。你可以使用 make:

# Makefile
.SUFFIXES:
htmls=$(shell find new -type f -name '*.html')
gzips=${htmls:%.html=%.html.gz}
all: ${gzips} cleanup
%.html.gz: %.html
        echo zopfli $^
cleanup:
        @# find all .html.gz files without .html files and remove them
        find new -type f -name '*.html.gz' \
        -exec bash -c '[[ ! -e "${1%.gz}" ]]' _ {} \; \
        -delete
        # or -exec sh -c '[ ! -e "$(basename "" .gz)" ]'

对于 find 那将是:

find new -type f -name '*.html' -exec bash -c '[[ ! -e "".gz || "" -nt "".gz ]]' _ {} \; -exec echo zopfli {} +