Comportamento inesperado com eco [[: digit:]]

10

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?

AbdAllah Talaat
fonte
Cuidado com: unix.stackexchange.com/q/347950/117549
Jeff Schaller

Respostas:

34

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

$ 
terdon
fonte
4
Veja também shopt -s failglobpara obter um comportamento mais útil semelhante ao das conchas modernas como zshou fish.
Stéphane Chazelas
Eu concordo com Stéphane, use failglob. nullglobpode causar problemas inesperados, por exemplo, ao colar um URL que tenha um ?.
Kevin
1
Claro, eu apenas mencionei nullglobpara demonstrar que o padrão está sendo interpretado como um globo pela casca.
terdon
14

{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}
ilkkachu
fonte
Obrigado pela sua resposta, sou novo no Linux então deixe-me por favor, pergunte-lhe como eco está relacionada a arquivos 1 3, a sua função é imprimir os seus argumentos para stdout não à procura de arquivos como a meu conhecimento
Abdallah Talaat
1
@AbdAllahTalaat isso não tem nada a ver com eco, na verdade. O shell (por exemplo, bash) "expandirá" o [[: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).
terdon
@AbdAllahTalaat, echonão procura por arquivos, o shell o faz, antes de executar o arquivo echo.
Ilkkachu 07/03/19
Especialmente porque acho que no DOS / Windows os utilitários expandem os curingas, não o shell. (Eu talvez
esteja
desculpe pessoal, mudei a resposta correta para a resposta do tedron porque o comentário dele continha o significado de que bash é o que o trabalho não ecoa ... a resposta dele também continha esse significado .. todos vocês me ajudaram ... eu gostaria de poder colocar resposta correta para todas as suas respostas e comentários
Abdallah Talaat
4

{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
DopeGhoti
fonte
Não, como as outras respostas indicam corretamente, o padrão é comparado aos nomes de arquivos.
precisa