比较旧目录和新目录并重新压缩更改的文件
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
文件(old
与 new
目录)的文件大小就足够了。如果它已更改,则必须对其进行压缩。有时文件会被删除或生成不在 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 --bar
或 parallel --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' _ {} +
如果其输出看起来不错,请删除 echo
s。
我建议删除 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 {} +
我 运行 一个生成静态网页的夜间工作。新文件存储在目录 new
中,旧文件移动到目录 old
中。生成页面后我 运行:
find new -type f -name "*.html" -exec zopfli {} \;
zopfli
提供更好的 gzip 压缩结果,但更 CPU 密集。因为我们谈论的是大约 30,000 页,所以这是一个问题。全部压缩需要6个小时
幸运的是,只有不到 1% 的静态页面真正发生了变化。所以我不必重新压缩它们。我可以将旧的 .gz
文件从 old
目录移动到 new
目录。要查看文件是否已更改,比较 .html
文件(old
与 new
目录)的文件大小就足够了。如果它已更改,则必须对其进行压缩。有时文件会被删除或生成不在 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 --bar
或 parallel --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' _ {} +
如果其输出看起来不错,请删除 echo
s。
我建议删除 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 {} +