【问题标题】:Piping output of `echo` to netcat fails while piping output of `printf` passes`echo` 的管道输出到 netcat 失败,而 `printf` 的管道输出通过
【发布时间】:2021-11-17 08:43:02
【问题描述】:

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

【问题讨论】:

    标签: bash shell netcat


    【解决方案1】:

    啊!找到了。

    确保您不会在命令末尾意外发送换行符 (\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。如果您想要可移植的和预期的行为,以及向设备发送任何命令的能力,请不要在任何 shell 脚本中使用echo

    使用echo 根本 在尝试使用它向设备发送任何可能的字符串时实际上是一个坏主意!为什么?好吧,@Jeff Schaller 向我指出了以下资源in the comments,它有 非常有价值的信息:Unix & Linux: Why is printf better than echo?

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

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

    2. 它是不可移植的:它有各种不同的实现方式。有些支持-e-n,有些不支持。有些默认情况下具有-e 行为,而没有-e。等等。

    3. 它不能将-n 打印为命令,在某些 shell 上完全。见my comment here

      在 Ubuntu 18.04 和 20.04 上的 bash 上,echo "-n" 应该输出 -n。相反,它不输出任何内容,因为它接受与 -n 标志相同的内容。 echo -- "-n" 应该解决这个问题并输出-n,但事实并非如此。它改为输出-- -n。我现在非常明白你的观点。 printf 更好。

    4. 正如@Charles Duffy 指出的in the comments:甚至the POSIX specification for echo 也建议不要使用echo,原因有很多:

      不可能在所有 POSIX 系统中可移植地使用 echo...

      鼓励新应用程序使用printf 而不是echo

    仅供参考,以下是来自Unix & Linux: Why is printf better than echo? 的几个关键引述:

    关于echo

    ...确保$var 不包含反斜杠字符且不以- 开头

    关于printf

    但请记住第一个参数是格式,因此不应包含可变/不受控制的数据。

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

    在下面的 cmets 中查看我和@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" 是要替换为格式字符串的字符串文字根据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

    【讨论】:

    • 更多背景信息(注意echo 的各种实现和“所以这意味着你不能使用 echo 来显示不受控制的数据”):unix.stackexchange.com/questions/65803/…
    • echo -n 是不可靠的——即使是POSIX specification for echo 也会告诉你这一点。 (上面的Unix & Linux 链接更详细)。
    • 顺便说一句,使用printf '%s' "string" 比使用printf "string" 更可靠,因为前者强制将字符串视为文字而不是格式字符串。
    • ...否则,如果您的字符串有%s、反斜杠或其他格式字符串中的特殊内容,它将以与给出的格式不匹配的格式写入。
    • 哦,对了。我得到它。例如,如果我要发送的字符串是 %s,则不会发送 anything!:printf "%s"。输出为空,因为printf 将第一个参数解释为格式字符串,而%s 表示格式字符串中的特殊内容。但这确实将我的%s 作为文字发送:printf "%s" "%s"。输出为%s。我会用这些新信息更新我的答案。
    【解决方案2】:

    Ubuntu 18.04上的netcat好像变了,可以使用“netcat -N”完成事务。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-10-02
      • 2021-02-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-16
      相关资源
      最近更新 更多