您的 find 命令对您的 tarball 没有任何用处,因为它在当前目录中搜索和删除,而不是在 tarball 中。
所以我们需要先解压压缩包(tar -xf),删除空文件(find),然后重新打包(tar -czf)。作为一项安全措施,我们将在临时目录 (mktemp -d) 中工作并创建新的 tarball (*.tar.gz.new) 而不是覆盖旧的。由于您只想删除yang 空文件,我们还将使用更多查找选项。以下是针对 GNU tar 的,适配自己的 tar 版本(或者安装 GNU tar)。在使用它之前阅读接下来会发生什么,以防万一......
for f in *.tar.gz; do
echo "Processing file $f"
d="$(mktemp -d)"
tar -xf "$f" -C "$d"
find "$d" -type f -name '*.yang' -size 0 -print -delete
tar -C "$d" -czf "$f.new" .
rm -rf "$d"
echo "******************************************"
done
但是您想要的比看起来更复杂,因为您的 tarball 可能包含您不允许使用的元数据(所有者、权限...)的文件。如果您以普通用户身份运行之前的内容, tar 将默默地更改此类文件和目录的所有权和权限。因此,在重新打包时,它们将具有修改的元数据。如果这是一个问题,并且您绝对想保留元数据,则基本上有两种选择:
- 使用
fakeroot 或等效项假装您是root。
- 无需解压即可删除 tarball 中的文件。
要使用 fakeroot,只需在 fakeroot 环境中运行上述 bash 脚本:
$ fakeroot
# for f in *.tar.gz; do
# ...
# done
# exit
第二种解决方案(就地 tarball 版本)使用 GNU tar 和 GNU awk:
for f in *.tar.gz; do
echo "Processing file $f"
t="${f%.*}"
gzip -cd "$f" > "$t"
tar -tvf "$t" | awk -vORS=$"\0" '/^-.*\.yang$/ && $3==0 {
match($0,/(\S+\s+){4}\S+\s/); print substr($0,RLENGTH+1)}' |
xargs -0 -n1 tar -f "$t" --delete
gzip -c9 "$t" > "$f.new"
echo "******************************************"
done
解释:
我们使用 GNU tar --delete 选项直接删除 tarball 中的文件,无需解压,这可能更优雅(即使它也可能比基于 fakeroot 的解决方案慢)。
让我们先找到压缩包中的所有空文件:
$ tar -tvf foo.tar
drwx------ john/users 0 2021-10-18 14:26 ./
drwx------ john/users 0 2021-10-18 16:34 ./
-rw------- john/users 0 2021-10-18 16:34 ./nonyang
drwx------ john/users 0 2021-10-18 15:22 ./foo.yang/
-rw------- john/users 0 2021-10-18 16:01 ./empty.yang
-rw------- john/users 7 2021-10-18 15:22 ./nonempty.yang
-rw------- john/users 0 2021-10-18 16:01 ./filename with spaces.yang
如您所见,尺寸在第三列。目录名称有一个前导 d 和一个尾随 /。符号链接有一个前导 l。因此,通过只保留以- 开头并以.yang 结尾的行,我们可以消除它们。 GNU awk 可以进行这种双重过滤:
$ tar -tvf foo.tar | awk '/^-.*\.yang$/ && $3==0 {print}'
-rw------- john/users 0 2021-10-18 16:01 ./empty.yang
-rw------- john/users 0 2021-10-18 16:01 ./filename with spaces.yang
这比我们想要的要多,所以让我们只打印名称部分。我们首先使用match 函数(设置一个名为RLENGTH 的变量)测量前5 个字段的长度,包括空格,然后使用substr 删除它们:
$ tar -tvf foo.tar | awk '/^-.*\.yang$/ && $3==0 {
match($0,/(\S+\s+){4}\S+\s/); print substr($0,RLENGTH+1)}'
./empty.yang
./filename with spaces.yang
我们可以尝试通过仅在第一行调用match 来进行一些优化,但我不能 100% 确定所有输出行都完全对齐,所以让我们在每一行调用它。
我们差不多完成了:只需将其传递给tar -f foo.tar --delete <filename>,一次一个名字。 xargs 可以为我们做到这一点,但还有最后一个技巧:由于文件名可以包含空格,我们必须使用另一个分隔符,这是在文件名中找不到的东西,例如 NUL 字符(ASCII 代码 0)。幸运的是 GNU awk 可以使用 NUL 作为输出记录分隔符 (ORS) 并且 xargs 具有 -0 选项来使用它作为输入分隔符。所以,让我们把所有这些放在一起:
$ tar -tvf foo.tar | awk -vORS=$"\0" '/^-.*\.yang$/ && $3==0 {
match($0,/(\S+\s+){4}\S+\s/); print substr($0,RLENGTH+1)}' |
xargs -0 -n1 tar -f foo.tar --delete
$ tar -tvf foo.tar
drwx------ john/users 0 2021-10-18 16:34 ./
-rw------- john/users 0 2021-10-18 16:34 ./nonyang
drwx------ john/users 0 2021-10-18 15:22 ./foo.yang/
-rw------- john/users 7 2021-10-18 15:22 ./nonempty.yang
在您的 for 循环内:
for f in *.tar.gz; do
echo "Processing file $f"
t="${f%.*}"
gzip -cd "$f" > "$t"
tar -tvf "$t" | awk -vORS=$"\0" '/^-.*\.yang$/ && $3==0 {
match($0,/(\S+\s+){4}\S+\s/); print substr($0,RLENGTH+1)}' |
xargs -0 -n1 tar -f "$t" --delete
gzip -c9 "$t" > "$f.new"
echo "******************************************"
done
请注意,我们必须在编辑之前解压缩 tarball,因为 GNU tar 无法编辑压缩的 tarball。