Expansão de nome de arquivo: encontrar correspondência de padrão de utilitário vs combinação de padrão de concha de Bash

5

Para a expansão do nome do arquivo, a opção '-name' do utilitário 'find' parece funcionar de forma semelhante, mas não exatamente da mesma forma que a correspondência de padrões internos do shell bash.

Aqui estão as seções relevantes do manual de referência GNU:

  • Correspondência de padrões de shell Bash: link
  • correspondência de padrões de utilitário 'encontrar': link

Isso é muito confuso por si só. Para aumentar essa confusão, a seção 2.1.4 da página man do utilitário 'find' (referenciada acima) é intitulada "Shell Pattern Matching", o que implica que 'find' está usando a funcionalidade de correspondência de padrões internos do shell. No entanto, este não parece ser o caso porque, de acordo com a página de manual 'find' ( link ), sob '-name pattern', diz o seguinte:

"A correspondência de nome de arquivo é executada com o uso da função de biblioteca fnmatch (3). Não esqueça de colocar o padrão entre aspas para protegê-lo da expansão pelo shell."

A partir disso, parece que não é o shell que está executando a correspondência de padrões, mas sim o utilitário find usando a biblioteca fnmatch.

Aqui estão minhas perguntas:

  1. A expansão de nome de arquivo e a correspondência de padrões padrão do shell bash (opção de shell extglob desativada) é diferente da do utilitário find usando a opção -name?
  2. Se sim, quais são essas diferenças?
  3. O bash também usa a biblioteca fnmatch ou algum outro mecanismo para expansão de nome de arquivo e correspondência de padrões?
por teancum144 16.06.2015 / 17:44

1 resposta

5

No shell, você precisa distinguir geração / expansão de nome de arquivo ( globbing ): um padrão que se expande para uma lista de arquivos do padrão de correspondência . globbing usa padrão de correspondência internamente , mas é realmente antes de tudo um operador para gerar uma lista de arquivos baseados em um padrão .

*/*.txt é um padrão que corresponde a uma sequência de 0 ou mais caracteres seguida por / seguido por uma sequência de zero ou mais caracteres seguida por .txt . Quando usado como um padrão de shell como em:

case $file in
  */*.txt) echo match
esac

Ele corresponderá a file=.foo/bar/baz.txt .

No entanto, */*.txt como glob é algo relacionado, mas mais complexo.

Ao expandir */*.txt em uma lista de arquivos, o shell abrirá o diretório atual, listará seu conteúdo, localizará os arquivos não ocultos do diretório type (ou symlink to directory) que correspondem a * , classifiquem essa lista , abra cada um deles, liste seu conteúdo e encontre os não ocultos que correspondem a *.txt .

Ele nunca expandirá .foo/bar/bar.txt , mesmo que isso corresponda ao padrão, porque não é assim que funciona. Por outro lado, os caminhos de arquivos gerados por um glob corresponderão a esse padrão.

Da mesma forma, um glob como foo[a/b]baz* encontrará todo o arquivo cujo nome começa com b]baz no diretório foo[a .

Então, já vimos que para globbing, mas não para correspondência de padrões, / é especial (globs são de alguma forma divididos em / e cada parte tratada separadamente) e dot-files são tratados especialmente.

Shell globbing e correspondência de padrões fazem parte da sintaxe do shell. Está entrelaçado com citações e outras formas de expansão.

$ bash -c 'case "]" in [x"]"]) echo true; esac'
true

Citando que ] remove seu significado especial (de fechar o% anterior[):

Pode até ficar bastante confuso quando você mistura tudo:

$ ls
*  \*  \a  x

$ p='\*' ksh -xc 'ls $p'
+ ls '\*' '\a'
\*  \a

OK \* é todos os arquivos que começam com \ .

$ p='\*' bash -xc 'ls $p'
+ ls '\*'
\*

Não são todos os arquivos que começam com \ . Então, de alguma forma, \ deve ter escapado do * , mas, novamente, não é igual a * ...

Para encontrar, é muito mais simples. find desce a árvore de diretórios em cada um dos argumentos de arquivo que recebe e então faz os testes conforme instruído para cada arquivo encontrado.

Para -type f , isso é verdadeiro se o arquivo for um arquivo normal, falso caso contrário, para -name <some-pattern> , se o nome do arquivo atualmente considerado corresponde ao padrão, false caso contrário. Não há nenhum conceito de arquivo oculto ou / handling ou shell quoting aqui, isso é apenas a correspondência de uma string (o nome do arquivo) contra um padrão.

Portanto, por exemplo, -name '*foo[a/b]ar' (que passa -name e *foo[a/b]ar argumentos para find ) corresponderá a foobar e .fooaar . Ele nunca corresponderá a foo/bar , mas isso porque -name corresponde ao nome do arquivo, seria com -path .

Agora, há uma forma de citar / escapar - para find - reconhecida aqui, e isso é apenas com a barra invertida. Isso permite escapar de operadores. Para o shell, ele é feito como parte da cotação usual do shell ( \ é um dos mecanismos de cotação do shell). Para find ( fnmatch() ), isso faz parte da sintaxe do padrão.

Por exemplo, -name '\**' corresponderia aos arquivos cujo nome começa com * . -name '*[\^x]*' corresponderia em arquivos cujo nome contenha ^ ou x ...

Agora, para os diferentes operadores reconhecidos por find , fnmatch() , bash e vários outros shells, todos devem concordar, pelo menos, com um subconjunto comum: * , ? e [...] .

Se um determinado shell ou find de implementação usa a função fnmatch() do sistema ou a sua própria depende da implementação. O GNU find faz pelo menos nos sistemas GNU. É muito improvável que os shells os usem, já que isso seria complicado para eles e não vale o esforço.

bash certamente não. Shells modulares como ksh, bash, zsh também têm extensões sobre * , ? , [...] e um número de opções e parâmetros especiais ( GLOBIGNORE / FIGNORE ) para afetar seu comportamento de globulação.

Além disso, observe que, além de fnmatch() , que implementa a correspondência de padrões de shell, há também as glob()

Agora, podem haver diferenças sutis entre os operadores de correspondência de padrões nessas várias implementações.

Por exemplo, para% GNUfnmatch(), ? , * ou [!x] não corresponderia a um byte ou sequência de bytes que não formam caracteres válidos enquanto bash (e a maioria dos outros shells) seria. Por exemplo, em um sistema GNU, find . -name '*' pode não corresponder a arquivos cujo nome contém caracteres inválidos, enquanto bash -c 'echo *' os listará (desde que não iniciem com . ).

Já mencionamos a confusão que pode ser incorrida citando.

    
por 16.06.2015 / 22:58