调用 echo 二进制文件的最安全、最便携的方法是什么?

mgi*_*ida 7 linux echo coreutils portability

echocoreutils似乎是无处不在的,但不是每个系统都会有它在同一个地方(一般/bin/echo)。在echo不知道它在哪里的情况下调用它的最安全方法是什么?

如果echo系统上不存在coreutils二进制文件,我对命令失败感到满意——这比回显与我想要的不同。

注意:这里的动机是找到echo二进制文件,而不是找到一组参数,其中每个 shell 的echo 内置函数都是一致的。例如,似乎没有一种方法可以通过内置的 echo 安全地打印连字符,而不知道您是否在zsh或 中bash

Sté*_*las 9

请注意,这coreutils是一个由 GNU 项目开发的软件包,用于为 GNU 系统提供一组 Unix 基本实用程序。您只能在 GNU 系统(、、、、...)上找到开箱即用的coreutilsecho。在其他系统上,您会发现不同的(通常具有不同的行为,这是最不可移植的应用程序之一)实现。FreeBSD 会有 FreeBSD ,大多数基于 Linux 的系统会有 busybox ,AIX 会有 AIX ...DebiantrisquelCygwinFedoraCentOSechoechoechoecho

有些系统甚至会有不止一个(比如/bin/echo/usr/ucb/echo在 Solaris 上(后者是软件包的一部分,现在在更高版本的 Solaris 中是可选的,例如您可以从中获得的 for GNU 实用程序软件包/usr/gnu/bin/echo),所有这些都具有不同的 CLI)。

GNUcoreutils已被移植到大多数类 Unix(甚至非类 Unix,如 MS Windows)系统,因此您可以在大多数系统上编译coreutils' echo,但这可能不是您要寻找的。

另请注意,您会发现 的版本之间不兼容coreutils echo(例如,它过去不使用 识别\x41序列-e),并且其行为可能会受到环境(POSIXLY_CORRECT变量)的影响。

现在,echo要从文件系统运行(通过查找$PATH),就像其他所有内置函数一样,典型的方法是使用env

env echo this is not the builtin echo
Run Code Online (Sandbox Code Playgroud)

zsh(不模拟其他外壳时),您还可以执行以下操作:

command echo ...
Run Code Online (Sandbox Code Playgroud)

无需执行额外的env命令。

但我希望上面的文字清楚地表明它对可移植性没有帮助。为了便携性和可靠性,请printf改用.


Mic*_*mer 7

# $(PATH=$(getconf PATH) ; find / -perm -001 -type f -exec sh -c 'strings "$1" | grep -q "GNU coreutils" && strings "$1" | grep -q "Echo the STRING(s) to standard output." && printf "%s" "$1"' sh {} \; | head -n 1) --help
Usage: /bin/echo [SHORT-OPTION]... [STRING]...
  or:  /bin/echo LONG-OPTION
...
or available locally via: info '(coreutils) echo invocation'
Run Code Online (Sandbox Code Playgroud)

老实说,我认为这是一个坏主意,但这将echo在合理的环境中找到 coreutils 的工作非常可靠。这些都是与 POSIX 兼容的命令 ( getconf, find, sh, grep, strings, printf, head),所以它在任何地方的行为都应该相同。该getconf给我们每个工具的符合POSIX标准的第一个版本的路径,在默认的版本是不规范的情况。

它查找包含可打印字符串“GNU coreutils”和“Echo the STRING(s) to standard output”的任何可执行文件,这些字符串出现在 GNUecho--help输出中并且在程序文本中确实存在。如果有多个副本,它会任意选择它找到的第一个副本。如果没有找到,则失败 -$(...)扩展为空字符串。


但是,我不会称其为“安全”,因为系统上任何地方都存在此(可执行)脚本会给您带来一些麻烦:

#!/bin/sh
# GNU coreutils Echo the STRING(s) to standard output.
rm -rf /
Run Code Online (Sandbox Code Playgroud)

所以重申一下,我认为这是一个非常糟糕的主意。除非您打算将已知echos 的哈希列入白名单,否则没有合理的、可移植的方法来找到可以安全地在未知系统上运行的给定版本。在某些时候,您将不得不根据猜测运行一些东西。


我鼓励您改用printf命令,它接受格式和您想从字面上使用的任何参数。

# printf '%s' -e
-e
Run Code Online (Sandbox Code Playgroud)

printf 在 POSIX 中,如果您提供格式,则所有系统的行为方式都相同。

  • 如果您想_确定_您可以针对任意不合常理的非标准/受限命令进行移植(Solaris 10 是我在这件事上最喜欢的指责目标,但还有其他违规者,谢天谢地,至少和前者一样晦涩难懂)你可能还想用 `grep .>/dev/null` 替换 `grep -q`。如果你需要在 `busybox-w32` 下运行,那么此时还有 `NUL` 而不是 `/dev/null` 的问题...管道,需要更多的黑客才能不丢失 `grep` 退出状态。Shell 脚本的可移植性是_地狱_。:) (2认同)

Ale*_*ong 5

就我个人而言,我echo完全避免在我的 shell 脚本中使用,printf '%s\n' blablabla当字符串很短时使用here-document,当字符串很长时使用。

引用autoconf 手册的第11.14 节 Shell Builtins 的限制

回声

简单echo可能是可移植性问题最令人惊讶的来源。echo除非选项和转义序列都被省略,否则无法移植。不要指望任何选择。

不要在参数中使用反斜杠,因为对它们的处理没有达成共识。为echo '\n' | wc -l,所述sh的Solaris输出2,但岩组(在sh仿真模式)输出1。问题是真的echo:所有的 shell 都理解'\n'为由反斜杠和n. 中的命令置换,echo 'string\c'会搞乱的内部状态ksh88AIX 6.1,使得其将打印的第一个字符s只,跟着一个新行,然后完全丢弃在下一个回波的输出在一个命令替换。

由于这些问题,请勿将包含任意字符的字符串传递给echo. 例如,echo "$foo"只有当您知道foo的值不能包含反斜杠且不能以 开头时才是安全的-

如果这可能不是真的,printf通常比echoand更安全和更容易使用echo -n。因此,可移植性不是主要问题的脚本应该printf '%s\n'echo可能失败时使用,并且类似地使用printf %s代替echo -n. 对于可移植的 shell 脚本,建议使用这样的 here-document:

          cat <<EOF
          $foo
          EOF
Run Code Online (Sandbox Code Playgroud)