相当于 unzip 中的 tar 的“--strip-components=1”?

相当于 unzip 中的 tar 的“--strip-components=1”?

我有一个脚本,可以将 tar.gz 文件提取到指定的子目录我的子文件夹

mkdir mysubfolder; tar --extract --file=sourcefile.tar.gz --strip-components=1 --directory=mysubfolder;

有没有与 zip 文件执行此操作等效的方法?

答案1

正如 Mathias 所说,unzip没有这样的选项。但是一行 bash 脚本可以完成这项工作。

问题是:最佳方法取决于您的档案布局。解决方案是假设如果内容直接位于存档根目录中,则单个顶级目录将彻底失败:考虑一个包含/a/foo、、/b/foo的存档,以及剥离和/foo的混乱。/a/b

同样的失败也发生在 上tar --strip-component。没有一劳永逸的解决方案。

因此,要剥离根目录,假设一个(和仅有的一):

unzip -d "$dest" "$zip" && f=("$dest"/*) && mv "$dest"/*/* "$dest" && rmdir "${f[@]}"

只要确保二级文件/目录不是与顶级父级具有相同的名称(例如/foo/foo)。但是/foo/bar/foo/foo/bar/bar是可以的。如果它们确实如此,或者您只是想安全起见,您可以使用临时目录进行提取:

temp=$(mktemp -d) && unzip -d "$temp" "$zip" && mkdir -p "$dest" &&
mv "$temp"/*/* "$dest" && rmdir "$temp"/* "$temp"

如果您使用 Bash,您可以使用以下命令测试顶层是否为单个目录:

f=("$temp"/*); (( ${#f[@]} == 1 )) && [[ -d "${f[0]}" ]] && echo "Single dir!"

说到 Bash,您应该打开dotglob包含隐藏文件,然后您可以将所有内容包装在一个方便的函数中:

# unzip featuring an enhanced version of tar's --strip-components=1
# Usage: unzip-strip ARCHIVE [DESTDIR] [EXTRA_cp_OPTIONS]
# Derive DESTDIR to current dir and archive filename or toplevel dir
unzip-strip() (
    set -eu
    local archive=$1
    local destdir=${2:-}
    shift; shift || :
    local tmpdir=$(mktemp -d)
    trap 'rm -rf -- "$tmpdir"' EXIT
    unzip -qd "$tmpdir" -- "$archive"
    shopt -s dotglob
    local files=("$tmpdir"/*) name i=1
    if (( ${#files[@]} == 1 )) && [[ -d "${files[0]}" ]]; then
        name=$(basename "${files[0]}")
        files=("$tmpdir"/*/*)
    else
        name=$(basename "$archive"); name=${archive%.*}
        files=("$tmpdir"/*)
    fi
    if [[ -z "$destdir" ]]; then
        destdir=./"$name"
    fi
    while [[ -f "$destdir" ]]; do destdir=${destdir}-$((i++)); done
    mkdir -p "$destdir"
    cp -ar "$@" -t "$destdir" -- "${files[@]}"
)

现在将它放入你的~/.bashrc,你就再也不用担心它了。只需使用:

unzip-strip sourcefile.zip [mysubfolder] [OPTIONS]

这个小野兽将会:

  • mysubfolder如果不存在则为您创建
  • 自动检测您的 zip 存档是否包含单个顶级目录并相应地处理提取。
  • mysubfolder选修的。如果为空,它将提取到当前的目录(不一定是档案目录!),以以下名称命名:
    • 档案中的单个顶级目录(如果有)
    • 或存档文件名,不带(可能.zip)扩展名
  • 如果给定或派生的目标路径已经存在作为文件,增加名称直到找到合适的名称(新路径或现有目录)。
  • 默认情况下这将:
    • 安静
    • 覆盖所有现有文件
    • 保留链接和属性(模式、时间戳等)
  • 您可以将 extra 传递OPTIONScp。有用的选项包括:
    • -v|--verboseunzip:输出每个复制的文件,就像
    • -n|--no-clobber:不覆盖现有文件
    • -u|--update:仅覆盖比目标更新的文件
  • 这些额外的内容OPTIONS是第 3 个及以上参数。如果需要,请制作适当的参数解析器。
  • 它将使用双倍的由于采用“提取到临时目录并复制”方法,因此在操作过程中会占用提取的磁盘空间。没有办法解决这个问题,否则会损失部分灵活性/功能。

答案2

您可以使用-j垃圾路径(不创建目录)。这只推荐用于比较常见的单级档案。具有多级目录结构的档案将被扁平化 - 这甚至可能导致要提取的文件的名称冲突。

从 unzip 的手册页中:

   -j     junk  paths.   The  archive's directory structure is not recreated; all files are deposited in the
          extraction directory (by default, the current one).

答案3

正如其他人所指出的,unzip 不支持此功能。但是,bsdtar 也可以提取 zip 文件。

bsdtar xvf app.zip --strip-components=1 -C /opt/some-app

bsdtar 还可以提取流(unzip 不支持)。

curl -sSL https://... | bsdtar xvf - --strip-components=1 -C /opt/some-app

答案4

我找不到这样的选项手册页unzip,所以恐怕这是不可能的。:(

但是,(视情况而定)您可以解决这个问题。例如,如果您确定 zip 文件中唯一的顶级目录名称foo-后跟版本号,则可以执行以下操作:

cd /tmp
unzip /path/to/file.zip
cd foo-*
cp -r . /path/to/destination/folder

相关内容