То, что вы описываете как "обнаружение подкаталогов", должно происходить по умолчанию. В этом примере с 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.
Вы можете создать скрипт-обертку, который извлекает архив во временный каталог, затем, если временный каталог содержит только один другой каталог, переместите этот каталог в текущий рабочий каталог и удалите каталог tmp и, если в папке несколько файлов / каталогов. temp dir, переименуйте его в название архива. Что-то вроде этого:
#!/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