Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python3
- # File: tar.py
- # SPDX-License-Identifier: Unlicense
- # This is free and unencumbered software released into the public domain.
- #
- # Anyone is free to copy, modify, publish, use, compile, sell, or
- # distribute this software, either in source code form or as a compiled
- # binary, for any purpose, commercial or non-commercial, and by any
- # means.
- # Tectonics:
- # $ black tar.py
- import argparse
- import shutil
- import pathlib
- import subprocess
- import sys
- VERSION = "1.0.0"
- def main(prog, args, version):
- """Archives src_dirs to dest_tar."""
- opts = parse_cmdline(prog, args, version)
- cmd_args = tar_cmd(opts)
- kwargs = {"check": True, "text": True}
- if opts.get("combine_output", False):
- kwargs["stderr"] = subprocess.STDOUT
- subprocess.run(cmd_args, **kwargs)
- def parse_cmdline(prog, args, version):
- """Parses the command line aruments in args.
- Shows a help message if help is requested, or the program version if
- version is requested. In either of these cases the program terminates.
- """
- DEST = "DEST_TAR"
- parser = argparse.ArgumentParser(
- description=f"Archives the source files/directories to {DEST}.",
- epilog=f"""The extension of the {DEST} file name is used
- to determine how to compress the destination tar file.
- A parallel compression tool will be used if possible.""",
- formatter_class=argparse.ArgumentDefaultsHelpFormatter,
- allow_abbrev=False,
- )
- # source option
- parser.add_argument(
- "-s",
- "--src",
- action="extend",
- nargs="+",
- required=True,
- help="files/directoriess to archive",
- )
- # destination option
- parser.add_argument(
- "-d",
- "--dest",
- help="name of the destination tar file",
- metavar=f"{DEST}",
- )
- # mode option
- parser.add_argument(
- "-m",
- "--mode",
- default="create",
- choices=["create", "append", "update"],
- help="mode used for writing the tar file",
- )
- # nice option
- parser.add_argument(
- "-n",
- "--nice",
- nargs="?",
- const=10,
- default=0,
- type=int,
- help="""niceness factor (10 if specified with no value);
- values range from -20 (most favourable) to 19
- (least favourable)""",
- )
- # combine-output option
- parser.add_argument(
- "-c",
- "--combine-output",
- action="store_true",
- default=False,
- help="redirect error messages to stdout",
- dest="combine_output",
- )
- # verbose option
- parser.add_argument(
- "-v",
- "--verbose",
- action="store_true",
- default=False,
- help="verbosely list files processed",
- )
- # version option
- parser.add_argument("--version", action="version", version=str(version))
- # additional tar arguments
- parser.add_argument(
- "tar-opts",
- action="extend",
- nargs="*",
- help="additional arguments to pass to tar",
- )
- result = vars(parser.parse_args(args))
- if result["nice"]:
- if result["nice"] < -20:
- result["nice"] = -20
- elif result["nice"] > 19:
- result["nice"] = 19
- return result
- def tar_cmd(args):
- """Returns the tar process to be be run as a list of strings."""
- # the result list either starts with tar,
- # or with "nice -n <val> tar"
- result = []
- if "nice" in args and args["nice"] != 0:
- result = [which_or_name("nice"), "-n", str(args["nice"])]
- # add standard tar options
- result.extend([which_or_name("tar"), f"--{args['mode']}", "--recursion"])
- # add parallel compress program if parallel compression is possible
- compressor = compress_program(args["dest"])
- if compressor:
- result.extend(["--use-compress-program", compressor])
- else:
- result.append("--auto-compress")
- # verbose option
- if args.get("verbose", False):
- result.extend(["--verbose", "--totals"])
- # add permissions
- result.extend(["--preserve-permissions", "--atime-preserve"])
- # add exclusions
- for name in ["/boot", ".cache", "cache", "/dev", "/proc", "sys"]:
- result.extend(["--exclude", name])
- result.extend(["--exclude-caches-all", "--exclude", args["dest"]])
- # add tar-opts if they exist
- if "tar-opts" in args:
- result.extend(args["tar-opts"])
- # add src and dest
- # add --absolute-names option to silence warnings about absolute paths
- # result.append("--absolute-names")
- if args["dest"]:
- result.extend(["--file", args["dest"]])
- result.extend(args["src"])
- return result
- def compress_program(tar_file):
- """Returns the compress program that tar should use based on the
- name of the file that is going to be written to.
- """
- match pathlib.PurePath(tar_file).suffix.lower():
- # bzip2
- case ".bz" | ".bz2":
- cmd = shutil.which("pbzip")
- if cmd:
- return f"{cmd} --keep "
- cmd = shutil.which("lrzip")
- if cmd:
- return f"{cmd} -b "
- cmd = shutil.which("lbzip2")
- if cmd:
- return f"{cmd} --keep "
- # gzip
- case ".gz" | "gzip" | "tgz":
- cmd = shutil.which("pigz")
- if cmd:
- return f"{cmd} --keep "
- cmd = shutil.which("lrzip")
- if cmd:
- return f"{cmd} -g "
- cmd = shutil.which("crabz")
- if cmd:
- return f"{cmd} -g "
- # TODO lrz
- case ".lrz":
- pass
- # lz
- case "lz" | "tlz":
- cmd = shutil.which("plzip")
- if cmd:
- return f"{cmd} --keep "
- # TODO lz4 lempel-ziv
- case ".lz4":
- pass
- # lzo
- case ".lzo":
- cmd = shutil.which("lrzip")
- if cmd:
- return f"{cmd} -l "
- # TODO lzw
- case ".lzw":
- pass
- # lzma | xz
- case "lzma" | ".xz":
- cmd = shutil.which("pixz")
- if cmd:
- return f"{cmd}"
- cmd = shutil.which(pxz)
- if cmd:
- return f"{cmd} --keep "
- # TODO zip
- case ".zip":
- pass
- # zstd
- case ".zst" | ".zstd":
- cmd = shutil.which("zstd")
- if cmd:
- return f"{cmd} -T0 "
- def which_or_name(cmd):
- """Returns the fully qualified name of cmd. If not found then returns
- the original value."""
- path = shutil.which(cmd)
- return path if path != "" else cmd
- if __name__ == "__main__":
- main(sys.argv[0], sys.argv[1:], VERSION)
Advertisement