Se eu executar o seguinte script simples:
#!/bin/bash
printf "%-20s %s\n" "Früchte und Gemüse" "foo"
printf "%-20s %s\n" "Milchprodukte" "bar"
printf "%-20s %s\n" "12345678901234567890" "baz"
Imprime:
Früchte und Gemüse foo
Milchprodukte bar
12345678901234567890 baz
ou seja, o texto com tremas (como ü
) é "reduzido" por um caractere por trema.
Certamente, tenho alguma configuração errada em algum lugar, mas não consigo descobrir qual poderia ser.
Isso ocorre se a codificação do arquivo for UTF-8.
Se eu alterar sua codificação para latin-1, o alinhamento está correto, mas os tremas estão incorretos:
Fr�chte und Gem�se foo
Milchprodukte bar
12345678901234567890 baz
echo Früchte und Gemüse | wc -c -m
a diferença.printf
é.Respostas:
POSIX requer
printf
é%-20s
para contar os 20 em termos de bytes não caracteres apesar de que faz pouco sentido comoprintf
é imprimir texto , formatado (ver discussão no Grupo de Austin (POSIX) ebash
listas de discussão).O
printf
embutidobash
e a maioria dos outros cartuchos POSIX honram isso.zsh
ignora esse requisito bobo (mesmo emsh
emulação) eprintf
funciona como você esperaria lá. O mesmo para oprintf
built-infish
(não é um shell semelhante ao POSIX).O
ü
caractere (U + 00FC), quando codificado em UTF-8, é composto de dois bytes (0xc3 e 0xbc), o que explica a discrepância.Essa cadeia é composta por 18 caracteres, tem 18 colunas de largura (
-L
sendo umawc
extensão GNU para relatar a largura de exibição da linha mais larga na entrada), mas é codificada em 20 bytes.Em
zsh
oufish
, o texto seria alinhado corretamente.Agora, também existem caracteres com largura 0 (como combinar caracteres como U + 0308, a diarese combinada) ou largura dupla como em muitos scripts asiáticos (para não mencionar caracteres de controle como Tab) e nem
zsh
alinharem aqueles corretamente.Exemplo, em
zsh
:Em
bash
:ksh93
possui uma%Ls
especificação de formato para contar a largura em termos de largura de exibição .Isso ainda não funciona se o texto contiver caracteres de controle como TAB (como poderia?
printf
Teria que saber a que distância as paradas de tabulação estão no dispositivo de saída e em que posição ele começa a imprimir). Ele funciona acidentalmente com caracteres de backspace (como naroff
saída em queX
(negritoX
) é escrito comoX\bX
), emboraksh93
considere todos os caracteres de controle como tendo uma largura de-1
.Como outras opções, você pode tentar:
Isso funciona com algumas
expand
implementações (embora não seja do GNU).Nos sistemas GNU, você pode usar o GNU
awk
cujasprintf
contagens em caracteres (não bytes, nem larguras de exibição, ainda não estão OK para os caracteres de 0 ou 2 de largura, mas sim para sua amostra):Se a saída for para um terminal, você também pode usar seqüências de escape de posicionamento do cursor. Gostar:
fonte
ü
caracter pode ser composto comou
+¨
, que é de 3 bytes. No caso da pergunta, ele é codificado como 2 caracteres, mas nem todosü
são criados igualmente.u\u308
tem dois caracteres (wc -m
pelo menos no Unix / sentido) para um glifo / graphem / graphem-cluster e já é mencionado e incluído nesta resposta.printf(3)
(pouco sentido depois do requisito C99 que você mencionou, obrigado por isso), mas não oprintf(1)
utilitário, pois todo operador de shell ou outro utilitário de texto lida com caracteres (ou foi modificado para também lidar com caracteres como owc
que obteve um-m
(enquanto-c
permaneceu byte ) oucut
que obteve um-b
depois-c
pode significar algo além de bytes).Na verdade, não, mas seu terminal não fala latim-1 e, portanto, você recebe lixo em vez de trema.
Você pode corrigir isso usando iconv:
(ou apenas execute todo o script do shell canalizado para iconv)
fonte