O que você descreve como "detecção de subdiretório" deve acontecer por padrão. Neste exemplo com o GNU tar
:
$ tree
.
├── dir1
│ └── file4
├── dir2
│ ├── file5
│ └── file6
├── file1
├── file2
└── file3
Arquivo:
$ 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
Se você estiver usando um programa de arquivamento que não mantém a estrutura de diretórios ao criar um arquivo (você tem certeza disso, a propósito? Eu não sei de nenhum que não faça isso), então a informação é perdido. Não há como recriar a estrutura de diretórios a menos que ela tenha sido salva no próprio arquivo. Nesse caso, ela deve ser recriada na extração do arquivo por padrão.
Se você quiser imitar o comportamento de 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.
Você pode criar um script de wrapper que extraia o archive em um diretório temporário; em seguida, se o diretório temporário contiver apenas outro diretório, mova esse diretório para o diretório de trabalho atual e exclua o diretório tmp e, se houver vários arquivos / dirs no dir temporário, renomeie-o para o nome do arquivo. Algo parecido com isto:
#!/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