从 stdin 捕获特殊字符到 shell 变量
Capturing special characters from stdin to a shell variable
我有一个程序可以打印包含空字节 [=19=]
和特殊字符(如 \x1f
和换行符)的内容。例如:
someprogram
#!/bin/bash
printf "ALICE[=11=]BOB\x1fCHARLIE\n"
给定这样一个程序,我想以这样一种方式读取它的输出,即所有这些特殊字符都被捕获在 shell 变量 output
中。所以,如果我 运行:
echo $output
因为我没有给出 -e
,所以我希望输出为:
ALICE[=13=]BOB\x1fCHARLIE\n
如何实现?
我的第一次尝试是:
output=$(someprogram)
但是我得到了没有特殊字符的回显输出:
./myscript.sh: line 2: warning: command substitution: ignored null byte in input
ALICEBOBCHARLIE
我也试过使用read
如下:
output=""
while read -r
do
output="$output$REPLY"
done < <(someprogram)
然后我摆脱了警告,但输出仍然缺少所有特殊字符:
ALICEBOBCHARLIE
那么我如何捕获 someprogram
的输出,使我的结果字符串中包含所有特殊字符?
编辑:请注意,在 bash:
中可以有这样的字符串
$ x="ALICE[=18=]BOB\x1fCHARLIE\n"
$ echo $x
ALICE[=18=]BOB\x1fCHARLIE\n
所以这不应该是问题所在。
EDIT2:既然我得到了一个可接受的答案并且我对事情的理解更好了一点,我将稍微重新表述这个问题。所以,我只需要能够将 someprogram
的输出存储在某些 shell 变量中,这样我就可以将它打印到标准输出而无需任何特殊字符的任何更改,就好像 someprogram
直接通过管道传输到标准输出。
您不能在 bash 变量中存储零字节。不可能。
通常的解决方案是将字节流转换为十六进制。然后每次你想用它做点什么的时候把它转换回来。
$ x=$(printf "ALICE[=10=]BOB\x1fCHARLIE\n" | xxd -p)
$ echo "$x"
414c49434500424f421f434841524c49450a
$ <<<"$x" xxd -p -r | hexdump -C
00000000 41 4c 49 43 45 00 42 4f 42 1f 43 48 41 52 4c 49 |ALICE.BOB.CHARLI|
00000010 45 0a |E.|
00000012
您也可以为此目的编写自己的序列化和反序列化函数。
我的另一个想法是,例如通过使用零字节作为分隔符(因为任何其他字节都是有效的)将数据读入数组。然而,这会在区分尾随零字节时出现问题:
$ readarray -d '' arr < <(printf "ALICE[=11=]BOB\x1fCHARLIE\n")
$ printf "%s[=11=]" "${arr[@]}" | hexdump -C
00000000 41 4c 49 43 45 00 42 4f 42 1f 43 48 41 52 4c 49 |ALICE.BOB.CHARLI|
00000010 45 0a 00 |E..|
# ^^ additional zero byte if input doesn't contain a trailing zero byte
00000013
我有一个程序可以打印包含空字节 [=19=]
和特殊字符(如 \x1f
和换行符)的内容。例如:
someprogram
#!/bin/bash
printf "ALICE[=11=]BOB\x1fCHARLIE\n"
给定这样一个程序,我想以这样一种方式读取它的输出,即所有这些特殊字符都被捕获在 shell 变量 output
中。所以,如果我 运行:
echo $output
因为我没有给出 -e
,所以我希望输出为:
ALICE[=13=]BOB\x1fCHARLIE\n
如何实现?
我的第一次尝试是:
output=$(someprogram)
但是我得到了没有特殊字符的回显输出:
./myscript.sh: line 2: warning: command substitution: ignored null byte in input
ALICEBOBCHARLIE
我也试过使用read
如下:
output=""
while read -r
do
output="$output$REPLY"
done < <(someprogram)
然后我摆脱了警告,但输出仍然缺少所有特殊字符:
ALICEBOBCHARLIE
那么我如何捕获 someprogram
的输出,使我的结果字符串中包含所有特殊字符?
编辑:请注意,在 bash:
中可以有这样的字符串$ x="ALICE[=18=]BOB\x1fCHARLIE\n"
$ echo $x
ALICE[=18=]BOB\x1fCHARLIE\n
所以这不应该是问题所在。
EDIT2:既然我得到了一个可接受的答案并且我对事情的理解更好了一点,我将稍微重新表述这个问题。所以,我只需要能够将 someprogram
的输出存储在某些 shell 变量中,这样我就可以将它打印到标准输出而无需任何特殊字符的任何更改,就好像 someprogram
直接通过管道传输到标准输出。
您不能在 bash 变量中存储零字节。不可能。
通常的解决方案是将字节流转换为十六进制。然后每次你想用它做点什么的时候把它转换回来。
$ x=$(printf "ALICE[=10=]BOB\x1fCHARLIE\n" | xxd -p)
$ echo "$x"
414c49434500424f421f434841524c49450a
$ <<<"$x" xxd -p -r | hexdump -C
00000000 41 4c 49 43 45 00 42 4f 42 1f 43 48 41 52 4c 49 |ALICE.BOB.CHARLI|
00000010 45 0a |E.|
00000012
您也可以为此目的编写自己的序列化和反序列化函数。
我的另一个想法是,例如通过使用零字节作为分隔符(因为任何其他字节都是有效的)将数据读入数组。然而,这会在区分尾随零字节时出现问题:
$ readarray -d '' arr < <(printf "ALICE[=11=]BOB\x1fCHARLIE\n")
$ printf "%s[=11=]" "${arr[@]}" | hexdump -C
00000000 41 4c 49 43 45 00 42 4f 42 1f 43 48 41 52 4c 49 |ALICE.BOB.CHARLI|
00000010 45 0a 00 |E..|
# ^^ additional zero byte if input doesn't contain a trailing zero byte
00000013