Em todos os shells que eu conheço, rm [A-Z]*
remove todos os arquivos que começam com uma letra maiúscula, mas com o bash isso remove todos os arquivos que começam com uma letra.
Como esse problema existe no Linux e Solaris com o bash-3 e o bash-4, não pode ser um bug causado por um correspondente de padrão de bugs na libc ou por uma definição de localidade com configuração incorreta.
Esse comportamento estranho e arriscado é intencional ou é apenas um bug que existe sem correção há muitos anos?
locale
? Não consigo reproduzir isso (touch foo; echo [A-Z]*
gera o padrão literal, não "foo", em um diretório vazio).# echo [A-Z]* ; export LC_COLLATE=C ; echo [A-Z]*
A b B z ZABZRespostas:
LC_COLLATE
é uma variável que determina a ordem de intercalação usada ao classificar os resultados da expansão do nome do caminho e determina o comportamento das expressões de intervalo, classes de equivalência e sequências de intercalação na expansão do nome do caminho e na correspondência de padrões.Considere o seguinte:
Observe que quando o comando
echo [a-z]
é chamado, a saída esperada seria todos os arquivos com caracteres minúsculos. Além disso, comecho [A-Z]
, arquivos com caracteres maiúsculos seriam esperados.Agrupamentos padrão com localidades, como
en_US
a seguinte ordem:a
ez
(in[a-z]
) são TODAS as letras maiúsculas, excetoZ
.A
eZ
(in[A-Z]
) são TODAS as letras minúsculas, excetoa
.Vejo:
Se você alterar a
LC_COLLATE
variável paraC
a aparência esperada:Portanto, não é um bug , é um problema de agrupamento .
Em vez de expressões de intervalo, você pode usar classes de caracteres definidas no POSIX , como
upper
oulower
. Eles também funcionam comLC_COLLATE
configurações diferentes e até com caracteres acentuados :fonte
tr
então foi isso que verifiquei primeiro.LC_COLLATE
qual também está documentado no manual.[A-Z]
inbash
corresponde a todos os elementos de intercalação (caracteres, mas também podem ser sequências de caracteres, comoDsz
nas localidades húngaras) que são classificadas apósA
e classificadas antesZ
. No seu local,c
provavelmente classifica entre B e C.Então,
c
ouz
seria correspondido por[A-Z]
, mas nãoẐ
oua
.No código C, o pedido seria:
Então
[A-Z]
iria corresponderA
,B
,C
,Z
, mas nãoÇ
e ainda nãoẐ
.Se você deseja combinar com letras maiúsculas (em qualquer script), você pode usá-lo
[[:upper:]]
. Não existe uma maneira incorporadabash
de combinar apenas letras maiúsculas no script latino (exceto listando-as individualmente).Se você quiser combinar com o
A
deZ
Inglês letras sem diacríticos, você pode usar[A-Z]
ou[[:upper:]]
mas noC
local (assumindo que os dados não são codificados em conjuntos de caracteres como BIG5 ou GB18030, que tem vários personagens cujas codificação contém a codificação dessas cartas) ou lista eles individualmente ([ABCDEFGHIJKLMNOPQRSTUVWXYZ]
).Observe que há alguma variação entre as conchas.
For
zsh
,bash -O globasciiranges
(opção de nome estranho introduzida no bash-4.3),schily-sh
eyash
,[A-Z]
corresponde aos caracteres cujo ponto de código está entre o deA
e o deZ
, portanto seria equivalente ao comportamento do código debash
idioma C.Para cinzas, mksh e cascas antigas, o mesmo que
zsh
acima, mas limitado a conjuntos de caracteres de byte único. Ou seja, em um código de idioma UTF-8, por exemplo,[É-Ź]
não corresponderiaÓ
, mas, como isso[<c3><89>-<c5><b9>]
, corresponderia aos valores de bytes 0x89 a 0xc5!ksh93
comporta-se comobash
exceto que trata como intervalos de casos especiais cujas extremidades começam com letras minúsculas ou maiúsculas. Nesse caso, ele corresponde apenas aos elementos de intercalação que se classificam entre essas extremidades, mas que são (ou o primeiro caractere para elementos de intercalação de vários caracteres) também em minúsculas (ou maiúsculas, respectivamente). Portanto[A-Z]
, haveria correspondência emÉ
, mas não em,e
comoe
a classificação entreA
eZ
mas não é maiúscula comoA
eZ
.Para
fnmatch()
padrões (como emfind -name '[A-Z]'
) ou expressões regulares do sistema (como emgrep '[A-Z]'
), isso depende do sistema e da localidade. Por exemplo, em um sistema GNU aqui,[A-Z]
não corresponde no códigox
doen_GB.UTF-8
idioma, mas noth_TH.UTF-8
. Não está claro para mim quais informações são usadas para determinar isso, mas aparentemente são baseadas em uma tabela de pesquisa derivada dos dados do código de idioma LC_COLLATE ).Todos os comportamentos são permitidos pelo POSIX, pois o POSIX deixa o comportamento dos intervalos não especificados em códigos de idioma que não sejam o código C. Agora podemos discutir sobre os benefícios de cada abordagem.
bash
A abordagem de faz muito sentido[C-G]
, pois queremos que os personagens entreC
eG
. E usar a ordem de classificação do usuário para o que determina o que é intermediário é a abordagem mais lógica.Agora, o problema é que isso quebra as expectativas de muitas pessoas, especialmente aquelas que estão acostumadas com o comportamento tradicional do pré-Unicode, mesmo nos dias anteriores à internacionalização. Embora, para um usuário normal, faça sentido que
[C-I]
incluah
como ah
letra está entreC
eI
e que[A-g]
não incluaZ
, é uma questão diferente para as pessoas que lidam com o ASCII apenas por décadas.Esse
bash
comportamento também é diferente do[A-Z]
intervalo correspondente em outras ferramentas GNU, como nas expressões regulares do GNU (como emgrep
/sed
...) oufnmatch()
como emfind -name
.Isso também significa que o que
[A-Z]
corresponde varia de acordo com o ambiente, com o sistema operacional e com a versão do sistema operacional. O fato de[A-Z]
corresponder Á, mas não Ź também é subótimo.Para
zsh
/yash
, usamos uma ordem de classificação diferente. Em vez de confiar na noção de ordem de caracteres do usuário, usamos os valores do código do ponto de caractere. Isso tem o benefício de ser fácil de entender, mas de um ponto prático de poucos, fora do ASCII, não é muito útil.[A-Z]
corresponde às 26 letras maiúsculas em inglês dos EUA,[0-9]
corresponde aos dígitos decimais. Existem pontos de código no Unicode que seguem a ordem de alguns alfabetos, mas isso não é generalizado e não pode ser generalizado, pois pessoas diferentes que usam o mesmo script não necessariamente concordam com a ordem das letras.Para shells e mksh tradicionais, traço, está quebrado (agora que a maioria das pessoas usa caracteres de vários bytes), mas principalmente porque ainda não têm suporte para vários bytes. A adição de suporte de vários bytes a shells como
bash
ezsh
tem sido um grande esforço e ainda está em andamento.yash
(shell japonês) foi projetado inicialmente com suporte a vários bytes desde o início.A abordagem do ksh93 tem o benefício de ser consistente com as expressões regulares do sistema ou fnmatch () (ou pelo menos parece ser pelo menos nos sistemas GNU). Lá, isso não quebra a expectativa de algumas pessoas, pois
[A-Z]
não inclui letras minúsculas,[A-Z]
incluiÉ
(e Á, mas não Ź). Não é consistente comsort
ou geralmentestrcoll()
ordem.fonte
mksh
(ambos derivados do pdksh).posh -c $'case Ó in [É-Ź]) echo yes; esac'
não retorna nada.sort
porque osbash
globs são baseados na ordem de classificação dos caracteres. No momento, não tenho acesso a uma versão tão antiga dobash
, mas posso verificar mais tarde. Foi diferente então?\xFF
existe o byte 0xFF, não o caractere U + 00FF (ÿ
ele próprio codificado como 0xC3 0xBF).\xFF
sozinho não forma um caractere válido, portanto não vejo por que ele deve corresponder[É-Ź]
.Ele foi planejado e documentado na
bash
documentação, seção de correspondência de padrões . A expressão de intervalo[X-Y]
incluirá todos os caracteres entreX
eY
usando a sequência de intercalação e o conjunto de caracteres do código de idioma atual:Você pode ver,
b
classificado entreA
eZ
noen_US.utf8
código do idioma.Você tem algumas opções para evitar esse comportamento:
ou ativar
globasciiranges
(com bash 4.3 e acima):fonte
Eu observei esse comportamento em uma nova instância do Amazon EC2. Como o OP não ofereceu um MCVE , postarei um:
Portanto, não ter meu
LC_*
conjunto leva o lançamento do bash 4.1.2 (1) no Linux para produzir um comportamento aparentemente estranho. Posso alternar com segurança o comportamento ímpar definindo e desabilitando as respectivas variáveis de localidade. Sem surpresa, esse comportamento parece consistente através da exportação:Enquanto estou vendo o bash se comportar quando Stéphane "Shellshock" Chazelas respondeu , acho que a documentação do bash sobre correspondência de padrões é buggy:
Eu li essa frase (ênfase minha) como "se as variáveis de localidade relevantes não estiverem definidas, o bash será padronizado como a localidade C". Bash não parece estar fazendo isso. Em vez disso, parece estar padronizado para uma localidade em que os caracteres são classificados em ordem de dicionário com dobras diacríticas:
Eu acho que seria bom para o bash documentar como ele se comportará quando
LC_*
(especificamenteLC_CTYPE
eLC_COLLATE
) estiver indefinido. Mas enquanto isso, vou compartilhar um pouco de sabedoria :e
Atualização Com base no comentário do @ G-Man, vamos analisar mais profundamente o que está acontecendo:
Ah ha! Isso explica o agrupamento visto anteriormente. Vamos remover todas as variáveis de localidade:
Aqui vamos nós. Agora, o bash opera de forma consistente com relação à documentação neste sistema Linux. Se qualquer uma das variáveis de região são definidos (
LANGUAGE
,LANG
,LC_COLLATE
,LC_CTYPE
,LC_ALL
, etc.), em seguida, Bash usa aqueles de acordo com o manual. Caso contrário, o bash volta para C.O FAQ do bash do Wooledge tem o seguinte a dizer:
Portanto, o aparente problema, tanto na operação quanto na documentação, pode ser explicado analisando a soma total de todas as variáveis de localização do código do idioma.
fonte
C
localidade, isso é um bug.env | grep LANG
ouecho "$LANG"
.LANG
. Com essa dica, tudo é explicado.A localidade pode alterar quais caracteres são correspondidos
[A-Z]
. Usarpara eliminar a influência. (Eu usei um subshell para localizar a alteração).
fonte
export LC_ALL=C
primeiro.Como já foi dito, esse é um problema de "ordem de classificação".
O intervalo az pode conter letras maiúsculas em alguns locais:
A solução correta desde o bash 4.3 é definir a opção
globasciiranges
:para fazer festa de agir como se
LC_COLLATE=C
foi definido no glob faixas ing.fonte
Parece que encontrei a resposta certa para minha própria pergunta:
O Bash é um buggy, pois não gerencia seu próprio local. Portanto, definir LC_ * em um processo bash não tem efeito nesse processo de shell.
Se você definir LC_COLLATE = C e depois iniciar outro bash, a globbing funcionará conforme o esperado no novo processo do bash.
fonte
export
isso corretamente.