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

10

Eu fiquei surpreso recentemente quando fiz algo como %código% e descobriu que arquivos como mv ./* ../somedirectory não foram movidos.

Eu faço a maior parte do meu trabalho em zsh no OS X, e essa surpresa me mordeu no bash no CentOS. Eu tentei bash no OS X e encontrei o mesmo comportamento: .gitignore não corresponde aos arquivos de pontos. Isso parece muito indesejável para mim, mas aparentemente é o padrão bash. (Pode ser o padrão zsh também para tudo que eu lembro, mas eu posso ter mudado isso anos atrás no meu .zshrc e esquecido que ele funcionou de forma diferente.)

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

Caso isso não seja 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
    
por iconoclast 13.06.2012 / 05:10

5 respostas

12

Bash

Como você já percebeu, o bash não corresponderá a . no início do nome ou a uma barra. Para alterar a correspondência em relação ao ponto, você deve definir a opção dotglob - homem bash :

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

Para ativar / definir com o bash use shopt , por exemplo:

shopt -s dotglob


Para zsh, você também pode usar a opção dotglob , mas precisará usar setopt para ativá-la, por exemplo:

setopt dotglob
    
por 13.06.2012 / 05:15
6

Eu testei isso e resolveu o problema:

shopt -s dotglob

Saída:

~/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....
    
por 13.06.2012 / 05:15
4

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

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

Para 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 que importam.
Os arquivos do sistema . .. ainda existem, mas uma expansão do shell (usando *) não os incluirá.

E o diretório dest ination 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 GLOBIGNORE tem esses efeitos side :

  • Defina dotglob ( shopt -s dotglob ) para arquivos de pontos matemáticos em expansões.
  • Nomes de arquivos . e .. são ignorados quando GLOBIGNORE é 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 precaução dupla).

$ GLOBIGNORE=/+/

Pode ser útil definir também nullglob, se necessário, para evitar a obtenção de um * quando não houver arquivo para corresponder ao * .

    
por 01.05.2016 / 08:19
2

* é um glob que é expandido pelo shell. Por padrão, os shells não incluem arquivos cujo nome comece com . (chamados de arquivos ocultos ou dotfiles), a menos que o . inicial seja digitado literalmente.

* ou [.]* ou ?* ou *.* ou dir/* não incluirão dotfiles.

.* ou dir/.* será.

Então você poderia fazer:

mv -- * .* /dest/

no entanto, alguns shells incluindo bash (mas não zsh , mksh nor fish ) têm essa má característica de que a expansão de .* inclui as entradas de diretório especiais . e .. não quero aqui (e geralmente nunca quero um glob para incluir, e é por isso que eu chamo isso de um erro).

Por esse motivo, você descobrirá que às vezes as pessoas usam (em shells semelhantes a Bourne):

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

São três globs, o primeiro que corresponde 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 shells modernas têm melhores maneiras de contornar isso

zsh

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

mv -- *(D) /dest/

zsh também corrigiu essa outra má especificação do shell Bourne, pois se o padrão não corresponder, o comando mv não será executado.

Como dito acima, ele nunca incluirá . nem .. em seus globs, então

mv -- * .* /dest/

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

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

Como em alguns outros shells, você também pode forçar todos os globs a incluírem dotfiles (por exemplo, se você está querendo que os dotfiles sejam incluídos mais frequentemente do que não) com:

setopt dotglob

ou:

set -o dotglob

Depois disso, se você quiser que um glob glob não inclua dotfiles, você pode escrevê-lo:

echo *(^D)

Ou:

echo [^.]*

Bash

Infelizmente bash não tem qualificadores glob. Então você tem a possibilidade de incluir globalmente a inclusão de arquivos de ponto. Em bash , a sintaxe é:

shopt -s dotglob

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

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

Se você definir a variável GLOBIGNORE como algo não vazio, ela ativará automaticamente a opção dotglob e excluirá . e .. de .* globs, mas não de dir/.* ou .*/file (!) para que a salvaguarda seja bastante inútil. Você poderia fazer GLOBIGNORE='*/.:*/..:./*:../*:*/./*:*/../*' , mas isso iria quebrar globs como */. ou ./* ou ../* .

Uma solução melhor é usar [.]* ou dir/[.]* ou [.]*/file (com dotglob ativado) para expandir os arquivos pontuáveis, exceto . e .. .

peixe

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

mv -- * .* /dest/

Funcionaria se houvesse arquivos ocultos e não ocultos. Caso contrário, YMMV e com algumas versões, pode chamar mv -- /dest se não houver nenhum arquivo.

ksh93

Nenhum qualificador de glob em ksh93 . Você pode incluir dotfiles em globs com:

FIGNORE='@(.|..)'

Ao contrário de bash ' GLOBIGNORE , isso foi feito corretamente e também corrige o problema de .* , incluindo . e .. .

yash

yash tem uma opção dot-glob ( set -o dot-glob ), mas ao contrário de bash , as expansões glob (mesmo de * ) incluem . e .. , por isso é bastante inútil.

tcsh

set globdot

Funciona como em bash , ou seja, * inclui arquivos de pontos, exceto . e .. , mas .* ainda inclui . e .. (e você pode usar [.]* para expandir ocultos arquivos, exceto . e .. ).

    
por 27.04.2016 / 14:13
0

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

Da minha experiência desarmar o dotglob é mau negócio para quase todos os seus comandos (cp, mv etc.) MAS o mesmo é verdadeiro configuração o nullglob (especialmente com expressões regulares que precisam de escape!).

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

  # 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
    
por 25.10.2016 / 19:21