Como imprimir uma variável que contenha caracteres não imprimíveis?

5

Eu quero exibir o valor da variável $IFS , que pode conter caracteres não imprimíveis (por exemplo: newline).

Eu usei o seguinte comando para fazer isso:

echo -n "$IFS" | hexdump -C

Que funcionou bem no meu caso.

Mas há algo errado em usar este comando? Por exemplo, echo substitui alguns caracteres não imprimíveis por outros caracteres antes de imprimi-los em seu stdout ou algum outro problema como esse?

    
por user270971 17.01.2018 / 11:43

3 respostas

5

Especialmente com IFS , você absolutamente quer citá-lo, já que, caso contrário, ele se transforma em nada. Você já fez isso, então não há problema.

Quanto a echo , isso depende do shell. Algumas versões do echo manipulam escapes de barra invertida por padrão, outras não. Bash não faz, zsh faz:

$ bash -c 'echo "foo\nbar"'
foo\nbar
$ zsh -c 'echo "foo\nbar"'
foo
bar

É melhor usar printf : printf "%s" "$IFS" | hexdump -C .

Veja também: Por que o printf é melhor que o eco?

printf "%q" "$IFS" também funciona no Bash e no zsh.

Isso deve mantê-lo bem, exceto que Bash não pode lidar com bytes NUL ( %code% ), zsh can. Bash:

$ var=$'foo
$ var=$'foo
$ bash -c 'echo "foo\nbar"'
foo\nbar
$ zsh -c 'echo "foo\nbar"'
foo
bar
bar' $ printf "%q\n" "$var" foo$'
$ var=$'foo
$ var=$'foo%pre%bar'
$ printf "%q\n" "$var"
foo$'%pre%'bar
bar' $ printf "%q\n" "$var" foo
'bar
bar' $ printf "%q\n" "$var" foo

zsh:

%pre%     
por 17.01.2018 / 12:03
2

Algumas abordagens diferentes para dar representações visuais de strings:

POSIX

$ printf %s "$IFS" | od -vtc -to1
0000000      \t  \n  
$ ksh93 -c 'typeset -p IFS'
IFS=$' \t\n'
$ zsh -c 'typeset -p IFS'
typeset IFS=$' \t\n\C-@'
$ mksh -c 'typeset -p IFS'
typeset IFS=$' \t\n'
$ a=$'\u00e9e\u301\u200b' ksh -c 'typeset -p a'
typeset -x a=$'\u[e9]e\u[301]\u[200b]'
040 011 012 000 0000004 $ printf '%s\n' "$IFS" | LC_ALL=C sed -n l \t$
$ a=$'\u00e9e\u301\u200b' bash -c 'printf "%q\n" "$IFS" "$a" ""'
$' \t\n'
éé​
''
$ a=$'\u00e9e\u301\u200b' ksh -c 'printf "%q\n" "$IFS" "$a" ""'
$' \t\n'
$'\u[e9]e\u[301]\u[200b]'
''
\ $'\t'$'\n'$'
$ a=$'\u00e9e\u301\u200b' zsh -c 'print -r -- ${(qqqq)a}'
$'éé​'
$ a=$'\u00e9e\u301\u200b' zsh -c '(){local LC_ALL=C; print -r -- ${(qqqq)a}}'
$'31e41203'
' éé​ '' $ a=$'\u00e9e\u301\u200b' sh -c '/usr/bin/printf "%q\n" "$IFS" "$a" ""' ' '$'\t\n' éé​ '' $ a=$'\u00e9e\u301\u200b' zsh -c 'LC_ALL=C printf "%q\n" "$IFS" "$a" ""' \ $'\t'$'\n'$'
$ printf %s "$IFS" | od -vtc -to1
0000000      \t  \n  
$ ksh93 -c 'typeset -p IFS'
IFS=$' \t\n'
$ zsh -c 'typeset -p IFS'
typeset IFS=$' \t\n\C-@'
$ mksh -c 'typeset -p IFS'
typeset IFS=$' \t\n'
$ a=$'\u00e9e\u301\u200b' ksh -c 'typeset -p a'
typeset -x a=$'\u[e9]e\u[301]\u[200b]'
040 011 012 000 0000004 $ printf '%s\n' "$IFS" | LC_ALL=C sed -n l \t$
$ a=$'\u00e9e\u301\u200b' bash -c 'printf "%q\n" "$IFS" "$a" ""'
$' \t\n'
éé​
''
$ a=$'\u00e9e\u301\u200b' ksh -c 'printf "%q\n" "$IFS" "$a" ""'
$' \t\n'
$'\u[e9]e\u[301]\u[200b]'
''
\ $'\t'$'\n'$'
$ a=$'\u00e9e\u301\u200b' zsh -c 'print -r -- ${(qqqq)a}'
$'éé​'
$ a=$'\u00e9e\u301\u200b' zsh -c '(){local LC_ALL=C; print -r -- ${(qqqq)a}}'
$'31e41203'
' éé​ '' $ a=$'\u00e9e\u301\u200b' sh -c '/usr/bin/printf "%q\n" "$IFS" "$a" ""' ' '$'\t\n' éé​ '' $ a=$'\u00e9e\u301\u200b' zsh -c 'LC_ALL=C printf "%q\n" "$IFS" "$a" ""' \ $'\t'$'\n'$'%pre%' $'3'$'1'e$'4'$'1'$'2'$'0'$'3' '' $ a=$'\u00e9e\u301\u200b' bash -c 'LC_ALL=C printf "%q\n" "$IFS" "$a" ""' $' \t\n' $'31e41203' ''
0$
' $'3'$'1'e$'4'$'1'$'2'$'0'$'3' '' $ a=$'\u00e9e\u301\u200b' bash -c 'LC_ALL=C printf "%q\n" "$IFS" "$a" ""' $' \t\n' $'31e41203' ''
0$

(o \n extra é necessário, caso contrário, o comportamento de sed não é especificado se a última linha não terminar em uma nova linha). Um POSIX sh não terá NUL em $IFS como o meu zsh faz aqui. O comportamento de sed não é especificado se a entrada contiver NULs.

Shell embutidos

  • typeset -p (ksh, zsh, bash, yash) pode fornecer uma saída não ambígua para algumas strings.

    %pre%

    mas para este último (usando Unicode combinando acento agudo e caracteres de espaço com largura zero), o zsh / mksh não está ajudando (mesmo com LC_ALL=C typeset -p a com mksh -o utf8-mode ). A saída de bash geralmente não é inequívoca quando enviada para um terminal.

  • printf %q com GNU printf e printf incorporado em ksh93 , zsh e bash :

    %pre%
  • q , qq , qqq , qqqq sinalizadores de expansão de parâmetro em zsh .

    para vários tipos de cotação, qqqq é o mesmo para $'...' :

    %pre%

    Há também q e q+ que só usa a citação para coisas que precisam (embora ainda com a ressalva para aqueles unicode).

vários comandos não padrão:

  • hexadamixador: hexdump , hd , xxd ... Você deseja fornecer a saída de printf %s "$var" (ou print -rn -- "$var" com ksh / zsh ou echo -nE - "$var" com zsh ...).

  • cat -vte ou cat -A

  • uconv -x hex para pontos de código unicode de caracteres (em oposição ao valor hexadecimal dos bytes da codificação), somente para UTF-8 (é possível pré-processar a entrada com iconv -t utf-8 , desde que seja um texto válido em codificação do locale)

  • uconv -x name para os nomes dos personagens

  • recode ..dump . tanto hex e nome, mas sabe sobre menos caracteres Unicode (não atualizado com versões mais recentes do Unicode). Funciona em localidades não-UTF-8 embora.

por 17.01.2018 / 15:28
0

O único caractere que não funciona no bash é o nulo.

$ var="$(perl -wE 'print map chr, 0 .. 255')"
$ echo -n "$var" | xxd
0000000: 0102 0304 0506 0708 090a 0b0c 0d0e 0f10  ................
0000010: 1112 1314 1516 1718 191a 1b1c 1d1e 1f20  ............... 
0000020: 2122 2324 2526 2728 292a 2b2c 2d2e 2f30  !"#$%&'()*+,-./0
0000030: 3132 3334 3536 3738 393a 3b3c 3d3e 3f40  123456789:;<=>?@
0000040: 4142 4344 4546 4748 494a 4b4c 4d4e 4f50  ABCDEFGHIJKLMNOP
0000050: 5152 5354 5556 5758 595a 5b5c 5d5e 5f60  QRSTUVWXYZ[\]^_'
0000060: 6162 6364 6566 6768 696a 6b6c 6d6e 6f70  abcdefghijklmnop
0000070: 7172 7374 7576 7778 797a 7b7c 7d7e 7f80  qrstuvwxyz{|}~..
0000080: 8182 8384 8586 8788 898a 8b8c 8d8e 8f90  ................
0000090: 9192 9394 9596 9798 999a 9b9c 9d9e 9fa0  ................
00000a0: a1a2 a3a4 a5a6 a7a8 a9aa abac adae afb0  ................
00000b0: b1b2 b3b4 b5b6 b7b8 b9ba bbbc bdbe bfc0  ................
00000c0: c1c2 c3c4 c5c6 c7c8 c9ca cbcc cdce cfd0  ................
00000d0: d1d2 d3d4 d5d6 d7d8 d9da dbdc ddde dfe0  ................
00000e0: e1e2 e3e4 e5e6 e7e8 e9ea ebec edee eff0  ................
00000f0: f1f2 f3f4 f5f6 f7f8 f9fa fbfc fdfe ff    ...............

printf é mais portável que echo , mas para meu sistema e shell (bash), sua saída é exatamente a mesma.

printf %s "$var"
    
por 17.01.2018 / 11:46