将 `echo` 的输出管道输出到 netcat 失败,而 `printf` 的管道输出通过

Piping output of `echo` to netcat fails while piping output of `printf` passes

我在 Linux Ubuntu 18.04 和 20.04 上使用 netcat (nc) 通过 TCP 以太网向电源设备发送命令。如果我使用 echo 设备无法正确接收命令,但如果我使用 printf 它工作正常。为什么?

# Example command to Ethernet-connected digital power supply over TCP

# fails
echo 'measure:voltage? ch1' | timeout 0.2 nc 192.168.0.1 9999

# works
printf 'measure:voltage? ch1' | timeout 0.2 nc 192.168.0.1 9999

参考文献:

  1. timeout命令:https://unix.stackexchange.com/questions/492766/usage-of-nc-with-timeouts-in-ms/492796#492796
    1. Update/Note(在我写完答案后写的,所以下面的printf形式是正确的):如果没有timeout命令,我们将不得不使用-w 选项与 netcat,但它只接受以秒为单位的整数等待期超时,如下所示(注意 -w 1 将“等待期”或超时设置为 1 整秒):
      printf '%s' "my command to send" | nc -w1 192.168.0.1 9999 
      

呃!找到了。

确保您没有在命令末尾不小心发送换行符 (\n)

看起来 echo 向字符串添加了一个尾随换行符,而 printf 没有,并且这个尾随换行符干扰了设备解析命令的能力。如果我强行将它(换行符,\n)添加到 printf cmd 的末尾,那么它也会失败——这意味着设备不会按预期响应命令:

# fails:
printf 'measure:voltage? ch1\n' | timeout 0.2 nc 192.168.0.1 9999

...看起来您可以使用 -n:

来抑制 echo 的尾随换行符
# works!
echo -n 'measure:voltage? ch1' | timeout 0.2 nc 192.168.0.1 9999

# also works, of course, as stated in the question
printf 'measure:voltage? ch1' | timeout 0.2 nc 192.168.0.1 9999

来自 man echo:

-n     do not output the trailing newline

要点:使用 printfecho -n 以在打印末尾不包含尾随换行符。

但是,这还没有结束!这里还有两点:

1。如果您想要可移植和预期的行为,以及发送 any[= 的能力,请不要在任何 shell 脚本中使用 echo 152=] 命令到设备!

完全使用 echo 实际上是一个坏主意,因为它试图将任何可能的字符串发送到设备!为什么?嗯,@Jeff Schaller pointed out the following resource to me , and it has tons of really valuable information: Unix & Linux: Why is printf better than echo?.

不使用 echo 的几个主要原因包括:

  1. 它的实现、参数和行为都有点老套。

  2. 它不可移植:它有各种不同的实现,这些实现在不同系统之间差异很大。有些支持 -e-n,有些则不支持。有些默认情况下具有 -e 行为,但没有 -e。等等

  3. 它无法将 -n 作为命令打印, 在某些 shell 上根本。参见 my comment here

    On bash on Ubuntu 18.04 and 20.04, echo "-n" should output -n. Instead, it outputs nothing since it accepts that as the same thing as the -n flag. echo -- "-n" should solve that and output -n, but it doesn't. It outputs -- -n instead. I see your point very well now. printf is better.

  4. 因为 @Charles Duffy points out : even the POSIX specification for echo 建议不要使用 echo,因为其中有许多不一致和原因:

    It is not possible to use echo portably across all POSIX systems...

    New applications are encouraged to use printf instead of echo.

仅供参考,这里有一些来自 Unix & Linux: Why is printf better than echo? 的关键引述:

关于echo

...make sure that $var doesn't contain backslash characters and doesn't start with -

关于printf

But remember the first argument is the format, so shouldn't contain variable/uncontrolled data.

2。正确使用 printf 作为 printf '%s' "my command string",而不是 printf "my command string"

在下面的评论中查看我和@Charles Duffy 之间的更多讨论。您应该像这样使用 printf

# CORRECT USAGE: >>> FINAL ANSWER; DO THIS! <<<
printf '%s' 'measure:voltage? ch1' | timeout 0.2 nc 192.168.0.1 9999  

# NOT this:
printf 'measure:voltage? ch1' | timeout 0.2 nc 192.168.0.1 9999

这是因为传递给 printf 的第一个参数被解析为 格式字符串 。例如,如果您尝试发送到设备的命令字符串是 %s,这不会发送任何内容!:

printf "%s"

输出为空,因为 printf 将第一个参数解释为格式字符串,而 %s 表示格式字符串中的特殊内容。但这确实将 %s 作为文字发送:

printf "%s" "%s"

输出为

%s

printf 参数中的第一个 "%s" 格式字符串 ,第二个 "s"string literal 根据 printf 规范替换第一个 %s 所在的格式字符串。

这意味着 printf 的正确用法将能够向设备发送任何命令,而不正确的用法将删除任何格式字符,例如 %s , %02X, %u, 等等 -- printf-style format string special chars or sequences.

中的任何一个

另请参阅:

  1. Unix & Linux: Why is printf better than echo?
  2. the POSIX specification for echo

似乎Ubuntu 18.04 上的netcat 发生了变化,您可以使用“netcat -N”来完成交易。