Programa de linha de comando para extrair arquivos com detecção automática de subdiretórios

1

Estou procurando a contraparte pura da linha de comando para ark -ba <path> (no KDE) ou file-roller -h <path> (no GNOME / Unity).

Infelizmente, tanto ark como file-roller requerem que o X esteja em execução. Estou ciente de que é relativamente simples escrever uma ferramenta que detecta arquivos com base em sua extensão de arquivo e, em seguida, executa o programa apropriado:

#!/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

No entanto, isso não cuida da detecção de subdiretórios (e, de fato, muitos programas de extração nem mesmo fornecem essa opção).

Então, pode haver um programa que faça exatamente isso?

    
por 该用户不存在 09.12.2012 / 05:44

2 respostas

3

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
    
por 09.12.2012 / 15:19
2

aunpack do atool faz isso por padrão.

Uso: aunpack <archive file>

Disponível na maioria das distribuições de distribuição Linux.

    
por 04.05.2016 / 11:07