História do Bash globbing

11

Existe uma razão histórica pela qual as expressões regulares e "regulares" do Bash não são idênticas? Por exemplo, acredito que no Bash [1-2]*corresponde a qualquer coisa que comece com 1 ou 2 seguido por qualquer outra coisa, enquanto uma expressão regular [1-2]*corresponderia apenas a uma sequência de 1s e 2s. Meus scripts Bash e REGEX foo são bastante fracos e eu regularmente encontro problemas associados a essas diferenças, o que me deixou curioso para saber por que elas são diferentes.

StrongBad
fonte
3
Você consideraria fazer em rm -- ^[^.].*\.txt$vez de rm -- *.txt?
Stéphane Chazelas
1
Muitos dos seus Qs são tocados neste tópico do lwn: lwn.net/Articles/96687
slm
Existem comandos que operam em nomes de arquivos e recebem regexp. Por exemplo, find, find . -regex ".*\.txt$" | xargs rm --ou renamepara renomear arquivos (é sedpara nomes de arquivos), cuidado com o fato de que alguns sistemas têm uma aparência diferente rename.
Ctrl-alt-delor
@richard, eu ^[^.].*\.txt$deveria levar em consideração a ignorância dos arquivos de ponto. Note que o -regexé um extensões GNU, alguns escudos como ksh93 ou zsh pode incorporar regexps em suas bolhas (tente por exemplo: ksh93 -c 'echo ~(E:^[^.].*\.txt$)')
Stéphane Chazelas
2
Essa festança segue a prática existente com tanto cuidado e evita mudanças e extensões irreconciliavelmente incompatíveis é um dos seus maiores pontos fortes.
precisa saber é

Respostas:

12

bashfoi inicialmente projetado no final dos anos 80 como um clone parcial de kshalguns recursos interativos do csh / tcsh.

As origens do globbing devem ser encontradas nas conchas anteriores nas quais se baseia.

kshem si é uma extensão do shell Bourne. O próprio shell Bourne (lançado pela primeira vez em 1979 no Unix V7) foi uma implementação limpa do zero, mas não se afastou completamente do shell Thompson (o shell V1 -> V6) e incorporou recursos do shell Mashey.

Em particular, os argumentos de comando ainda estavam separados por espaços em branco, |agora era o novo operador de canal, mas ^ainda era suportado como uma alternativa (e também explica por que você faz [!a-z]e não [^a-z]), $1ainda era o primeiro argumento para um script e a barra invertida ainda era o caractere de escape . Muitos operadores regexp ( ^\|$) têm um significado especial no shell.

O shell Thompson contava com um utilitário externo para globbing. Quando shencontrado sem aspas *, [ou ?s no comando, ele executaria o comando glob.

rm *.txt

acabaria rodando glob como:

["glob", "rm", "*.txt"]

e glob acabaria executando rmcom a lista de arquivos que correspondem a esse padrão.

grep a.\*b *.txt

funcionaria globcomo:

["glob", "grep", "a.\252b", "*.txt"]

O *acima foi citado definindo o 8º bit nesse caractere, impedindo globde tratá-lo como um curinga. globremoveria esse bit antes de ligar grep.

Para fazer o equivalente a regexps, isso teria sido:

regexp rm '\.txt$'

Ou:

regexp rm '^[^.].*\.txt$'

excluir arquivos de ponto.

A necessidade de escapar dos operadores, pois eles dobram como caracteres especiais do shell, o fato de que ., comum nos nomes de arquivos, é um operador regexp, não é muito apropriado combinar nomes de arquivos e complicado para um iniciante. Na maioria dos casos, tudo o que você precisa são curingas que possam substituir um ( ?) ou qualquer número ( *) de caracteres.

Agora, diferentes invólucros adicionaram diferentes operadores de globbing. Atualmente, os globs ksh e zsh (e até certo ponto bash -O extglobque implementam um subconjunto de globs ksh) são funcionalmente equivalentes aos regexps com uma sintaxe menos trabalhosa para usar com nomes de arquivos e a sintaxe atual do shell. Por exemplo, em zsh(com extensão extendedglob), você pode:

echo a#.txt

se você deseja (improvável) corresponder a nomes de arquivos que consistem em sequências de aseguidas por .txt. Mais fácil do que echo (^a*\.txt$)(aqui, usar chaves como uma maneira de isolar os operadores regex dos operadores de shell, que poderiam ter sido uma das maneiras pelas quais os shells poderiam lidar com isso).

echo (foo|bar|<1-20>).(#i)mpg

Para arquivos mpg (sem distinção entre maiúsculas e minúsculas) cujo nome base é foo, bar ou um número decimal de 1 a 20 ...

ksh93agora também pode incorporar regexps (básico, estendido, parecido com perl ou "aumentado") em seus globos (embora seja bastante complicado) e até fornece uma ferramenta para converter entre glob e regexp ( printf %R, printf %P):

echo ~(Ei:.*\.txt)

de jogo (não oculto) txt arquivos com E Xtended expressões regulares, distinção entre maiúsculas i nsensitively.

Stéphane Chazelas
fonte
Escrita legal! Você realmente não pode usar ~(opt:pat)para nenhuma das opções em maiúsculas. Talvez print -r -- ~(Ei).*\.txt$. Colocar o padrão dentro parece ser útil apenas para evitar ter que ativar e desativar uma opção para parte de um padrão. Estranhamente, você pode misturar e combinar vários idiomas padrão dentro do mesmo globo. ~(Ki)*.~(E)txt$é equivalente. (No final, tudo é convertido em regex e passado para o mecanismo de regex da libast internamente).
Ormaaj
@ormaaj, ~(Ei:.*\.txt)funciona para mim mesmo com versões de 15 anos como o ksh93 o +.
Stéphane Chazelas
Também funciona com um dos meus binários de teste salvos (24/12/2014), mas lembro-me de ter problemas com isso. As coisas sempre foram quebradas aleatoriamente e corrigidas novamente entre cada versão quando o ksh ainda era desenvolvido comercialmente. Lembro que o código de correspondência de padrões é uma das áreas frágeis.
Ormaaj 6/06
@ormaaj, um diferente entre ~(E)xe ~(E:x)é que o último está ancorado (corresponde xapenas enquanto o anterior corresponde a qualquer coisa que contenha x), que pode ser o tipo de problema em que você se deparou (o uso ~(-lr)~(E:x)para remover a ancoragem ~(E-lr:x)não funciona). De qualquer forma, eu concordo que é bem complicado, mesmo na versão mais recente.
Stéphane Chazelas
9

Linguagens regulares foram introduzidas por Kleene em 1956. O artigo seminal não possuía a notação moderna e completa para expressões regulares, mas introduziu a “estrela Kleen”: A*significando “qualquer número de repetições de A”. Na década seguinte, surgiram algumas notações mais ou menos padronizadas, em particular .para um caráter arbitrário e ?para significar que o caractere anterior é opcional.

A notação de glamour de Bash deriva do globcomando introduzido no Unix v1 em 1971. Na época, o globbing era realizado por um programa separado; mais tarde foi movido para dentro da concha. O globcomando inicial deve ?significar "qualquer caractere" e *"qualquer sequência de caracteres". Não sei por que os personagens foram escolhidos; ?é bastante intuitivo e *pode ter sido inspirado em expressões regulares.

O globbing não tinha a intenção de ser tão geral quanto as expressões regulares, e as expressões regulares não eram muito difundidas na época, portanto não havia necessidade de unificar os conceitos. Desde o início, havia incompatibilidades sintáticas, com ?, .e *que significa coisas diferentes em padrões de nome de arquivo e em expressões regulares.

Os reservatórios modernos, como o bash, se expandem nos padrões glob, mas a evolução gradual mantém a compatibilidade com versões anteriores. O Ksh88 (a versão de 1988 do shell Korn ) introduziu uma sintaxe estendida para padrões de shell, que não podia ser a mesma sintaxe das expressões regulares usuais, mas foi fortemente inspirada por ele: *(PATTERN)significar qualquer número de repetições de PATTERN, @(PATTERN1|PATTERN2)significar " PATTERN1ou PATTERN2", etc.

As versões modernas do bash (desde a versão 2.02) suportam os padrões estendidos do ksh88, se você emitir shopt -s extglobprimeiro.

Gilles 'SO- parar de ser mau'
fonte
O Bash nunca suportou extglobs? Tanto quanto sei, Bash, zsh e {pd, m} ksh suportam exatamente os mesmos globs documentados no manual ksh88 desde os primeiros dias. Até hoje, o Ksh nem tem uma opção para desativar os quantificadores glob "estendidos", e o ksh93 é o único do grupo a ter extensões além do que o ksh88 tinha.
ormaaj
2
@ormaaj O Ksh88 estendeu os globs e a extglobopção foi introduzida no bash 2.02 em torno de 1998. O Zsh adquiriu ksh_globna série 3.1 em algum momento na mesma época. O Zsh possui muitas extensões de globbing próprias (algumas exigindo a extended_globopção).
Gilles 'SO- stop be evil'
Entendo. Portanto, já era tarde o suficiente para justificar a necessidade de uma opção. (Acho que o padrão sendo off é bastante inútil nos dias de hoje, mas, interessante.)
ormaaj
1
@ormaaj, Note que bash, ao contrário do kshextglob, o basgl não é compatível com POSIX porque não está desabilitado nas variáveis. Em ksh, var='@(*)'; echo $varexpande para todos os nomes de arquivo no diretório atual que iniciam @(e terminam )como o POSIX exige, enquanto bash -O extglobexpande para todos os arquivos. (ainda assim, pode-se considerar que o comportamento do bash faz mais sentido aqui (e o comportamento do ksh é bastante doloroso quando você deseja ter padrões em variáveis)). Essa sintaxe glob é tão estranha por causa disso (compatibilidade POSIX / Bourne). Compare com os globs estendidos do zsh.
Stéphane Chazelas
@ StéphaneChazelas Isso é tudo verdade, e eu gosto de como o ksh é um pouco inteligente sobre isso. Raramente entra em jogo, a menos que esteja realmente restrito ao POSIX. Com quase todos os usos para divisão de palavras substituídos por recursos melhores, e o armazenamento de padrões em variáveis ​​é um incômodo extremo, pois você precisa esvaziar o IFS, desative a expansão de chaves em qualquer lugar, exceto o bash. Eu acho que ainda é impossível estar completamente seguro com os padrões armazenados. Esse antigo problema de fuga nunca foi realmente resolvido, por exemplo.
Ormaaj
1

Razão histórica: SIM. Referência:
http://en.wikipedia.org/wiki/Glob_(programming)#Origin

Apenas para mostrar a divergência, aqui está um exemplo bom e fácil: a*

  • shell globbing: significado é, primeiro caractere é ae depois o que for (a, ab, abca ...)
  • regex: significado é zero ou mais repetições de caractere a(a, aa, aaa ...)

Eu concordaria prontamente que essa discrepância de significado é muito confusa para novos usuários.

O globbing é talvez mais fácil de entender para os novatos, mas também é uma construção menos poderosa.

fgeorgatos
fonte