![如何打印包含不可打印字符的变量?](https://linux22.com/image/125777/%E5%A6%82%E4%BD%95%E6%89%93%E5%8D%B0%E5%8C%85%E5%90%AB%E4%B8%8D%E5%8F%AF%E6%89%93%E5%8D%B0%E5%AD%97%E7%AC%A6%E7%9A%84%E5%8F%98%E9%87%8F%EF%BC%9F.png)
我想显示$IFS
变量的值,该值可能包含不可打印的字符(例如:换行符)。
我使用以下命令来执行此操作:
echo -n "$IFS" | hexdump -C
这对我来说效果很好。
但是使用这个命令有什么问题吗?例如,echo
在将一些不可打印的字符打印到其之前,是否将它们替换为一些其他字符stdout
,或者类似的其他问题?
答案1
给出字符串视觉表示的一些不同方法:
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$
(额外的内容\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]'
但对于后一种(使用 Unicode 组合急性重音符号和零宽度空格字符), zsh/mksh 没有帮助(即使使用 with
LC_ALL=C typeset -p a
)mksh -o utf8-mode
。bash
当发送到终端时,其输出通常不会明确。printf %q
使用GNUprintf
和printf
的内置功能ksh93
以及zsh
:bash
$ 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' ''
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'
还有
q
andq+
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 语言环境中工作。
答案2
尤其是对于IFS
,你绝对想引用它,否则它就会变得一无所获。你已经这么做了,所以没问题。
至于echo
,这取决于外壳。某些版本echo
默认处理反斜杠转义,有些则不处理。 Bash 没有,zsh 可以:
$ bash -c 'echo "foo\nbar"'
foo\nbar
$ zsh -c 'echo "foo\nbar"'
foo
bar
最好改用printf
:printf "%s" "$IFS" | hexdump -C
。
也可以看看:为什么 printf 比 echo 更好?
printf "%q" "$IFS"
也适用于 Bash 和 zsh。
这应该会让你保持良好状态,除了 Bash\0
根本无法处理 NUL 字节 ( ),而 zsh 可以。重击:
$ var=$'foo\0bar'
$ printf "%q\n" "$var"
foo
兹什:
$ var=$'foo\0bar'
$ printf "%q\n" "$var"
foo$'\0'bar
答案3
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 ...............
printf
那么更便携echo
,但对于我的系统和 shell (bash),它们的输出是完全相同的。
printf %s "$var"