Por que o asterisco [az] corresponde aos números?

13

Eu tenho 3 diretórios no caminho atual.

$ls
a_0db_data  a_clean_0db_data  a_clean_data
$ls a_*_data
a_0db_data:

a_clean_0db_data:

a_clean_data:

$ls a_[a-z]*_data
a_clean_0db_data:

a_clean_data:

Eu esperava que o último comando ls correspondesse apenas a_clean_data. Por que ele também coincide com o que contém 0?

bash --version
GNU bash, version 4.2.24(1)-release (i686-pc-linux-gnu)
user13107
fonte
2
Veja esta pergunta para saber mais sobre a diferença entre uma expressão regular e uma glob.
terdon
4
Então, o fato de a_*_datacorresponder a algum desses arquivos não o surpreendeu?
Cthulhu
@Cthulhu você me pegou!
user13107

Respostas:

29

A [a-z]parte não é o que corresponde ao número; é o *. Você pode estar confundindo globbing de shell e expressões regulares .

Ferramentas como grepaceitam vários tipos de expressões regulares ( básicas por padrão, -Epara estendidas, -Ppara expressões regulares Perl )

Por exemplo ( -vinverte a partida)

$ ls a_[a-z]*_data | grep -v "[0-9]"
a_clean_data

Se você deseja usar uma regex bash, aqui está um exemplo de como testar se a variável $refé um número inteiro:

re='^[0-9]+$'
if ! [[ $ref =~ $re ]] ; then
  echo "error"
fi
Sebastian
fonte
Como usar o bash regex então? (consulte tldp.org/LDP/Bash-Beginners-Guide/html/sect_04_01.html )
user13107
1
veja esta pergunta
umläute 10/09/14
21

Então o problema é: por que a_[a-z]*_data combina a_clean_0db_data?

Isso pode ser dividido em quatro partes:

  • a_coincide com o início de a_clean_0db_data, deixando clean_0db_datapara ser correspondido

  • [a-z]corresponde a qualquer caractere no intervalo a-z(por exemplo c), deixando lean_0db_dataa correspondência

  • * corresponde a qualquer número de caracteres, por exemplo lean_0db

  • _data corresponde ao final _data

Em expressões regulares, [a-z]*significaria qualquer número de caracteres (incluindo zero) no intervalo de a..z , mas você está lidando com globbing de shell, não com expressões regulares.

Se você deseja expressões regulares, algumas findimplementações têm um -regexpredicado para isso:

find . -maxdepth 1 -regex "^.*/a_[a-z]*_data$"

O -maxdepthé aqui apenas para limitar os de resultados de pesquisa para a pasta em que está. A expressão regular corresponde a toda filename, portanto, eu adicionei um ^.*/para corresponder ao caminho-parte

umläute
fonte
11

*em padrões de shell corresponde a 0 ou mais caracteres. Não deve ser confundido com o *operador de expressão regular que significa 0 ou mais do átomo anterior .

Não há equivalente a regexp *nos padrões básicos de shell. No entanto, várias conchas têm extensões para isso.

  • kshtem *(something):

    ls a_*([a-z])_data
  • você pode ter o mesmo bashcom shopt -s extglobou zshcom setopt kshglob:

    shopt -s extglob
    ls a_*([a-z])_data
  • Em zshcom extendedglobhabilitado, #é equivalente à expressão regular *:

    setopt extendedglob
    ls a_[a-z]#_data
  • Nas versões recentes de ksh93, você também pode usar expressões regulares em globs. Aqui com expressões regulares estendidas :

    ls ~(E:a_[a-z]*_data)

Observe que [a-z]corresponde a coisas diferentes, dependendo da localidade atual. Ele geralmente corresponde apenas a 26 apara zletras não acentuadas latinos na Clocalidade. Em outros locais, geralmente corresponde a mais e nem sempre faz sentido. Para corresponder uma letra em sua localidade, você pode preferir [[:alpha:]].

Stéphane Chazelas
fonte
Você poderia dar um exemplo de [a-z]correspondência mais que as 26 letras correspondentes no código C? O que me lembro de quando olhei pela última vez isso, todas as codificações praticamente usadas nas variantes do Unix tinham a ISO-646 como base (então os 128 códigos superiores eram usados ​​de maneira diferente, diretamente para caracteres em codificações como a ISO-8859-X, combinados em codificações como UTF-8 ou a família EUC). Mesmo o AIX não possuía localidades EBCDIC (pelo menos, disponíveis para mim). Lembro-me de tentar descobrir se os padrões POSIX / UNIX exigiam, mas não lembro o resultado.
APROGRAMMETRO
1
@ AProgrammer, independente da codificação, baseada na ordem de classificação (LC_COLLATE). [a-z]geralmente inclui éou í(mas não necessariamente ź) nos locais onde o conjunto de caracteres os possui, se o ponto de código nessa codificação está entre o de a e z ou não. Somente o código de idioma C garante uma ordem de classificação com base no valor do ponto de código. Veja esta outra resposta para mais detalhes.
Stéphane Chazelas
Ok, o que eu perdi foi que o intervalo foi interpretado de acordo com a seqüência de intercalação atual.
APROGRAMMETRO