tar / bz2 compactar um arquivo removendo o original não compactado

4

Existe alguma maneira de transformar um diretório chamado dir1 em dir1.tar.bz2 sem manter o original? Eu preciso economizar espaço e quero compactar alguns arquivos grandes, mas não tenho espaço suficiente para manter uma cópia compactada e original. Existe alguma maneira de transformar o arquivo existente em um arquivo diretamente?

    
por Gregg Leventhal 08.10.2013 / 20:42

1 resposta

7

tar não pode fazer isso, mas você pode conseguir o que deseja com:

find dir1 -depth -print0 | xargs -0 tar --create --no-recursion --remove-file --file - | bzip2 > dir1.tar.bz2

onde:

  • find dir1 -depth -print0

    lista todos os arquivos e diretórios em dir1 , listando o conteúdo do diretório antes do próprio diretório ( -depth ). O uso de -print0 (e -0 in xargs below) é a chave para suportar nomes de diretórios e arquivos com espaços incorporados .

  • xargs -0 tar --create --no-recursion --remove-file --file -

    cria um arquivo tar e adiciona todos os arquivos ou diretórios a ele. O arquivo tar é enviado para a saída padrão com a opção --file - .

  • bzip2 > dir1.tar.bz2

    compacta o arquivo tar da entrada padrão para um arquivo chamado dir1.tar.bz2 .

A quantidade de espaço livre em disco necessária é o tamanho do maior arquivo compactado em dir1 porque tar , ao processar um arquivo, aguarda até que o arquivamento seja concluído antes de excluí-lo. Como tar é canalizado para bzip2 , por um breve momento, antes de tar removê-lo, todos os arquivos residem em dois locais: descompactados no sistema de arquivos e compactados dentro de dir1.tar.bz2 .

Eu estava curioso para ver como o espaço em disco foi usado, então fiz esse experimento no meu Ubuntu VM:

  1. Crie um sistema de arquivos de 1 GB:

    $ dd if=/dev/zero of=/tmp/1gb bs=1M count=1024
    $ losetup /dev/loop0 /tmp/1gb
    $ mkfs.ext3 /dev/loop0
    $ sudo mount /dev/loop0 /tmp/mnt
    $ df -h
    Filesystem      Size  Used Avail Use% Mounted on
    /dev/loop0     1008M   34M  924M   4% /tmp/mnt
    
  2. Preencha o sistema de arquivos com 900 arquivos de 1 megabyte:

    $ chown jaume /tmp/mnt
    $ mkdir /tmp/mnt/dir1
    $ for (( i=0; i<900; i++ )); do dd if=/dev/urandom of=/tmp/mnt/dir1/file$i bs=1M count=1; done
    $ chown -R jaume /tmp/mnt
    $ df -h
    Filesystem      Size  Used Avail Use% Mounted on
    /dev/loop0     1008M  937M   20M  98% /tmp/mnt
    

    O sistema de arquivos agora está 98% cheio.

  3. Faça uma cópia de dir1 para verificação posterior:

    $ cp -a /tmp/mnt/dir1 /tmp/dir1-check
    
  4. Comprima dir1 :

    $ ls /tmp/mnt
    dir1  lost+found
    $ find /tmp/mnt/dir1 -depth -print0 | xargs -0 tar --create --no-recursion --remove-file --file - | bzip2 > /tmp/mnt/dir1.tar.bz2
    $
    

    Observe que os comandos foram executados sem nenhum erro 'sem espaço deixado no dispositivo'.

    dir1 foi removido, apenas dir1.tar.bz2 existe:

    $ ls /tmp/mnt
    dir1.tar.bz2  lost+found
    
  5. Expanda dir1.tar.bz2 e compare com /tmp/dir1-check :

    $ tar --extract --file dir1.tar.bz2 --bzip2 --directory /tmp
    $ diff -s /tmp/dir1 /tmp/dir1-check
    (...)
    Files /tmp/dir1/file97 and /tmp/dir1-check/file97 are identical
    Files /tmp/dir1/file98 and /tmp/dir1-check/file98 are identical
    Files /tmp/dir1/file99 and /tmp/dir1-check/file99 are identical
    $
    

    Cópia de dir1 e dir1.tar.bz2 não compactados são idênticas!

Isso pode ser generalizado em um script:

  1. Crie um arquivo chamado tarrm (ou qualquer outro nome de sua preferência) com estes conteúdos:

    #!/bin/bash
    
    # This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
    # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
    # You should have received a copy of the GNU General Public License along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    # dir is first argument
    dir="$1"
    # check dir exists
    if [ ! -d "$dir" ]; then
        echo "$(basename $0): error: '$dir' doesn't exist" 1>&2
        exit 1
    fi
    # check if tar file exists
    if [ -f "${dir}.tar" -o -f "${dir}.tar.bz2" ]; then
        echo "$(basename $0): error: '$dir.tar' or '${dir}.tar.bz2' already exist" 1>&2
        exit 1
    fi
    
    # --keep is second argument
    if [ "X$2" == "X--keep" ]; then
        # keep mode
        removefile=""
        echo " Tarring '$dir'"
    else
        removefile="--remove-file"
        echo " Tarring and **deleting** '$dir'"
    fi
    
    # normalize directory name (for example, /home/jaume//// is a legal directory name, but will break ${dir}.tar.bz2 - it needs to be converted to /home/jaume)
    dir=$(dirname "$dir")/$(basename "$dir")
    
    # create compressed tar archive and delete files after adding them to it
    find "$dir" -depth -print0 | xargs -0 tar --create --no-recursion $removefile --file - | bzip2 > "${dir}.tar.bz2"
    
    # return status of last executed command
    if [ $? -ne 0 ]; then
        echo "$(basename $0): error while creating '${dir}.tar.bz2'" 1>&2
    fi
    
  2. Torne-o executável:

    chmod a+x tarrm

O script faz uma verificação básica de erros: dir1 deve existir, dir1.tar.bz2 e dir1.tar não devem existir e tem um modo keep . Também suporta nomes de diretórios e arquivos com espaços incorporados.

Eu testei o script, mas não posso garantir que ele seja perfeito , então, primeiro use-o no modo de manutenção:

./tarrm dir1 --keep

Essa invocação adicionará dir1 a dir1.tar.bz2 , mas não excluirá o diretório.

Quando você confia no script, use-o da seguinte forma:

./tarrm dir1

O script informará que dir1 será excluído no processo de tarring:

Tarring and **deleting** 'dir1'

Por exemplo:

$ ls -lF
total 4
drwxrwxr-x 3 jaume jaume 4096 2013-10-11 11:00 dir 1/
$ find "dir 1"
dir 1
dir 1/subdir 1
dir 1/subdir 1/file 1
dir 1/file 1
$ /tmp/tarrm dir\ 1/
 Tarring and **deleting** 'dir 1/'
$ echo $?
0
$ ls -lF
total 4
-rw-rw-r-- 1 jaume jaume 181 2013-10-11 11:00 dir 1.tar.bz2
$ tar --list --file dir\ 1.tar.bz2 
dir 1/subdir 1/file 1
dir 1/subdir 1/
dir 1/file 1
dir 1/
    
por 11.10.2013 / 11:02