A codificação de caracteres do seu código de idioma (que você pode dizer com locale charmap
) é um multi-byte por caractere.
O mais comum hoje em dia é o UTF-8, onde os caracteres podem ser codificados em 1 a 4 bytes. Nem todas as seqüências de bytes formam caracteres válidos em UTF-8. Cada caractere não-ASCII em UTF-8 inicia com um byte que possui os dois bits mais altos e informa quantos bytes com o maior (mas não o segundo maior) conjunto de bits segue.
/dev/urandom
contém um fluxo aleatório de bytes. tr
transliterates character, então ele precisa decodificar esses bytes como caracteres. Esses caracteres ASCII no seu intervalo são todos codificados em um caractere em UTF-8, mas tr
ainda precisa decodificar todos os caracteres. Existem, por exemplo, outras codificações multi-byte em que alguns caracteres diferentes de A
contêm o byte 0x41 (o código para A
).
Porque esse fluxo aleatório de bytes é obrigado a conter seqüências inválidas (por exemplo, um byte 0x80 por si só é inválido em UTF-8 como um caractere não-ASCII tem que iniciar com um byte maior que 0xc1 (0xc0 e 0xc1 estão em nenhum caractere UTF-8)), então tr
retorna com um erro quando isso acontece.
O que você quer aqui é considerar o fluxo de bytes como caracteres em uma codificação que tenha um byte por caractere. O que você escolher não é importante, pois todos os caracteres do seu intervalo (assumindo por AZ, você significa ABCDEFGHIJKLMNOPQRSTUVWXYZ e não coisas como Ý
, Ê
) fazem parte do conjunto de caracteres portáteis, portanto, sejam codificados da mesma forma em todos os conjuntos suportados no seu sistema.
Para isso, você definiria a variável LC_CTYPE
localization, que é aquela que decide qual conjunto de caracteres é usado e o que itens como blank
, alpha
classes de caracteres contêm. Mas para a definição do intervalo A-Z, você também desejará definir a variável LC_COLLATE
(aquela que decide sobre a ordenação de string).
O C
aka POSIX
locale é aquele que garante que os caracteres sejam de um único byte e que A-Z seja ABCDEFGHIJKLMNOPQRSTUVWXYZ. Você poderia fazer:
LC_CTYPE=C LC_COLLATE=C tr -dc 'A-Za-z0-9_!@#$%^&*()+=-'
(aqui movendo o -
para o final, caso contrário, )-+
seria considerado como um intervalo como A-Z
)
Mas observe que a variável LC_ALL
substitui todas as outras variáveis LC_*
e LANG
. Então, se LC_ALL
já estiver definido, o acima não terá efeito. Então, você pode simplesmente fazer:
LC_ALL=C tr -dc 'A-Za-z0-9_!@#$%^&*()+=-'
Isso afetará outras coisas, como o idioma das mensagens de erro, mas, de qualquer forma, a alteração de LC_CTYPE já pode ter sido um problema para mensagens de erro (por exemplo, não é possível expressar mensagens de erro russas ou japonesas no conjunto de caracteres C ).