1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
import os
import argparse
import tarfile
import subprocess
import stat
def is_exe(fpath: str) -> bool:
return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
def _usage() -> str:
return "\n".join(
[
"Usage:",
f"`tar_directory.py [--tar-by-py] [--exclude fileOrDir0 ... --exclude fileOrDirN] archive.tar directory [skip prefix]`",
"or",
f"`tar_directory.py [--tar-by-py] --extract archive.tar output_directory`",
]
)
def main():
parser = argparse.ArgumentParser(description='Make or extract tar archive')
parser.add_argument('--extract', action="store_true", default=False, help="Extract archive")
parser.add_argument('--tar-by-py', action="store_true", default=False, help="Force use taring by python")
parser.add_argument(
'--exclude', type=str, action='append', default=[], help="Exclude for create archive (can use multiply times)"
)
parser.add_argument('archive', type=str, action='store', help="Archive name, for example, archive.tar") # required
parser.add_argument(
'directory', type=str, action='store', help="Directory name for create from or extract to"
) # required
parser.add_argument(
'prefix', type=str, nargs='?', action='store', help="Path prefix for skip before create archive"
) # dont't required, because nargs=?
args = parser.parse_args()
if args.extract and (args.exclude or args.prefix):
raise Exception(f"Illegal usage: {" ".join(args)}\n{_usage()}")
tar = args.archive
directory = args.directory
prefix = args.prefix
if args.extract:
dest = os.path.abspath(directory)
if not os.path.exists(dest):
os.makedirs(dest)
for tar_exe in ('/usr/bin/tar', '/bin/tar') if not args.tar_by_py else []:
if not is_exe(tar_exe):
continue
if args.extract:
command = [tar_exe, '-xf', tar, '-C', dest]
else:
source = os.path.relpath(directory, prefix) if prefix else directory
command = (
[tar_exe]
+ (['--exclude=' + exclude for exclude in args.exclude] if args.exclude else [])
+ ['-cf', tar]
+ (['-C', prefix] if prefix else [])
+ [source]
)
subprocess.run(command, check=True)
break
else:
if args.extract:
with tarfile.open(tar, 'r') as tar_file:
tar_file.extractall(dest)
else:
source = directory
with tarfile.open(tar, 'w') as out:
def filter(tarinfo: tarfile.TarInfo):
if args.exclude:
for exclude in args.exclude:
if tarinfo.name.endswith(exclude):
return None
tarinfo.mode = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH if tarinfo.mode | stat.S_IXUSR else 0
tarinfo.mode = (
tarinfo.mode | stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH
)
# Set mtime to 1970-01-01 00:00:01, else if mtime == 0
# it not used here https://a.yandex-team.ru/arcadia/contrib/python/python-libarchive/py3/libarchive/__init__.py#L355
# But mtime != 0 also ignored by libarchive too :((
tarinfo.mtime = 1
tarinfo.uid = 0
tarinfo.gid = 0
tarinfo.uname = 'dummy'
tarinfo.gname = 'dummy'
return tarinfo
out.add(
os.path.abspath(source),
arcname=os.path.relpath(source, prefix) if prefix else source,
filter=filter,
)
if __name__ == '__main__':
main()
|