![相当于 unzip 中的 tar 的“--strip-components=1”?](https://linux22.com/image/1342500/%E7%9B%B8%E5%BD%93%E4%BA%8E%20unzip%20%E4%B8%AD%E7%9A%84%20tar%20%E7%9A%84%E2%80%9C--strip-components%3D1%E2%80%9D%EF%BC%9F.png)
我有一个脚本,可以将 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 传递
OPTIONS
给cp
。有用的选项包括:-v|--verbose
unzip
:输出每个复制的文件,就像-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