ark -ba <path>
我正在寻找(在 KDE 上)或file-roller -h <path>
(在 GNOME/Unity 上)的纯命令行对应物。
不幸的是,ark 和 file-roller 都需要运行 X。我知道编写一个根据文件扩展名检测档案的工具相对简单,然后运行相应的程序:
#!/bin/bash
if [[ -f "$1" ]] ; then
case $1 in
*.tar.bz2) tar xjvf $1 ;;
*.tar.gz) tar xzvf $1 ;;
*.bz2) bunzip2 $1 ;;
*.rar) rar x $1 ;;
*.gz) gunzip $1 ;;
*.tar) tar xf $1 ;;
*.tbz2) tar xjvf $1 ;;
*.tgz) tar xzvf $1 ;;
*.zip) unzip $1 ;;
*.Z) uncompress $1 ;;
*.7z) 7z x $1 ;;
*) echo "'$1' cannot be extracted with this utility" ;;
esac
else
echo "path '$1' does not exist or is not a file"
fi
但是,这并没有解决子目录检测的问题(事实上,许多提取程序甚至不提供这样的选项)。
那么是否存在一个可以实现这一目标的程序呢?
答案1
您所描述的“子目录检测”应该默认发生。在此示例中,使用 GNU tar
:
$ tree
.
├── dir1
│ └── file4
├── dir2
│ ├── file5
│ └── file6
├── file1
├── file2
└── file3
档案:
$ tar cvf all.tar *
$ mkdir new_dir
$ mv all.tar new_dir
$ cd new_dir
$ tar xvf all.tar
$ tree
.
├── all.tar
├── dir1
│ └── file4
├── dir2
│ ├── file5
│ └── file6
├── file1
├── file2
└── file3
如果您使用的存档程序在创建存档时不保留目录结构(顺便问一下,您确定吗?我不知道有哪个程序不这样做),那么信息就会丢失。除非目录结构已保存在存档本身中,否则无法重新创建目录结构,在这种情况下,默认情况下应在存档提取时重新创建目录结构。
如果你想模仿的行为ark -a
:
-a, --autosubfolder Archive contents will be read, and if detected
to not be a single folder archive, a subfolder
with the name of the archive will be created.
您可以创建一个包装器脚本,将档案提取到临时目录,然后如果临时目录仅包含另一个目录,则将该目录移动到当前工作目录并删除临时目录,如果临时目录中有多个文件/目录,则将其重命名为档案的名称。 类似这样的内容:
#!/usr/bin/env bash
for file in "$@"
do
## Get the file's extension
ext=${file##*.}
## Special case for compressed tar files. They sometimes
## have extensions like tar.bz2 or tar.gz etc.
if [[ "$(basename "$file" ."$ext")" =~ \.tar$ ]]; then
if [[ "$ext" = "gz" ]]; then
ext="tgz"
elif [[ "$ext" = "bz2" ]]; then
ext="tbz"
fi
fi
## Create the temp dir
tmpDir=$(mktemp -d XXXXXX);
case $ext in
7z)
7z -o "$tmpDir" e "$file"
;;
tar)
tar xf "$file" -C "$tmpDir"
;;
tbz)
tar xjf "$file" -C "$tmpDir"
;;
tgz)
tar xzf "$file" -C "$tmpDir"
;;
rar)
unrar e "$file" "$tmpDir"
;;
zip)
unzip "$file" -d "$tmpDir"
;;
*)
echo "Unknown extension: '$ext', skipping..."
;;
esac
## Get the tmp dir's structure
tmpContents=( "$tmpDir"/* )
c=1
## If the tmpDir contains only one item and that is a directory
if [[ ${#tmpContents[@]} = 1 ]] && [[ -d "${tmpContents[0]}" ]]
then
## Move that directory to the current working directory
## and delete the tmpDir, renaming it if a file/directory with
## the same name already exists.
dirName=${tmpContents[0]##*/}
[[ -e "$dirName" ]] && dirName="$dirName.$c"
while [[ -e "$dirName" ]]; do
((c++))
dirName="${dirName/.*}.$c"
done
mv "${tmpContents[0]}" "$dirName"
else
## If the tmpDir contains anything more than a single directory,
## rename thye tmpDir to the name of the archive sans extension.
## If a file/dir of that name already exists, add a counter.
dirName="${file##*/}" ## strip path
dirName="${dirName%%.*}" ## strip extension(s)
[[ -e "$dirName" ]] && dirName="$dirName.$c"
while [[ -e "$dirName" ]]; do
((c++))
dirName="${dirName/.*}.$c"
done
mv "$tmpDir" "$dirName"
fi
printf "Archive '%s' extracted to %s\n" "$file" "$dirName" >&2
done
答案2
答案3
这是 terdon on strike 的答案的更新。这解决了一些带有版本号的文件的问题,例如“VSCode-3.2.1.tar.gz”等。此外还增加了对 .tar.xz 存档的支持,并结合了所有压缩的 tar 选项,因为 -j 和 -z 等 tar 选项实际上仅用于创建存档,而不是提取(它会自动检测)。此外,在某些情况下,成功提取后不会删除临时目录,所以我也修复了这个问题。
#!/usr/bin/env bash
# https://superuser.com/questions/516873/commandline-program-to-extract-archives-with-automatic-subdirectry-detection
for file in "$@"
do
## Get the file's extension
ext=${file##*.}
## Special case for compressed tar files. They sometimes
## have extensions like tar.bz2 or tar.gz etc.
if [[ "$(basename "$file" ."$ext")" =~ \.tar$ ]]; then
if [[ "$ext" =~ ^(gz|bz2|xz|Z)$ ]]; then
ext="tar"
fi
fi
## Create the temp dir
tmpDir=$(mktemp -d XXXXXX);
case $ext in
7z)
7z -o "$tmpDir" e "$file"
;;
tar)
tar -xf "$file" -C "$tmpDir"
;;
rar)
unrar e "$file" "$tmpDir"
;;
zip)
unzip "$file" -d "$tmpDir"
;;
*)
echo "Unknown extension: '$ext', skipping..."
;;
esac
## Get the tmp dir's structure
tmpContents=( "$tmpDir"/* )
c=1
## If the tmpDir contains only one item and that is a directory
if [[ ${#tmpContents[@]} = 1 ]] && [[ -d "${tmpContents[0]}" ]]
then
## Move that directory to the current working directory,
## renaming it if a file/directory with the same name exists.
## Then delete the tmpDir.
dirName=${tmpContents[0]##*/}
[[ -e "$dirName" ]] && dirName="$dirName.$c"
while [[ -e "$dirName" ]]; do
((c++))
dirName="${dirName%.*}.$c" ## increment dirName.1 counter
done
mv "${tmpContents[0]}" "$dirName"
rm -r "$tmpDir"
else
## If the tmpDir contains anything more than a single directory,
## rename the tmpDir to the name of the archive, without extension.
## If a file/dir of that name already exists, add a counter.
dirName="${file##*/}" ## strip path
dirName="${dirName%.*}" ## strip extension
dirName="${dirName%.tar}" ## strip remaining tar extension
[[ -e "$dirName" ]] && dirName="$dirName.$c"
while [[ -e "$dirName" ]]; do
((c++))
dirName="${dirName%.*}.$c" ## increment dirName.1 counter
done
mv "$tmpDir" "$dirName"
fi
printf "Archive '%s' extracted to %s\n" "$file" "$dirName" >&2
done
无论如何,这显然是如何aunpack
工作的(减去我们使用的 dirName 增量器),所以如果你不想安装它,只需将它添加到你的$HOME/.local/bin/
或其他地方,名为unpack
/ untar
,或类似的东西chmod +x
。
请注意,此解决方案当然还取决于您是否安装了7zip
、、和。(或您将使用的任何文件类型)。如果您不使用 rar 文件,则无需 unrar,等等。unrar
unzip
tar
注 2:unar
一些 Linux 发行版中似乎还附带一个名为“Unarchiver for a variety of file format”的终端程序。它似乎也以这种方式运行,但当它在提取过程中遇到已存在的文件夹名称时,会发出警告/选择重命名/覆盖/跳过/退出。它似乎也适用于上述所有文件类型。