Qual é a configuração no bash para globbing, para controlar se * corresponde aos arquivos de ponto


12

Fiquei surpreso recentemente quando fiz algo parecido mv ./* ../somedirectory e descobri que arquivos como .gitignorenão foram movidos.

Eu faço a maior parte do meu trabalho no zsh no OS X, e essa surpresa me deixou confusa no CentOS. Eu tentei bash no OS X e encontrei o mesmo comportamento: *não corresponde aos arquivos de ponto. Isso me parece muito indesejável, mas aparentemente é o padrão do bash. (Pode ser o padrão zsh também para tudo o que me lembro, mas posso tê-lo mudado anos atrás no meu .zshrc e esquecido que ele funcionou de maneira diferente.)

Como posso configurar o bash para se comportar conforme o esperado: para * corresponder a todos os arquivos e não ignorar os arquivos de ponto.

Caso isso não esteja claro, veja como reproduzi-lo

cd /tmp
mkdir {t,d}est
touch test/{.,}{1,2,3,4,5,6,7}
ls -hal test
mv test/* dest
ls -hal test     # notice dot files are still there
ls -hal dest     # notice only some files were mv'ed


Sim, eles estão relacionados. Isso não apareceu quando eu pesquisei (provavelmente porque o questionador não mencionou arquivos de globbing ou de ponto), mas é realmente uma pergunta diferente. Ele estava perguntando como mover os arquivos, e eu estava perguntando como alterar o comportamento do shell, e especificamente para o bash. Talvez eu não tenha perguntado se essa pergunta havia sido exibida para mim (já que sua resposta extremamente completa e completa inclui a configuração do bash), mas ainda é uma pergunta diferente, e alguém procurando uma resposta para minha pergunta não necessariamente encontrará a pergunta dele.
Iconoclast

Todo esse negócio de "alguém que procura uma resposta para minha pergunta não vai necessariamente encontrar a sua pergunta" é exatamente por isso que temos essa maneira de encerrar as perguntas como duplicatas.
Gilles 'SO- stop be evil'

Sim, mas parece que você está perdendo o ponto principal, que é uma pergunta diferente .
Iconoclast

Respostas:


14

Bater

Como você já percebeu, o bash não corresponde a um .no início do nome ou a uma barra. Para alterar a correspondência referente ao ponto, você deve definir a dotglobopção - man bash :

dotglob Se definido, o bash inclui nomes de arquivos começando com um `. ' dentro
    os resultados da expansão do nome do caminho.

Para habilitá-lo / configurá-lo com o uso do bash shopt, por exemplo:

shopt -s dotglob


Para o zsh, você também pode usar a dotglobopção, mas precisará setopthabilitá-la, por exemplo:

setopt dotglob

2
Com zsh, a melhor opção é ativar o dotglob em uma base por glob:mv test/*(D) /dest
Stéphane Chazelas 27/04

7

Eu testei isso e resolve o problema:

shopt -s dotglob

Resultado:

~/stackexchangeanswers/40662$ ls -hal dest
total 8.0K
drwxr-xr-x 2 jodiec jodiec 4.0K 2012-06-12 22:15 .
drwxr-xr-x 4 jodiec jodiec 4.0K 2012-06-12 22:15 ..
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 1
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 .1
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 2
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 .2
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 3
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 .3
...snipped....

Desculpe, mas Ulrich respondeu primeiro.
Iconoclast

3
Estamos todos no mesmo time. Está certo.
Jodie C

6

*é uma glob que é expandida pela casca. Por padrão, os shells não incluem arquivos cujo nome começa com um .(chamado arquivos ocultos ou arquivos de ponto), a menos que o líder .seja inserido literalmente.

*ou [.]*ou ?*ou *.*ou dir/*não incluirá arquivos de ponto.

.*ou dir/.*vontade.

Então você poderia fazer:

mv -- * .* /dest/

no entanto, alguns shells, incluindo bash(mas não zsh, mkshnem fish) têm essa característica incorreta de que a expansão de .*inclui as entradas de diretório especiais .e ..que você não deseja aqui (e geralmente nunca deseja que uma glob inclua, e é por isso que eu a chamo de característica incorreta).

Por esse motivo, você verá que às vezes as pessoas usam (em conchas tipo Bourne):

mv -- * .[!.]* ..?* /dest/

São três globs, o primeiro correspondendo a arquivos não ocultos, o segundo nome de arquivo começando com .seguido por um caractere diferente de .e o terceiro nome de arquivo começando com ..seguido por pelo menos um caractere.

No entanto, algumas conchas modernas têm maneiras melhores de contornar isso

zsh

Com zsh, você pode usar o (D)qualificador glob para especificar que o glob deve incluir arquivos de ponto:

mv -- *(D) /dest/

zshtambém corrigiu essa outra falha no shell Bourne, pois se o padrão não corresponder, o mvcomando não será executado.

Como dito acima, também nunca incluirá .nem ..em seus globs, então

mv -- * .* /dest/

estará seguro. No entanto, se não houver nenhum arquivo correspondente *ou nenhum arquivo correspondente .*ao comando será abortado, seria melhor usar:

mv -- (*|.*) /dest/

Como em alguns outros shells, você também pode forçar todos os globs a incluir arquivos de ponto (por exemplo, se você deseja que os arquivos de ponto sejam incluídos com mais frequência do que não) com:

setopt dotglob

ou:

set -o dotglob

Depois disso, se você quiser que um determinado globo não inclua arquivos de ponto, você pode escrevê-lo:

echo *(^D)

Ou:

echo [^.]*

Bater

Infelizmente bashnão tem qualificadores glob. Portanto, você fica com a ativação da inclusão do dotfile globalmente. Em bash, a sintaxe é:

shopt -s dotglob

(e use [^.]*para globs sem arquivos ocultos).

Com dotglob, bashnão inclui .nem ..em globs como *, mas ainda faz para globs como .*.

Se você definir a GLOBIGNOREvariável como algo não vazio, ela ativará automaticamente a dotglobopção e excluirá .e ..de .*globs, mas não de dir/.*ou .*/fileones (!), Para que a proteção seja bastante inútil. Você poderia fazer, GLOBIGNORE='*/.:*/..:./*:../*:*/./*:*/../*'mas então isso quebraria globs como */.ou ./*ou ../*.

Uma solução melhor é usar [.]*ou dir/[.]*ou [.]*/file(com dotglobativado) expandir arquivos de ponto, exceto .e ...

peixe

fishglobs não incluem .nem ... Quando não há correspondência, dependendo da versão, funcionará como zsh(ou bash -o failglob) ou bash -o nullglob.

mv -- * .* /dest/

Funcionaria se houver arquivos ocultos e não ocultos. Caso contrário, o YMMV e com algumas versões poderá ser chamado mv -- /destse não houver nenhum arquivo.

ksh93

Nenhum qualificador glob ksh93também. Você pode incluir arquivos de ponto nos globs com:

FIGNORE='@(.|..)'

Contrariamente à bashs' GLOBIGNORE, isso é feito corretamente e também corrige o problema de .*incluindo .e ...

yash

yashtem uma dot-globopção ( set -o dot-glob), mas, ao contrário bash, as expansões glob (inclusive de *) incluem .e, ..portanto, são bastante inúteis.

tcsh

set globdot

Obras como em bash, que é *incluir arquivos dot exceto .e ..mas .*ainda inclui .e ..(e você pode usar [.]*para expandir os arquivos ocultos, exceto .e ..).


4

A maneira mais fácil no bash de imprimir arquivos de ponto correspondentes, ignorando .e ..é:

$ GLOBIGNORE=/+/
$ printf '%s\n' *


Testar:

$ cd tmp; mkdir empty; cd empty; touch {,.}{a..h}
$ GLOBIGNORE=/+/
$  ls *
a  .a  b  .b  c  .c  d  .d  e  .e  f  .f  g  .g  h  .h

Ele imprimiu todos os arquivos com pontos ou não, com a exceção notável de .e ...

Seu exemplo também funcionará corretamente:

$ cd /tmp; mkdir {t,d}est; touch test/{,.}{a..h}
$ mv test/* dest/
$ ls -a test
.  ..
$ ls -a test/*
ls: cannot access test/*: No such file or directory

Vazio de arquivos importantes.
Os arquivos do sistema . ..ainda existem, mas uma expansão do shell (usando *) não os inclui.

E o diretório de destino contém todos os arquivos:

$ cd dest
$ ls -a *
a  .a  b  .b  c  .c  d  .d  e  .e  f  .f  g  .g  h  .h

Por quê?

Isso funciona porque a configuração de GLOBIGNORE tem os seguintes efeitos colaterais:

  • Defina dotglob ( shopt -s dotglob) como arquivos de pontos matemáticos em expansões.
  • Os nomes de arquivos . e ..são ignorados quando GLOBIGNORE está definido e não nulo.

Detalhes no manual: LESS=+'/The GLOBIGNORE' man bash.

Além disso, precisamos definir a variável GLOBIGNORE para conter um nome que nenhum nome de arquivo possa corresponder:
uma barra (e outra coisa apenas como uma precaução dupla).

$ GLOBIGNORE=/+/

Também pode ser útil definir o nullglob se for necessário evitar um *quando não houver arquivo para corresponder ao *.


0

Muito interessado nesta configuração de GLOBIGNORE = / + / @ user79743

Da minha experiência desactivação dotglob é mau negócio para quase todos os seus comandos (cp, mv etc.), mas o mesmo é verdade a criação do nullglob (especialmente com expressões regulares que precisa então escapando!).

No entanto, se você precisar defini-los no início do seu programa, mas interferir em partes específicas nas quais você chama comandos como cp, mv etc. ou usa expressões regulares, faça o seguinte:

  # set dotglob, unset nullglob AFTER this
  is_nullglob=$( shopt -s | egrep -i '.*nullglob' )
  is_dotglob=$( shopt -s | egrep -i '.*dotglob' )
  [[ $is_nullglob ]] && shopt -u nullglob
  [[ ! $is_dotglob ]] && shopt -s dotglob
  # ... call commands ...
  # .....................
  # reset dotglob, nullglob to their previous values
  [[ $is_nullglob ]] && shopt -s nullglob
  [[ ! $is_dotglob ]] && shopt -u dotglob
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.