Como tar um diretório de arquivos e pastas sem incluir o próprio diretório?

382

Eu normalmente faço:

tar -czvf my_directory.tar.gz my_directory

E se eu apenas quiser incluir tudo (incluindo todos os arquivos ocultos do sistema) no meu diretório, mas não o diretório em si? Eu não quero:

my_directory
   --- my_file
   --- my_file
   --- my_file

Eu quero:

my_file
my_file
my_file
eu vou
fonte
Esse é o comportamento padrão de fazer tar -czf? No meu caso, é apenas armazenar os arquivos e não o diretório. Quando eu apenas taro diretório ele inclui, mas tar -czfapenas adiciona os arquivos.
Durga Swaroop

Respostas:

233
cd my_directory/ && tar -zcvf ../my_dir.tgz . && cd - 

deve fazer o trabalho em uma linha. Funciona bem para arquivos ocultos também. "*" não expande arquivos ocultos pela expansão do nome do caminho, pelo menos no bash. Abaixo está o meu experimento:

$ mkdir my_directory
$ touch my_directory/file1
$ touch my_directory/file2
$ touch my_directory/.hiddenfile1
$ touch my_directory/.hiddenfile2
$ cd my_directory/ && tar -zcvf ../my_dir.tgz . && cd ..
./
./file1
./file2
./.hiddenfile1
./.hiddenfile2
$ tar ztf my_dir.tgz
./
./file1
./file2
./.hiddenfile1
./.hiddenfile2
tomoe
fonte
2
Isso também funcionará em arquivos com espaços ou outros caracteres especiais. Bom trabalho!
PanCrit 03/06/2009
29
Não é perfeito - o arquivo tar contém '.' e também ./file1 em vez de apenas file1. Eu gosto da solução mateusza abaixo para usar --strip-components quando não tarar.
Ivan
23
@ Ivan, se você substituir .por, *para que o comando seja cd my_directory/ && tar -zcvf ../my_dir.tgz * && cd .., ele funcionará conforme o esperado.
Anônimo
3
@jmathew Você também pode usar um subshell para que o diretório de trabalho do seu shell atual não seja alterado:$ (cd my_directory/ && tar -zcvf ../my_dir.tgz .)
Alec
Eu sei que é uma resposta antiga, mas cdentrar em diretórios e sair é muito ruim. Poderia pelo menos usar pushde popdse tarnão tiver nenhum sinalizador como -C.
21919 Andris
667

Use o -Cinterruptor de alcatrão:

tar -czvf my_directory.tar.gz -C my_directory .

O -C my_directorycomando tar indica que o diretório atual deve ser alterado my_directorye .significa "adicionar o diretório atual inteiro" (incluindo arquivos e subdiretórios ocultos).

Certifique-se de fazê-lo -C my_directoryantes de fazê- lo, .ou então você obterá os arquivos no diretório atual.

dubek
fonte
5
+1 obrigado! Foi o maldito '.' Eu estava desaparecido. assim agravando
JCotton
14
"Ao contrário da maioria das opções, -C é processado no ponto em que ocorre na lista de arquivos a serem processados. Considere o seguinte comando: tar --create --file=foo.tar -C /etc passwd hosts -C /lib libc.a" apl.jhu.edu/Misc/Unix-info/tar/tar_65.html Eu sempre tento tar -czvf my_directory.tar.gz * -C my_directorye isso não funciona. -Clocalização é importante! Porra tar ...
m-ric
57
Não é perfeito - o arquivo tar contém '.' e também ./file1 em vez de apenas file1. Eu gosto da solução mateusza abaixo para usar --strip-components quando não tarar.
Ivan
3
@ Superole: o shell substitui os curingas antes de executar o tar. Observe também que o uso de um curinga *não incluirá arquivos ocultos (que era o requisito original).
Dubek
28
Ele cria . como um diretório raiz em .tar.gz.
Anônimo
59

Você também pode criar um arquivo morto como de costume e extraí-lo com:

tar --strip-components 1 -xvf my_directory.tar.gz
mateusza
fonte
3
Esta solução é especialmente boa em situações em que você está trabalhando com tarballs criados antes de todos os seus requisitos serem conhecidos ...
Digger
2
Mente que --strip-componentsé uma extensão GNU.
zneak
2
Esta resposta pode ser melhorada fornecendo o exemplo "como sempre" no contexto.
Josh Habdas 23/03/19
33

Dê uma olhada em --transform/ --xform, ele oferece a oportunidade de massagear o nome do arquivo à medida que o arquivo é adicionado ao arquivo:

% mkdir my_directory
% touch my_directory/file1
% touch my_directory/file2
% touch my_directory/.hiddenfile1
% touch my_directory/.hiddenfile2
% tar -v -c -f my_dir.tgz --xform='s,my_directory/,,' $(find my_directory -type f)
my_directory/file2
my_directory/.hiddenfile1
my_directory/.hiddenfile2
my_directory/file1
% tar -t -f my_dir.tgz 
file2
.hiddenfile1
.hiddenfile2
file1

A expressão Transform é semelhante à de sed, e podemos usar separadores diferentes de /( ,no exemplo acima).
https://www.gnu.org/software/tar/manual/html_section/tar_52.html

Magnus
fonte
3
Eu faria isso. Qualquer outra coisa é apenas um truque!
GTC
2
Esta é uma solução muito melhor.
Leesei 16/05
essa é a melhor solução.
Just dec
11
Boa solução, mas pode causar file list too long. Minha solução evita isso e é mais flexível também.
aross
Esta é uma otima soluçao. Você também pode passar --xformvárias vezes para vários caminhos.
elegível
26

TL; DR

find /my/dir/ -printf "%P\n" | tar -czf mydir.tgz --no-recursion -C /my/dir/ -T -

Com algumas condições (arquive apenas arquivos, diretórios e links simbólicos):

find /my/dir/ -printf "%P\n" -type f -o -type l -o -type d | tar -czf mydir.tgz --no-recursion -C /my/dir/ -T -

Explicação

Infelizmente, o abaixo inclui um diretório pai ./no arquivo:

tar -czf mydir.tgz -C /my/dir .

Você pode mover todos os arquivos desse diretório usando a --transformopção de configuração, mas isso não se livra do .próprio diretório. Torna-se cada vez mais difícil domesticar o comando.

Você pode usar $(find ...)para adicionar uma lista de arquivos ao comando (como na resposta da magnus ), mas isso pode causar um erro "lista de arquivos muito longa". A melhor maneira é combiná-lo com a -Topção do tar , assim:

find /my/dir/ -printf "%P\n" -type f -o -type l -o -type d | tar -czf mydir.tgz --no-recursion -C /my/dir/ -T -

Basicamente, o que ele faz é listar todos os arquivos ( -type f), links ( -type l) e subdiretórios ( -type d) no seu diretório, tornar todos os nomes de arquivos relativos -printf "%P\n", e depois passar isso para o comando tar (são utilizados nomes de arquivos do STDIN -T -). A -Copção é necessária para que o tar saiba onde estão os arquivos com nomes relativos. O --no-recursionsinalizador é para que o tar não recue em pastas que é solicitado a arquivar (causando arquivos duplicados).

Se você precisar fazer algo especial com os nomes de arquivos (filtragem, seguintes links simbólicos etc.), o findcomando é bastante poderoso e você pode testá-lo apenas removendo a tarparte do comando acima:

$ find /my/dir/ -printf "%P\n" -type f -o -type l -o -type d
> textfile.txt
> documentation.pdf
> subfolder2
> subfolder
> subfolder/.gitignore

Por exemplo, se você deseja filtrar arquivos PDF, adicione ! -name '*.pdf'

$ find /my/dir/ -printf "%P\n" -type f ! -name '*.pdf' -o -type l -o -type d
> textfile.txt
> subfolder2
> subfolder
> subfolder/.gitignore

Localização não GNU

O comando usa printf(disponível no GNU find) que indica findpara imprimir seus resultados com caminhos relativos. No entanto, se você não possui o GNU find, isso funciona para tornar os caminhos relativos (remove os pais com sed):

find /my/dir/ -type f -o -type l -o -type d | sed s,^/my/dir/,, | tar -czf mydir.tgz --no-recursion -C /my/dir/ -T -
aross
fonte
2
Ótima resposta. Muito elaborado e, o mais importante, resolve o problema perfeitamente.
31417 Alex
16

Esta resposta deve funcionar na maioria das situações. Observe, no entanto, como os nomes de arquivos são armazenados no arquivo tar como, por exemplo, ./file1ao invés de apenas file1. Descobri que isso causava problemas ao usar esse método para manipular tarballs usados ​​como arquivos de pacote no BuildRoot .

Uma solução é usar alguns globos Bash para listar todos os arquivos, exceto os ..seguintes:

tar -C my_dir -zcvf my_dir.tar.gz .[^.]* ..?* *

Este é um truque que aprendi com esta resposta .

Agora, o tar retornará um erro se não houver arquivos correspondentes ..?*ou .[^.]*, mas ainda funcionará. Se o erro for um problema (você está verificando o sucesso em um script), isso funciona:

shopt -s nullglob
tar -C my_dir -zcvf my_dir.tar.gz .[^.]* ..?* *
shopt -u nullglob

Embora agora estamos mexendo com as opções do shell, podemos decidir que é melhor ter *arquivos ocultos correspondentes:

shopt -s dotglob
tar -C my_dir -zcvf my_dir.tar.gz *
shopt -u dotglob

Isso pode não funcionar onde seu shell está *no diretório atual; portanto, use alternativamente:

shopt -s dotglob
cd my_dir
tar -zcvf ../my_dir.tar.gz *
cd ..
shopt -u dotglob
Rob Fisher
fonte
11
Eu recebo erros estranhos quando faço isso tar: start.sh: Cannot stat: No such file or directoryIsso acontece com todos os arquivos no meu diretório atual! Como evito isso?
BrainStone
@BrainStone Eu recebo exatamente os mesmos resultados.
mbmast
15
cd my_directory
tar zcvf ../my_directory.tar.gz *
mateusza
fonte
2
Hal perguntou explicitamente sobre arquivos ocultos. Você também precisa .??*.
PanCrit
2
-1: Isso não adiciona os arquivos ocultos ao tar. Veja a resposta do tbman.
Dubek
3

Se for um sistema Unix / Linux, e você se preocupa com arquivos ocultos (que serão perdidos por *), você precisa:

cd my_directory
tar zcvf ../my_directory.tar.gz * .??*

Não sei como são os arquivos ocultos no Windows.

PanCrit
fonte
2

Eu proporia a seguinte função Bash (o primeiro argumento é o caminho para o dir, o segundo argumento é o nome da base do arquivo resultante):

function tar_dir_contents ()
{
    local DIRPATH="$1"
    local TARARCH="$2.tar.gz"
    local ORGIFS="$IFS"
    IFS=$'\n'
    tar -C "$DIRPATH" -czf "$TARARCH" $( ls -a "$DIRPATH" | grep -v '\(^\.$\)\|\(^\.\.$\)' )
    IFS="$ORGIFS"
}

Você pode executá-lo desta maneira:

$ tar_dir_contents /path/to/some/dir my_archive

e irá gerar o arquivo my_archive.tar.gzno diretório atual. Funciona com elementos ocultos (. *) E com elementos com espaços no nome do arquivo.

gpz500
fonte
2
cd my_directory && tar -czvf ../my_directory.tar.gz $(ls -A) && cd ..

Este funcionou para mim e inclui todos os arquivos ocultos sem colocar todos os arquivos em um diretório raiz chamado "." como na resposta de tomoe :

med
fonte
1
tar.create() {
        local folder="${1}"

        local tar="$(basename "${folder}")".tar.gz

        cd "${folder}" && tar -zcvf "../${tar}" .; cd - &> /dev/null
}

Exemplo:

tar.create folder

Você é bem vindo.

mmm
fonte
1

Isto é o que funciona para mim.

tar -cvf my_dir.tar.gz -C /my_dir/ $(find /my_dir/ -maxdepth 1 -printf '%P ')

Você também pode usar

tar -cvf my_dir.tar.gz -C /my_dir/ $(find /my_dir/ -mindepth 1 -maxdepth 1 -printf '%P ')

No primeiro comando, find retorna uma lista de arquivos e subdiretórios de my_dir . No entanto, o diretório my_dir é incluído nessa lista como '.' O -printf parâmetro remove o caminho completo, incluindo o '.' e também todos No entanto, o no formato, a string '% P' de printf deixa um remanescente na lista de arquivos e subdiretórios de my_dir e pode ser vista por um espaço à esquerda no resultado do comando find .

Isso não será um problema para o TAR, mas se você quiser corrigir isso, adicione -mindepth 1 como no segundo comando.

serendrewpity
fonte
0

Use pax.

Pax é um pacote obsoleto, mas faz o trabalho perfeitamente e de maneira simples.

pax -w > mydir.tar mydir
rjv
fonte
Mais prático e faz o trabalho +1
Breno Salgado
esse comando cria mydir.tar com o conteúdo: mydir / file1 mydir / file2, exatamente o que deveria ser evitado.
Alex
0

Maneira mais simples que encontrei:

cd my_dir && tar -czvf ../my_dir.tar.gz *

alexgo
fonte
11
Isso não inclui arquivos ocultos.
assynts 26/06/19
0
# tar all files within and deeper in a given directory
# with no prefixes ( neither <directory>/ nor ./ )
# parameters: <source directory> <target archive file>
function tar_all_in_dir {
    { cd "$1" && find -type f -print0; } \
    | cut --zero-terminated --characters=3- \
    | tar --create --file="$2" --directory="$1" --null --files-from=-
}

Lida com segurança com nomes de arquivos com espaços ou outros caracteres incomuns. Opcionalmente, você pode adicionar um -name '*.sql'filtro ou semelhante ao comando find para limitar os arquivos incluídos.

marcingo
fonte
-1
 tar -cvzf  tarlearn.tar.gz --remove-files mytemp/*

Se a pasta for mytemp, se você aplicar o procedimento acima, ela compactará e removerá todos os arquivos da pasta, mas a deixará em paz

 tar -cvzf  tarlearn.tar.gz --remove-files --exclude='*12_2008*' --no-recursion mytemp/*

Você pode fornecer padrões de exclusão e também especificar para não examinar as subpastas também

user1456599
fonte
-3
tar -C my_dir -zcvf my_dir.tar.gz `ls my_dir`
mateusza
fonte