如何打印包含不可打印字符的变量?

use*_*971 10 shell character-encoding special-characters variable

我想显示$IFS变量的值,它可能包含不可打印的字符(例如:换行符)。

我使用以下命令来做到这一点:

echo -n "$IFS" | hexdump -C
Run Code Online (Sandbox Code Playgroud)

在我的情况下效果很好。

但是使用这个命令有什么问题吗?例如,在echo将某些无法打印的字符打印到它的stdout.

ilk*_*chu 7

尤其是对于IFS,您绝对想引用它,否则它就会变成空。你已经这样做了,所以没问题。

至于echo,这取决于外壳。echo默认情况下,某些版本的处理反斜杠转义符,有些则不处理。Bash 没有,zsh 有:

$ bash -c 'echo "foo\nbar"'
foo\nbar
$ zsh -c 'echo "foo\nbar"'
foo
bar
Run Code Online (Sandbox Code Playgroud)

最好printf改用:printf "%s" "$IFS" | hexdump -C

另请参阅:为什么 printf 比 echo 好?

printf "%q" "$IFS" 也适用于 Bash 和 zsh。

这应该会让你保持良好状态,除了 Bash 根本无法处理 NUL 字节 ( \0),而 zsh 可以。重击:

$ var=$'foo\0bar'
$ printf "%q\n" "$var"
foo
Run Code Online (Sandbox Code Playgroud)

zsh:

$ var=$'foo\0bar'
$ printf "%q\n" "$var"
foo$'\0'bar
Run Code Online (Sandbox Code Playgroud)


Sté*_*las 6

给出字符串的视觉表示的一些不同方法:

POSIX

$ printf %s "$IFS" | od -vtc -to1
0000000      \t  \n  \0
        040 011 012 000
0000004
$ printf '%s\n' "$IFS" | LC_ALL=C sed -n l
 \t$
\000$
Run Code Online (Sandbox Code Playgroud)

(额外的内容\n是必要的,否则sed如果最后一行没有以换行符结尾,则行为是未指定的)。POSIXsh不会$IFS像我的 zsh 那样包含 NUL 。sed如果输入包含 NUL ,则的行为未指定。

Shell 内置函数

  • typeset -p (ksh, zsh, bash, yash) 可能会为某些字符串提供明确的输出。

    $ 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]'
    
    Run Code Online (Sandbox Code Playgroud)

    但是对于后一个(使用 Unicode 结合重音符号和零宽度空格字符), zsh/mksh 没有帮助(即使LC_ALL=C typeset -p a使用mksh -o utf8-mode)。bash的输出在发送到终端时通常不是明确的。

  • printf %q使用 GNUprintfprintf内置的ksh93,zshbash

    $ 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'$'\0'
    éé?
    ''
    $ 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'$'\0'
    $'\303'$'\251'e$'\314'$'\201'$'\342'$'\200'$'\213'
    ''
    $ a=$'\u00e9e\u301\u200b' bash -c 'LC_ALL=C printf "%q\n" "$IFS" "$a" ""'
    $' \t\n'
    $'\303\251e\314\201\342\200\213'
    ''
    
    Run Code Online (Sandbox Code Playgroud)
  • q, qq, qqq, 中的qqqq参数扩展标志zsh

    对于各种类型的引用,qqqq其中之一是$'...'

    $ a=$'\u00e9e\u301\u200b' zsh -c 'print -r -- ${(qqqq)a}'
    $'éé?'
    $ a=$'\u00e9e\u301\u200b' zsh -c '(){local LC_ALL=C; print -r -- ${(qqqq)a}}'
    $'\303\251e\314\201\342\200\213'
    
    Run Code Online (Sandbox Code Playgroud)

    还有qand q+that 只对需要它的东西使用引用(尽管对于那些 unicode 仍然有警告)。

各种非标准命令:

  • hex-dumper: hexdump, hd, xxd... 你想给他们输出printf %s "$var"(或print -rn -- "$var"用 ksh/zsh,或echo -nE - "$var"zsh...)。

  • cat -vte 或者 cat -A

  • uconv -x hex对于字符的 unicode 代码点(与编码字节的十六进制值相反),仅适用于 UTF-8(iconv -t utf-8尽管只要它是语言环境编码中的有效文本,就可以对输入进行预处理)

  • uconv -x name 对于角色名称

  • recode ..dump. 十六进制和名称,但知道较少的 Unicode 字符(未使用较新版本的 Unicode 进行更新)。虽然在非 UTF-8 语言环境中工作。


cho*_*oba 4

bash 中唯一不起作用的字符是 null。

$ 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    ...............
Run Code Online (Sandbox Code Playgroud)

printf那么更便携echo,但对于我的系统和 shell (bash),它们的输出是完全相同的。

printf %s "$var"
Run Code Online (Sandbox Code Playgroud)

  • 我另外会选择 `printf '%s' "$var"` 而不是 `echo -n` (为了可移植性)。 (2认同)
  • 尝试使用 `var=-e` (2认同)