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.