为什么 '<<<' 在 gdb 中过滤空字节而 '<()' 没有?
Why is '<<<' filtering null bytes in gdb where '<()' does not?
我最近发现 gdb
中用于提供被调试程序的 stdin
的 <<<
技巧正在从流中过滤掉空字节。
这是一个小例子(任何人都应该能够在家里复制)来证明它过滤掉了空字节:
$> python -c 'print("A\x00" * 10)' | cat -A
A^@A^@A^@A^@A^@A^@A^@A^@A^@A^@$
$> gdb /bin/cat
... gdb license prelude ... snip...
(gdb) r -A <<< $(python -c 'print("A\x00" * 10)')
Starting program: /bin/cat -A <<< $(python -c 'print("A\x00" * 10)')
/bin/bash: warning: command substitution: ignored null byte in input
AAAAAAAAAA$
[Inferior 1 (process 3798) exited normally]
其中,使用 bash-specific <()
process substitution 留下空字节到达程序 stdin
gdb
:
(gdb) r -A < <(python -c 'print("A\x00" * 10)')
Starting program: /bin/cat -A < <(python -c 'print("A\x00" * 10)')
A^@A^@A^@A^@A^@A^@A^@A^@A^@A^@$
[Inferior 1 (process 3804) exited normally]
所以,我一直认为<<<
和<()
在做同样的事情,现在显然是错误的。我想知道这两种方法之间有什么区别,并对 bash
神秘错误消息进行解释,指出:
/bin/bash: warning: command substitution: ignored null byte in input
非常欢迎任何帮助!
So, I always though that <<<
and <()
were doing about the same thing which is now obviously wrong.
那些根本不做同样的事情。 <<<
运算符 将 重定向到 "here string" 关联进程的标准输入。通过 <()
进行的进程替换 扩展为 可以从中读取给定命令的标准输出的文件名(通常是 FIFO 或类似文件)。
你的意思似乎是 <<<
与命令替换(通过 $()
或反引号)的组合与通过 [=16 的普通标准输入重定向的组合做的事情大致相同=] 与进程替换。 这是正确的,但正如您所发现的,语义并不完全等同。
I would like to know what are the differences between the two methods and have an explanation about the bash mysterious error message
主要区别在于,从此处的字符串重定向需要首先将字符串生成为 shell 所持有的值(您正在通过命令替换执行此操作),而重定向进程替换涉及相关进程直接读取重定向输出。
最终,您收到的诊断表明您遇到的意外行为是由命令替换的行为引起的,而不是 <<<
的行为。虽然我没有找到它的明确记录,但我一点也不惊讶 Bash 在处理命令替换时从程序输出中删除空字符,因为我希望它的 shell 字符串的内部表示是作为 C 字符串。 C 字符串以 null 结尾,因此不能表示包含 null 字符的字符序列。
更新:
另请注意,正如@sorontar 在对其他答案的评论中观察到的那样,POSIX 表示如果命令替换中命令的输出包含空字节,则结果未指定。 Bash 因此可以自由剥离空字节——或者实际上在看到它们时或多或少地做任何它想做的事情——而不牺牲 POSIX 一致性。其他 shell 在这方面的选择可能与 Bash 不同。这是避免命令替换的一个很好的理由,因为空字节出现在命令输出中是可预见的可能性。
如前所述,这两种方法不是一回事(尽管在某些情况下可以出于相同的原因使用)
这个 <<<
是一个 here 字符串并且受变量扩展规则的约束。 Bash 不允许空字节出现在变量值中。
另一方面,进程替换 <()
被视为文件,文件中允许空字符。
所以您注意到的差异是由于这种 bash 行为造成的。
使用不同的 shell 可能此限制无效。
更多测试:
$ echo -en "A\x00A\x00A" |od -t x1c
0000000 41 00 41 00 41
A [=10=] A [=10=] A
0000005
$ a=$(echo -en "A\x00A\x00A");echo "$a" |od -t x1c
bash: warning: command substitution: ignored null byte in input
0000000 41 41 41 0a
A A A \n
0000004
$ cat <(echo -en "A\x00A\x00A") |od -t x1c #this is treated as file
0000000 41 00 41 00 41
A [=10=] A [=10=] A
0000005
$ cat <<<$(echo -en "A\x00A\x00A") |od -t x1c #this is considered a variable
bash: warning: command substitution: ignored null byte in input
0000000 41 41 41 0a
A A A \n
0000004
我最近发现 gdb
中用于提供被调试程序的 stdin
的 <<<
技巧正在从流中过滤掉空字节。
这是一个小例子(任何人都应该能够在家里复制)来证明它过滤掉了空字节:
$> python -c 'print("A\x00" * 10)' | cat -A
A^@A^@A^@A^@A^@A^@A^@A^@A^@A^@$
$> gdb /bin/cat
... gdb license prelude ... snip...
(gdb) r -A <<< $(python -c 'print("A\x00" * 10)')
Starting program: /bin/cat -A <<< $(python -c 'print("A\x00" * 10)')
/bin/bash: warning: command substitution: ignored null byte in input
AAAAAAAAAA$
[Inferior 1 (process 3798) exited normally]
其中,使用 bash-specific <()
process substitution 留下空字节到达程序 stdin
gdb
:
(gdb) r -A < <(python -c 'print("A\x00" * 10)')
Starting program: /bin/cat -A < <(python -c 'print("A\x00" * 10)')
A^@A^@A^@A^@A^@A^@A^@A^@A^@A^@$
[Inferior 1 (process 3804) exited normally]
所以,我一直认为<<<
和<()
在做同样的事情,现在显然是错误的。我想知道这两种方法之间有什么区别,并对 bash
神秘错误消息进行解释,指出:
/bin/bash: warning: command substitution: ignored null byte in input
非常欢迎任何帮助!
So, I always though that
<<<
and<()
were doing about the same thing which is now obviously wrong.
那些根本不做同样的事情。 <<<
运算符 将 重定向到 "here string" 关联进程的标准输入。通过 <()
进行的进程替换 扩展为 可以从中读取给定命令的标准输出的文件名(通常是 FIFO 或类似文件)。
你的意思似乎是 <<<
与命令替换(通过 $()
或反引号)的组合与通过 [=16 的普通标准输入重定向的组合做的事情大致相同=] 与进程替换。 这是正确的,但正如您所发现的,语义并不完全等同。
I would like to know what are the differences between the two methods and have an explanation about the bash mysterious error message
主要区别在于,从此处的字符串重定向需要首先将字符串生成为 shell 所持有的值(您正在通过命令替换执行此操作),而重定向进程替换涉及相关进程直接读取重定向输出。
最终,您收到的诊断表明您遇到的意外行为是由命令替换的行为引起的,而不是 <<<
的行为。虽然我没有找到它的明确记录,但我一点也不惊讶 Bash 在处理命令替换时从程序输出中删除空字符,因为我希望它的 shell 字符串的内部表示是作为 C 字符串。 C 字符串以 null 结尾,因此不能表示包含 null 字符的字符序列。
更新:
另请注意,正如@sorontar 在对其他答案的评论中观察到的那样,POSIX 表示如果命令替换中命令的输出包含空字节,则结果未指定。 Bash 因此可以自由剥离空字节——或者实际上在看到它们时或多或少地做任何它想做的事情——而不牺牲 POSIX 一致性。其他 shell 在这方面的选择可能与 Bash 不同。这是避免命令替换的一个很好的理由,因为空字节出现在命令输出中是可预见的可能性。
如前所述,这两种方法不是一回事(尽管在某些情况下可以出于相同的原因使用)
这个 <<<
是一个 here 字符串并且受变量扩展规则的约束。 Bash 不允许空字节出现在变量值中。
另一方面,进程替换 <()
被视为文件,文件中允许空字符。
所以您注意到的差异是由于这种 bash 行为造成的。 使用不同的 shell 可能此限制无效。
更多测试:
$ echo -en "A\x00A\x00A" |od -t x1c
0000000 41 00 41 00 41
A [=10=] A [=10=] A
0000005
$ a=$(echo -en "A\x00A\x00A");echo "$a" |od -t x1c
bash: warning: command substitution: ignored null byte in input
0000000 41 41 41 0a
A A A \n
0000004
$ cat <(echo -en "A\x00A\x00A") |od -t x1c #this is treated as file
0000000 41 00 41 00 41
A [=10=] A [=10=] A
0000005
$ cat <<<$(echo -en "A\x00A\x00A") |od -t x1c #this is considered a variable
bash: warning: command substitution: ignored null byte in input
0000000 41 41 41 0a
A A A \n
0000004