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
inxargs
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:
-
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
-
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.
-
Faça uma cópia de
dir1
para verificação posterior:$ cp -a /tmp/mnt/dir1 /tmp/dir1-check
-
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, apenasdir1.tar.bz2
existe:$ ls /tmp/mnt dir1.tar.bz2 lost+found
-
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
edir1.tar.bz2
não compactados são idênticas!
Isso pode ser generalizado em um script:
-
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
-
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/