Por que estou recebendo um erro “print_unicode: linha 9: printf: faltando um dígito unicode para \ u Unicode 0187” com este script

0

Eu os escrevi seguindo o shell script para ver como os caracteres Unicode se parecem no meu terminal.

#!/bin/bash

X=0

while [ $X -lt 65536 ]; do
    HEX='bc <<< "obase=16; $X"'
    HEX="0x${HEX}"
    UCODENAME='printf "%0*x\n" 4 $HEX'
    UCODECHAR='printf "\u%0*x\n" 4 $HEX'
    echo -e "Unicode ${UCODENAME} = ${UCODECHAR}"
    X=$((X + 1))
done

Quando executo o script, recebo a seguinte saída:

print_unicode: line 9: printf: missing unicode digit for \u
Unicode 0188 = ƈ

A segunda linha é exatamente o que estou procurando.

Eu tentei usar estritamente printf na tentativa de eliminar o erro.

#!/bin/bash

X=0

while [ $X -lt 65536 ]; do
    HEX='bc <<< "obase=16; $X"'
    HEX="0x${HEX}"
    printf 'Unicode %0*x = \u%0*x\n' 4 $HEX 4 $HEX
    X=$((X + 1))
done

Eu recebo a seguinte saída:

print_unicode: line 8: printf: missing unicode digit for \u
Unicode 037f = \u037f

A segunda linha não é o que estou procurando e ainda recebo a mesma mensagem de erro.

Como corrijo esse erro?

Bônus: Qual é a solução mais elegante para isso?

    
por G Derber 29.03.2016 / 23:19

3 respostas

1

A razão do erro que você tem é que:
O printf embutido entende o \U (ou \u ) somente quando é seguido por um número real:

$ printf '\U0021'
!

Para criar o número e também convertê-lo, um printf de dois passos é necessário (um double \ é necessário para passar pelas aspas duplas):

$ printf '%b' "$(printf '\U%04X' 33)"
!

Como você quer:

$ printf '%b' "$(printf '\u%0*X' 4 33)"
!

Isso também funciona:

$ printf '%b' "$(printf '\U%0*X' 8 33)"
!

Não há necessidade de usar bc para contar a quantidade de números hexadecimais.
bash poderia entender isso perfeitamente:

$ a=$(( 0xdef )); echo $(( a + 1 ))
3568

E para obter o valor hexadecimal de um número printf é bom o suficiente:

$ printf '0x%06x' 3568
0x000df0

O loop pode ser simplificado para:

#!/bin/bash

cp=$((0x020))     len=6

for (( cp=32; cp<$((0x010000)); cp++)); do    
    Ucode="$(printf '%b' "$(printf '\U%0*X' "$len" "$cp")")"
    printf 'Unicode U%0*x = %s\n' 4 "$cp" "$Ucode"
done

BEWARE De 0x20 a 0x010000, há muitas linhas (~ 64k linhas).

Eu aumentei o len para 6, já que o UNICODE poderia ter pontos de código de até 10FFFF.

Claro, o Ucode é totalmente definido por isso:

Ucode="$(printf '%b' "$(printf '\U%0*X' $len "$cp")")"

Observe que os pontos de código ( cp ) abaixo de dec = 32 ou HEX = 0x20 são caracteres de controle.

Mesmo que o código funcione para esses pontos de código, eu não recomendo que você toque com eles.

EXCETO para UNICODE U0000 porque o valor está sendo atribuído a uma variável.

Isto imprime %code%

$ printf '%b' "$(printf '\U%0*X' "6" "0")"

Confirme com xxd:

$ printf '%b' "$(printf '\U%0*X' "6" "0")" | xxd
0000000: 00

CAVEAT : O Bash abaixo de 4.3 não codifica corretamente os valores entre U0080 e U00FF em utf-8. Por favor use a versão 4.3 ou 4.4.

    
por 30.03.2016 / 03:07
1

Eu continuei experimentando uma solução encontrada.

#!/bin/bash

X=0

while [ $X -lt 65536 ]; do
    HEX='bc <<< "obase=16; $X"'
    HEX="0x${HEX}"
    UCODE='printf "%0*x\n" 4 $HEX'
    printf "Unicode ${UCODE} = \u${UCODE}\n"
    X=$((X + 1))
done                                                        

Eu tive a ideia de experimentar o printf desta maneira a partir de: link

Ainda estou aberto para ver soluções mais elegantes.

    
por 30.03.2016 / 00:30
1

Você pode fazer isso de uma maneira diferente (já que o bash parece ignorar as barras invertidas em torno do u in "\u" ):

#!/bin/bash

X=0

while [ $X -lt 65536 ]; do
    HEX=$(bc <<< "obase=16; $X")
    HEX="0x${HEX}"
    UCODENAME=$(printf "%0*x\n" 4 $HEX)
    UCODECHAR="\u$(printf "%0*x" 4 $HEX)"
    echo -e "Unicode ${UCODENAME} = ${UCODECHAR}"
    X=$((X + 1))
done

embora, é claro, o script ainda seja específico. Alguns outros comentários:

  • a maioria das pessoas sugeriria usar $( e ) em vez de back tics.
  • printf do bash pode imprimir Unicode diretamente (sem necessidade de eco).
  • o printf extra para UCODECHAR é redundante

Eliminando a redundância:

#!/bin/bash

X=0

while [ $X -lt 65536 ]; do
    HEX=$(bc <<< "obase=16; $X")
    HEX="0x${HEX}"
    UCODENAME=$(printf "%0*x\n" 4 $HEX)
    UCODECHAR="\u${UCODENAME}"
    echo -e "Unicode ${UCODENAME} = ${UCODECHAR}"
    X=$((X + 1))
done
    
por 30.03.2016 / 00:39