Operador match-zero-or-more na globalização shell

6

Estou preso a um problema bastante trivial aqui: como posso fazer com que o símbolo * no bash signifique zero ou mais , como acontece em ferramentas como sed ?

Por exemplo, ak* deve corresponder a qualquer arquivo cujo nome consista inteiramente de a seguido por zero ou mais k s. Sua expansão incluiria a , ak , akk e akkk , mas não akc .

Já experimentei unsetopt sh_glob em zsh e set -o noglob no bash; eles não produziram o comportamento desejado.

    
por Kira 27.10.2015 / 00:22

4 respostas

8

Exceto pelo ksh93 , nenhum dos shells usuais tem expressões regulares com a mesma sintaxe de sed, awk, etc. que podem ser usados para arquivos correspondentes.

Ksh93, bash e zsh têm expressões regulares com uma sintaxe diferente, compatível com globs:

  • ? corresponde a qualquer caractere único (como . na sintaxe usual de regexp)
  • […] corresponde a um conjunto de caracteres basicamente da mesma maneira
  • *(FOO) corresponde a qualquer número de ocorrências de FOO (como (FOO)* na sintaxe usual de regexp)
  • similarmente +(FOO) corresponde a uma ou mais ocorrências e ?(FOO) corresponde a zero ou uma ocorrência
  • @(FOO|BAR) corresponde a FOO ou BAR
  • As correspondências aplicam-se a toda a cadeia, não a uma substring; se você quiser uma substring, coloque * no começo e no final

Essa sintaxe precisa ser ativada com shopt -s extglob no bash e com setopt ksh_glob no zsh. Então no bash você escreveria

shopt -s extglob
ls a*(k)

Veja também minha expressão regular funciona em X, mas não em Y?

Ksh93, zsh e bash podem fazer correspondência de expressões regulares com expressões regulares estendidas (basicamente a sintaxe do awk) em strings, com o operador =~ da construção [[ … ]] . Isso não é conveniente para listar arquivos, mas se você realmente quiser, isso pode ser feito.

shopt -s dotglob  # <<< include dot files, for bash
setopt globdots   # <<< include dot files, for zsh
FIGNORE='@(.|..)' # <<< include dot files, for ksh
for x in *; do
  if [[ $x =~ ^ak*$ ]]; then
    …
  fi
done
    
por 27.10.2015 / 01:08
3

ls ak{k,} exibirá os arquivos que começam com ak , seguidos por outro k ou nada.

$ touch ak akk akc
$ ls -l ak{k,}
-rw-rw-r-- 1 cas cas 0 Oct 27 10:30 ak
-rw-rw-r-- 1 cas cas 0 Oct 27 10:30 akk

globs não são regexps, mas há mais para eles do que apenas * e ? .

Se você quiser usar regexps para encontrar nomes de arquivos correspondentes, use o comando find :

$ find . -maxdepth 1 -type f -regex './ak+$' 
./ak
./akk

A opção -maxdepth 1 limita a pesquisa apenas ao diretório atual (nenhum subdiretório será pesquisado)

Se você quiser pesquisas sem distinção entre maiúsculas e minúsculas, use -iregex em vez de -regex .

Existem vários métodos para usar os arquivos encontrados por find em outros comandos. Por exemplo:

find . -maxdepth 1 -type f -regex './ak+$' -ls
find . -maxdepth 1 -type f -regex './ak+$' -exec ls -ld {} +
find . -maxdepth 1 -type f -regex './ak+$' -print0 | xargs -0r ls -ld
ls -ld $(find . -maxdepth 1 -type f -regex './ak+$')

O último exemplo é propenso a vários modos de falha, incluindo 1. não lidar com espaço em branco, etc. em nomes de arquivos, 2. limites de comprimento de linha de comando. não recomendado.

    
por 27.10.2015 / 00:33
2

Em bash , a sintaxe que você pode usar é:         %código% Isso depende da opção ls a+(k) bash shell shopt sendo ativada. No Ubuntu 14.04 GNU / Linux, isso parece estar ativado por padrão.

Funciona assim:

$ shopt extglob
extglob         on
$ ls
ak  akc  akd  akk  akkk  akkkk
$ ls a+(k)
ak  akk  akkk  akkkk
$ shopt -u extglob
$ shopt extglob
extglob         off
$ ls a+(k)
bash: syntax error near unexpected token '('
$

Do manual de bash:

+(pattern-list)

Matches one or more occurrences of the given patterns.

a pattern-list is a list of one or more patterns separated by a ‘|’.

Veja isso no Manual de Bash no link .

    
por 27.10.2015 / 01:48
2

Algumas opções dependendo do shell:

$ touch a akk aka
$ ksh -c 'echo a*(k)'
a akk
$ zsh -o kshglob -o nobareglobqual -c 'echo a*(k)'
a akk

( nobareglobqual para esse (k) à direita não deve ser considerado como um qualificador glob aqui)

$ bash -O extglob -c 'echo a*(k)'
a akk

$ zsh -o extendedglob -c 'echo ak#'
a akk

zsh # é o equivalente de regexp * .

ksh93 também pode usar vários tipos de expressões regulares em seus globs:

$ ksh93 -c 'echo ~(E:ak*)' # extended RE
a akk
$ ksh93 -c 'echo ~(P:ak*)' # perl-like RE
a akk
$ ksh93 -c 'echo ~(X:ak*)' # AT&T augmented RE
a akk
$ ksh93 -c 'echo @(~(E)ak*)' # alternative syntax
a akk
    
por 27.10.2015 / 17:39