Sensibilidade a maiúsculas e minúsculas em globbing de colchete

10

Normalmente, o globing do bash faz distinção entre maiúsculas e minúsculas:

$ echo c*
casefix.pike cdless chalices.py charconv.py chocolate.pike circum.py clip.pike cpustats.pike crop.pike cwk2txt.py
$ echo C*
CarePackage.md ChocRippleCake.md Clips

O uso de colchetes não parece mudar isso:

$ echo [c]*
casefix.pike cdless chalices.py charconv.py chocolate.pike circum.py clip.pike cpustats.pike crop.pike cwk2txt.py
$ echo [C]*
CarePackage.md ChocRippleCake.md Clips

Ele ainda não o altera se um hífen for usado:

$ echo [c-c]*
casefix.pike cdless chalices.py charconv.py chocolate.pike circum.py clip.pike cpustats.pike crop.pike cwk2txt.py
$ echo [C-C]*
CarePackage.md ChocRippleCake.md Clips

Mas as letras são intercaladas:

$ echo [B-C]*
CarePackage.md casefix.pike cdless chalices.py charconv.py chocolate.pike ChocRippleCake.md circum.py clip.pike Clips cpustats.pike crop.pike cwk2txt.py
$ echo [b-c]*
beehive-anthem.txt bluray2mkv.pike branch branchcleanup.pike burdayim.pike casefix.pike cdless chalices.py charconv.py chocolate.pike circum.py clip.pike cpustats.pike crop.pike cwk2txt.py

Isso sugere que o hífen está usando uma ordem de localidade "AaBbCcDd". Então: existe alguma maneira de enviar glob para todos os arquivos que começam com uma letra maiúscula?

rosuav
fonte
3
Observe também a pegadinha que [AZ] corresponde a todas as letras minúsculas, exceto 'z'!
PJTraill

Respostas:

12

Na versão 4.3 e posterior do bash, existe uma opção shopt chamada globasciiranges:

De acordo com as páginas de manual do shopt builtin gnu :

globasciiranges
Se configurada, as expressões de intervalo usadas nas expressões de colchete de correspondência de padrão (consulte Correspondência de Padrão) se comportam como se no local C tradicional ao executar comparações. Ou seja, a sequência de intercalação do código de idioma atual não é levada em consideração; portanto, 'b' não será intercalado entre 'A' e 'B', e os caracteres ASCII maiúsculos e minúsculos serão intercalados.

Como resultado, você pode

$ shopt -s globasciiranges 
$ echo [A-Z]*

Use shopt -upara desativar.

Outra maneira é mudar o código de idioma para C. Você pode fazer isso temporariamente usando um subshell:

$ ( LC_ALL=C ; printf '%s\n' [A-Z]*; )

Você obterá os resultados necessários e, quando o sub shell for concluído, o código do idioma do shell principal permanecerá inalterado em relação ao que havia antes.

Outra alternativa é, em vez de [A-Z]usar a expansão de chaves, {A..Z}juntamente com a nullglobopção bash shopt.

Ao ativar a nullglobopção, se um padrão não for correspondido durante a expansão do nome do caminho, uma cadeia nula será retornada em vez do próprio padrão.
Como resultado, este funcionará conforme o esperado:

$ shopt -s nullglob;printf '%s\n' {A..Z}*
George Vasiliou
fonte
2
Perfeito, obrigado. Não posso usar [[:upper:]]porque, na verdade, quero apenas parte do alfabeto, mas isso funciona.
rosuav
1
@rosuav Bem-vindo. Verifique também a alternativa sub shell.
George Vasiliou
“Se ativado for igual ao código de idioma C” - você quer dizer que ele afeta o código de idioma usado para globbing e nada mais? (Um link de referência teria sido útil - o melhor que posso encontrar é gnu.org/software/bash/manual/html_node/Pattern-Matching.html , mas eu preferiria uma lista de todas as opções de shell, mas faltam globosciiranges de gnu.org/software/bash/manual/html_node/… ; também a questão unix.stackexchange.com/questions/227070/… lida com esse problema extensivamente.) Também da versão 4.3.
PJTraill
@PjTrail Veja minha edição com um link de referência para todas as opções do shopt. Além disso, você pode executar man bashno seu terminal e procurar (usando /) por globasciiranges.
George Vasiliou
Não LC_ALL=C printf '%s\n' [A-Z]*funcionaria para sua segunda solução - sem um subshell? BTW: há um erro de digitação:, nullblogmas são poucos caracteres para eu corrigi-lo.
Joe
5

Você pode escrever todas as letras maiúsculas exatamente como:

[ABCDEFGHIJKLMNOPQRSTUVWXYZ]*

ou use pode usar a classe de caracteres nomeada [:upper:]para representar todas as letras maiúsculas no seu atual locale:

[[:upper:]]*

Como você notou, ao usar o intervalo, como [B-C]as letras maiúsculas e minúsculas para o mesmo caractere alfabético, estão sendo organizadas adjacentemente (de acordo com a ordem de intercalação do locale).

heemail
fonte
3

A inclusão de caracteres “não intuitivos” nos intervalos de caracteres, como a inclusão de letras minúsculas em um intervalo cujos limites sejam letras maiúsculas, deve-se à LC_COLLATEconfiguração de localidade. LC_COLLATEdeve indicar a ordem de classificação, mas faz um mau trabalho (as strings de classificação são mais complexas do que as localidades podem fazer) e você fica melhor sem ela. Eu recomendo remover LC_COLLATEdas configurações de localidade. Se você estiver configurando LANG, ou LANGUAGE, não faça isso e definir apenas o que você precisa: LC_CTYPE, LC_MESSAGES, LC_TIME.

Para obter mais informações sobre localidades, consulte Para o que devo definir minha localidade e quais são as implicações disso? e defina LC_ *, mas não LC_ALL

Para obter resultados confiáveis ​​em um script, independentemente das configurações do usuário, defina LC_ALL=C.

Gilles 'SO- parar de ser mau'
fonte
0

Conjunto:

shopt -u nocaseglob

Na página do manual do bash:

>     nocaseglob
>         If  set,  bash matches filenames in a case-insensitive
>         fashion when performing pathname expansion (see Pathname
>          Expansion above).

Se você definir 'globasciiranges', não sei o que acontecerá com caracteres não-ascii como utf-8.

Udi
fonte
0

eco [cC] * deve fazer o que você deseja, da mesma forma [A-Za-z] *

Estou aqui porque o globbing no meu sistema acabou de deixar de ser sensível a maiúsculas e minúsculas; portanto, muitos dos meus scripts não funcionam mais como deveriam :-(

user208007
fonte
É o contrário do que estou vendo. Mas verifique as outras respostas para sugestões.
rosuav