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

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

我想显示$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 amksh -o utf8-modebash当发送到终端时,其输出通常不会明确。

  • 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'
    ''
    
  • 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'
    

    还有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 语言环境中工作。

答案2

尤其是对于IFS,你绝对想引用它,否则它就会变得一无所获。你已经这么做了,所以没问题。

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

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

最好改用printfprintf "%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"

答案4

我喜欢QBash 中的参数转换运算符:

echo "${IFS@Q}"

输出:

$' \t\n'

这个$'string'引用被称为ANSI-C 引用

Q运算符还可以用于通过 SSH 将参数传递给命令:

f='filename with space'
ssh "$host" "ls -l ${f@Q}"

参数扩展部分包含所有操作员的列表。该A运算符也很有用:

a=(array with 'various elements')
echo "${a[@]@A}"

输出:

declare -a a=([0]="array" [1]="with" [2]="various elements")

相关内容