arquivos ocultos cp com padrões glob

13

Situação:

$ mkdir foo && touch foo/.test
$ cp foo/* .
zsh: no matches found: foo/*
(or bash : cp: cannot stat foo/*’: No such file or directory)

Eu tenho um diretório cheio de pastas e arquivos ocultos. O que está acontecendo e qual é a solução?

Gradiente
fonte
dotglobé traduzido para globdotspelo zsh que é nome de opção inválida para o bash .

Respostas:

19

Isenção de responsabilidade: Esta resposta lida especificamente com o Bash, mas muito disso se aplica à pergunta sobre padrões glob!

O caractere estrela ( *) é um curinga. Há um certo conjunto de caracteres que ele substituirá e o primeiro caractere sendo um ponto ( .) não é um deles. Este é um caso especial apenas por causa do funcionamento dos sistemas de arquivos Unix, os arquivos que começam com um ponto são considerados "ocultos". Isso significa que ferramentas como cp, lsetc. não as "verão", a menos que seja explicitamente instruído a fazê-lo.

Exemplos

Primeiro, vamos criar alguns dados de amostra.

$ mkdir .dotdir{1,2} regdir{1,2}
$ touch .dotfile{1,2} regfile{1..3}

Então agora temos o seguinte:

$ tree -a
.
|-- .dotdir1
|-- .dotdir2
|-- .dotfile1
|-- .dotfile2
|-- regdir1
|-- regdir2
|-- regfile1
|-- regfile2
`-- regfile3

Agora vamos jogar alguns jogos. Você pode usar o comando echopara listar o que seria um caractere curinga ( *) específico para um determinado comando, como:

$ echo *
regdir1 regdir2 regfile1 regfile2 regfile3


$ echo reg*
regdir1 regdir2 regfile1 regfile2 regfile3

$ echo .*
. .. .dotdir1 .dotdir2 .dotfile1 .dotfile2

$ echo .* *
. .. .dotdir1 .dotdir2 .dotfile1 .dotfile2 regdir1 regdir2 regfile1 regfile2 regfile3

$ echo .dotdir*
.dotdir1 .dotdir2

Mudando o comportamento?

Você pode usar o comando shopt -s dotglobpara alterar o comportamento do *arquivo para que, além de arquivos como regfile1ele, também corresponda .dotfile1.

trecho da bashpágina de manual

dotglob If set, bash includes filenames beginning with a `.' in the results 
        of pathname expansion.

Exemplo:

$ shopt -s dotglob
$ echo *
.dotdir1 .dotdir2 .dotfile1 .dotfile2 regdir1 regdir2 regfile1 regfile2 regfile3

Você pode reverter esse comportamento com este comando:

$ shopt -u dotglob
$ echo *
regdir1 regdir2 regfile1 regfile2 regfile3

Sua situação?

Para você, você está dizendo cpque deseja copiar todos os arquivos que correspondem ao padrão *e que não existem arquivos.

$ cp foo/.* .

Ou você pode fazer isso se quiser tudo na foopasta:

$ cp foo .

Ou você pode ser explícito:

$ cp foot/.* foo/* .

Uma forma mais compacta usando expansão de chaves em bash:

$ cp foo/{.,}* .

A qualquer momento, você pode usar o echotruque para ver quais padrões de arquivo propostos (esse é o termo mais sofisticado para o qual a estrela faz parte).

$ echo {.,}*
. .. .dotdir1 .dotdir2 .dotfile1 .dotfile2 abc regdir1 regdir2 regfile1 regfile2 regfile3

Aliás, se você deseja copiar um diretório de arquivos + outros diretórios, normalmente deseja fazer isso recursivamente, essa é a -Ropção para cp:

$ cp -R foo/. .
slm
fonte
1
Você pode mencionar o uso de shopt -s dotglob, echo *e fornece:.dotdir1 .dotdir2 .dotfile1 .dotfile2 regdir1 regdir2 regfile1 regfile2 regfile3
Drav Sloan,
@ DravSloan - obrigado pela sugestão. Adicionado.
slm
cp -r foo .normalmente não funcionará, pois cpse recusará a copiar foosobre si mesmo, você provavelmente quis dizer cp -R foo/. .. (note que cp -Ré o padrão).
Stéphane Chazelas
1
Observe também que (com bastante sensibilidade), zshnão inclui .e ..na expansão de .*. Além disso zsh, se um padrão não corresponder, o comando será abortado (novamente com bastante sensibilidade); portanto, no caso geral, cp foo/{,.}* .falhará se não houver arquivos ocultos ou não ocultos.
Stéphane Chazelas
7
Esta resposta se aplica apenas ao bash. O solicitante está usando o zsh, onde a resposta é muito mais simples e shoptnão existe.
Gilles 'SO- stop be evil'
29

Com zsh, a maneira típica é usar o D qualificador glob (para incluir [D] ot arquivos):

cp foo/*(D) .

Observe que, com ou sem D, os zsh globs nunca incluem .nem ..(como seria de esperar).

Stéphane Chazelas
fonte
4

O shell está tratando esses arquivos como ocultos quando resolve o *caractere, portanto cp, não recebe nenhum desses nomes de arquivo como argumentos.

Você pode copiá-los especificando explicitamente cp foo/.* .

repetição
fonte
1

Se o que você deseja fazer é copiar todos os arquivos e diretórios de um lugar para outro, você pode usar o rsynccomando padrão . No exemplo que você fornece acima:

mkdir foo && touch foo/.test
rsync -a foo/ .

copiará recursivamente todo o conteúdo de foo, incluindo arquivos ocultos e diretórios ocultos, no diretório atual. A barra final no final de foo/é importante para o rsync; com ele apenas o conteúdo de fooé copiado, sem ele o rsync também copia foo. Por exemplo:-

mkdir src && mkdir dest && touch src/.test
rsync -a src  dest    // copies 'src' contents to 'dest/src'
rsync -a src/ dest    // copies 'src' contents to 'dest' 

Existem muitas outras opções disponíveis para ajustar o rsync, incluindo a cópia entre máquinas.

Rob Swarbrick
fonte
1

A resposta zsh de Stéphane Chazelas está correta para uma única expressão com um globo nela. No entanto, se você quiser defini-lo para todas as expressões por padrão, poderá usar

setopt GLOB_DOTS

Coloque no seu ~/.zshrcpara torná-lo permanente.

Sparhawk
fonte