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 0até 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 0até 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 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ê 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, echoapenas imprime o que o shell /etcsolicitar , 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 5e 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 bashexplicação das nullglobopçõ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 failglobpara obter um comportamento mais útil semelhante ao das conchas modernas como zshou fish.
failglob. nullglobpode causar problemas inesperados, por exemplo, ao colar um URL que tenha um ?.
nullglobpara 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 echoou 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 echonunca o vê [[:digit:]], ele vê apenas 1 3. Você pode ver isso em ação executando, o set -xque imprimirá os comandos reais sendo executados (execute set +xpara desativá-lo novamente).
echonã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