Comportamento inesperado com echo [[: digit:]]

9

Gostaria de perguntar:

Por que echo {1,2,3} é expandido para 1 2 3, o que é um comportamento esperado, while echo [[:digit:]] retorna [[:digit:]] enquanto esperava imprimir todos os dígitos de 0 para 9 ?

    
por AbdAllah Talaat 07.03.2018 / 19:36

3 respostas

34

Porque são duas coisas diferentes. O {1,2,3} é um exemplo de expansão de contraventores . A construção {1,2,3} é expandida pelo shell , antes que echo a veja. Você pode ver o que acontece se você usar set -x :

$ set -x
$ echo {1,2,3}
+ echo 1 2 3
1 2 3

Como você pode ver, o comando echo {1,2,3} é expandido para:

echo 1 2 3

No entanto, [[:digit:]] é uma classe de caracteres POSIX . Quando você dá a echo , o shell também o processa primeiro, mas desta vez ele está sendo processado como um shell glob . funciona da mesma forma como se você executasse echo * , o que imprimiria todos os arquivos no diretório atual. Mas [[:digit:]] é uma globalização que corresponderá a qualquer dígito. Agora, no bash, se um shell glob não corresponder a nada, ele será expandido para si mesmo:

$ echo /this*matches*no*files
+ echo '/this*matches*no*files'
/this*matches*no*files

Se o glob corresponder a algo, isso será impresso:

$ echo /e*c
+ echo /etc
/etc

Em ambos os casos, echo apenas imprime o que o shell lhe diz para imprimir, mas no segundo caso, como o glob corresponde a algo ( /etc ), é dito para imprimir esse item.

Portanto, como você não possui arquivos ou diretórios cujo nome consista exatamente em um dígito (que é o que [[:digit:]] corresponderia), o glob é expandido para si mesmo e você obtém:

$ echo [[:digit:]]
[[:digit:]]

Agora, tente criar um arquivo chamado 5 e executando o mesmo comando:

$ echo [[:digit:]]
5

E se houver mais de um arquivo correspondente:

$ touch 1 5       
$ echo [[:digit:]]
1 5

Isso é (mais ou menos) documentado em man bash na explicação das opções nullglob , o que desativa esse comportamento:

nullglob
    If  set,  bash allows patterns which match no files (see
    Pathname Expansion above) to expand to  a  null  string,
    rather than themselves.

Se você definir esta opção:

$ rm 1 5
$ shopt -s nullglob
$ echo [[:digit:]]  ## prints nothing

$ 
    
por 07.03.2018 / 19:45
14

{1,2,3} é expansão de contraventamento , se expande para as palavras listadas sem levar em conta o significado delas.

[...] é um grupo de caracteres usado em expansão de nome de arquivo (ou curinga ou glob) de forma semelhante ao asterisco * e ao ponto de interrogação ? . Ele corresponde a qualquer caractere listado ou caracteres que sejam membros de grupos nomeados, como [:digit:] , se estiverem listados. O comportamento padrão da maioria dos shells é deixar o curinga como está se não houver arquivos que correspondam a ele.

(Observe que você não pode transformar um curinga / padrão no conjunto de strings que ele corresponderia. O asterisco pode corresponder a qualquer string de qualquer tamanho, portanto, expandir qualquer padrão que o contenha produziria uma lista infinita de strings.)

Então:

$ bash -c 'echo [[:digit:]]'           # bash leaves it as-is
[[:digit:]]
$ zsh -c 'echo [[:digit:]]'            # zsh by default complains if no match
zsh:1: no matches found: [[:digit:]]
$ touch 1 3 d i g t
$ bash -c 'echo [[:digit:]]'           # now there are two matches
1 3                                    # note that d, i, g and t do NOT match

Mas ainda assim:

$ bash -c 'echo {1,2,3}'
1 2 3

Ambos são expandidos pelo shell , não importa se o comando que você está executando é ls ou echo ou rm . Observe também que, se um deles for citado, ele não será expandido:

$ bash -c 'echo "[[:digit:]]"'         # even though matching files still exist
[[:digit:]]
$ bash -c 'echo "{1,2,3}"'
{1,2,3}
    
por 07.03.2018 / 19:49
4

{1,2,3} (e e. g. {1..3} são expansões de chaves . Elas são interpretadas pelo shell antes da execução do comando.

[[:digit:]] é um token padrão correspondente , mas você não o está usando em um local com arquivos que correspondam a esse padrão. Se você usar uma correspondência de padrão que não tenha correspondências, ela se expandirá para si mesma:

$ echo [[:digit:]]; touch 3; echo [[:digit:]]
[[:digit:]]
3
    
por 07.03.2018 / 19:44