Eu gostaria de perguntar:
Por que é echo {1,2,3}
expandido para 1 2 3, que é um comportamento esperado, enquanto echo [[:digit:]]
retorna [[:digit:]]
enquanto eu esperava que ele imprimisse todos os dígitos de 0
até 9
?
Eu gostaria de perguntar:
Por que é echo {1,2,3}
expandido para 1 2 3, que é um comportamento esperado, enquanto echo [[:digit:]]
retorna [[:digit:]]
enquanto eu esperava que ele imprimisse todos os dígitos de 0
até 9
?
Respostas:
Porque são duas coisas diferentes. Este {1,2,3}
é um exemplo de expansão de chaves . A {1,2,3}
construção é expandida pelo shell , antes echo
mesmo 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ê o entrega echo
, o shell também o processa primeiro, mas desta vez está sendo processado como um shell glob . funciona da mesma maneira como se você executasse, o echo *
que imprimirá todos os arquivos no diretório atual. Mas [[:digit:]]
é um glob de shell que irá 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 a glob corresponder a algo, isso será impresso:
$ echo /e*c
+ echo /etc
/etc
Nos dois casos, echo
apenas imprime o que o shell /etc
solicitar , mas no segundo caso, como a glob corresponde a algo ( ), é solicitado que imprima essa coisa.
Portanto, como você não possui nenhum arquivo ou diretório cujo nome consiste em exatamente um dígito (que é o [[:digit:]]
que corresponderia), o globo é 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 está documentado na man bash
explicação das nullglob
opções que desabilitam 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
$
shopt -s failglob
para obter um comportamento mais útil semelhante ao das conchas modernas como zsh
ou fish
.
failglob
. nullglob
pode causar problemas inesperados, por exemplo, ao colar um URL que tenha um ?
.
nullglob
para demonstrar que o padrão está sendo interpretado como um globo pela casca.
{1,2,3}
Como expansão entre chaves , ela se expande para as palavras listadas sem levar em consideração o significado delas.
[...]
é um grupo de caracteres usado na expansão do nome do arquivo (ou curinga ou glob) de maneira semelhante ao asterisco *
e ao ponto de interrogação ?
. Corresponde a qualquer caractere único listado ou caracteres membros de grupos nomeados, como [:digit:]
se eles estivessem listados. O comportamento padrão da maioria dos shells é deixar o curinga como está se não houver arquivos correspondentes.
(Observe que você não pode realmente transformar um curinga / padrão no conjunto de seqüências de caracteres que ele corresponderia. O asterisco pode corresponder a qualquer sequência de qualquer comprimento, portanto, expandir qualquer padrão que a contenha produziria uma lista infinita de seqüências de caracteres.)
Assim:
$ 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 echo
ou rm
. Observe também que, se qualquer um desses itens 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}
[[:digit:]]
antes de passá-lo para echo
, então echo
nunca o vê [[:digit:]]
, ele vê apenas 1 3
. Você pode ver isso em ação executando, o set -x
que imprimirá os comandos reais sendo executados (execute set +x
para desativá-lo novamente).
echo
não procura por arquivos, o shell o faz, antes de executar o arquivo echo
.
{1,2,3}
(e, por exemplo, {1..3}
são expansões entre chaves . 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 arquivos que correspondam a esse padrão. Se você usar uma correspondência de padrão que não possui correspondências, ela se expandirá para si mesma:
$ echo [[:digit:]]; touch 3; echo [[:digit:]]
[[:digit:]]
3