Por que é echo {1,2,3}expandido para 1 2 3, que é um comportamento esperado, enquanto echo [[:digit:]]retorna [[:digit:]]enquanto eu esperava imprimir todos os dígitos de 0a 9?
Porque são duas coisas diferentes. O {1,2,3}é um exemplo de expansão de contraventamento . A {1,2,3}construção é expandida pelo shell , antes echomesmo de vê-lo. 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á para 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:]]é um shell glob que corresponderá a qualquer dígito. Agora, no bash, se um shell glob não corresponder a nada, ele será expandido para si mesmo:
Se o glob corresponder a algo, isso será impresso:
$ echo /e*c
+ echo /etc
/etc
Em ambos os casos, echoapenas imprime o que o shell disser para imprimir, mas no segundo caso, como o glob corresponde a algo ( /etc), ele é instruído a imprimir esse algo.
Portanto, como você não possui nenhum arquivo ou diretório cujo nome consista em exatamente um dígito (que é o [[:digit:]]que corresponderia), o glob é expandido para si mesmo e você obtém:
$ echo [[:digit:]]
[[:digit:]]
Agora, tente criar um arquivo chamado 5e executar 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 na man bashexplicação das nullglobopções 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.
{1,2,3}é expansão de colchetes , ela se expande para as palavras listadas sem levar em consideração seu significado.
[...]é um grupo de caracteres, usado na expansão do nome do arquivo (ou curinga ou glob) de forma semelhante ao asterisco *e ao ponto de interrogação ?. Ele corresponde a qualquer caractere único listado dentro ou caracteres que são membros de grupos nomeados, como [:digit:]se estivessem 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ê realmente 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:
$ 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 echoou rm. Observe também que, se algum deles for citado, eles não serão expandidos:
$ bash -c 'echo "[[:digit:]]"' # even though matching files still exist
[[:digit:]]
$ bash -c 'echo "{1,2,3}"'
{1,2,3}
{1,2,3}(e, por exemplo {1..3}, são expansões de colchetes . Eles são interpretados pelo shell antes da execução do comando.
[[:digit:]]é um token de correspondência de padrão , mas você não o está usando em um local com nenhum arquivo que corresponda a esse padrão. Se você usar uma correspondência de padrão que não possui correspondências, ela se expandirá:
Porque são duas coisas diferentes. O
{1,2,3}
é um exemplo de expansão de contraventamento . A{1,2,3}
construção é expandida pelo shell , antesecho
mesmo de vê-lo. Você pode ver o que acontece se você usarset -x
:Como você pode ver, o comando
echo {1,2,3}
é expandido para:No entanto,
[[:digit:]]
é uma classe de caracteres POSIX . Quando você dá paraecho
, 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ê executasseecho *
o que imprimiria todos os arquivos no diretório atual. Mas[[:digit:]]
é um shell glob que corresponderá a qualquer dígito. Agora, no bash, se um shell glob não corresponder a nada, ele será expandido para si mesmo:Se o glob corresponder a algo, isso será impresso:
Em ambos os casos,
echo
apenas imprime o que o shell disser para imprimir, mas no segundo caso, como o glob corresponde a algo (/etc
), ele é instruído a imprimir esse algo.Portanto, como você não possui nenhum arquivo ou diretório cujo nome consista em exatamente um dígito (que é o
[[:digit:]]
que corresponderia), o glob é expandido para si mesmo e você obtém:Agora, tente criar um arquivo chamado
5
e executar o mesmo comando:E se houver mais de um arquivo correspondente:
Isso é (mais ou menos) documentado na
man bash
explicação dasnullglob
opções que desativa esse comportamento:Se você definir esta opção:
{1,2,3}
é expansão de colchetes , ela se expande para as palavras listadas sem levar em consideração seu significado.[...]
é um grupo de caracteres, usado na expansão do nome do arquivo (ou curinga ou glob) de forma semelhante ao asterisco*
e ao ponto de interrogação?
. Ele corresponde a qualquer caractere único listado dentro ou caracteres que são membros de grupos nomeados, como[:digit:]
se estivessem 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ê realmente 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:
Mas ainda:
Ambos são expandidos pelo shell , não importa se o comando que você está executando é
ls
, ouecho
ourm
. Observe também que, se algum deles for citado, eles não serão expandidos:{1,2,3}
(e, por exemplo{1..3}
, são expansões de colchetes . Eles são interpretados pelo shell antes da execução do comando.[[:digit:]]
é um token de correspondência de padrão , mas você não o está usando em um local com nenhum arquivo que corresponda a esse padrão. Se você usar uma correspondência de padrão que não possui correspondências, ela se expandirá: