以下 Python 脚本读取标准输入上的 tarball,并从其所有成员的 uid 和 gid 中减去 10000:
#!/usr/bin/env python3
import tarfile
import sys
with tarfile.open(fileobj=sys.stdin.buffer, mode="r|*") as in_tar, \
tarfile.open(fileobj=sys.stdout.buffer, mode="w|", format=tarfile.PAX_FORMAT) as out_tar:
for member in in_tar:
member.uid = member.uid-10000
member.gid = member.gid-10000
if member.isfile():
with in_tar.extractfile(member) as file:
out_tar.addfile(member, file)
else:
out_tar.addfile(member)
编辑:这篇文章的早期版本省略了format=tarfile.PAX_FORMAT 到tarfile.open() 的参数。这意味着输出 tarball 将不包含可能包含在 Python 3.7 及更早版本的输入 tarball 的 PAX 标头中的扩展属性。从 Python 3.8 开始,PAX_FORMAT 是默认值。
本文的其余部分留作历史用途。
这个 Python 脚本的问题是,tarball 模块不支持扩展属性(xattr、SCHILY),因此它不适合转换根文件系统的 tarball,这是想要转换 uid/gid 的主要原因数字。这是一个有效的 go 脚本:
package main
import (
"archive/tar"
"io"
"log"
"os"
)
func main() {
tr := tar.NewReader(os.Stdin)
tw := tar.NewWriter(os.Stdout)
for {
hdr, err := tr.Next()
if err == io.EOF {
break
} else if err != nil {
log.Fatal(err)
}
hdr.Uid -= 10000
hdr.Gid -= 10000
if err := tw.WriteHeader(hdr); err != nil {
log.Fatal(err)
}
if hdr.Typeflag == tar.TypeReg {
if _, err := io.Copy(tw, tr); err != nil {
log.Fatal(err)
}
}
}
if err := tw.Close(); err != nil {
log.Fatal(err)
}
}
使用go run script.go 运行它或使用go build script.go 编译它。