我听说printfecho好。根据我的经验,我只能回忆起一个必须使用printf的实例,因为echo无法将某些文本输入RHEL 5.8上的某些程序,而printf可以。但是显然还有其他差异,我想询问一下它们是什么以及何时有特定情况下何时使用一个与另一个。

#1 楼

基本上,这是一个可移植性(和可靠性)问题。

最初,echo不接受任何选项,也没有扩展任何东西。它所做的只是输出由空格字符分隔并由换行符终止的参数。

现在,有人认为,如果我们可以执行echo "\n\t"之类的操作来输出换行符或制表符,那会很好。或选择不输出结尾的换行符。

他们随后更加努力地思考,但没有在外壳中添加该功能(例如perl,其中双引号内的\t实际上是制表符),将其添加到echo中。

David Korn意识到了这个错误,并引入了一种新的shell引号形式:$'...',后来被bashzsh复制,但那时已经太迟了。

现在,当标准UNIX echo接收到包含两个字符\t的参数时,将输出一个制表符,而不是输出它们。并且一旦在参数中看到\c,它就会停止输出(因此也不输出结尾的换行符。)

其他shell / Unix供应商/版本选择了不同的方法:他们添加了-e选项可扩展转义序列,而-n选项可不输出尾随换行符。有些使用-E来禁用转义序列,有些使用-n但不使用-e,一个echo实现所支持的转义序列列表不一定与另一个实现所支持的列表相同。

Sven Mascheck的页面不错

在那些支持选项的echo实现中,通常不支持--来标记选项的结束(某些非伯恩类shell内置的echo确实可以,而zsh对此则支持-),因此,例如,很难在许多shell中使用"-n"输出echo

在某些外壳上,例如bashksh93²或yash$ECHO_STYLE变量),其行为甚至取决于外壳的编译方式或环境(如果echo在环境中以及版本4,即$POSIXLY_CORRECT,则GNU zsh的行为也会改变。带有bsd_echo选项,某些基于pdksh带有posix选项,或者是否称为sh。因此,即使来自相同版本的bash,也不能保证两个echo的行为相同。在这方面,bash回声不是POSIX,例如-n未按POSIX要求输出bash。 UNIX规范更加严格,它禁止使用echo -e,并且需要扩展某些转义序列(包括-e<newline>)来停止输出。

由于许多实现不兼容,因此这些规范在这里并没有真正起作用。 。甚至连macOS5之类的某些认证系统也不兼容。使用BIG5字符集在语言环境中对反斜杠字符进行编码,例如-n),则行为未指定。不包含反斜杠字符,也不以\c开头。 POSIX规范实际上确实告诉我们在这种情况下使用^-([eEn]*|-help|-version)$

因此,这意味着您不能使用α显示不受控制的数据。换句话说,如果您正在编写脚本并且它正在接受外部输入(来自用户的自变量,或来自文件系统的文件名...),则不能使用echo "$var"来显示它。
这可以:

echo >&2 Invalid file.


这不是:

echo >&2 "Invalid file: $file"


(尽管会当未在编译时或通过环境以某种方式启用$var选项时,可以与某些(不兼容UNIX的)-实现正常工作,例如printf

echo不好在大多数实现中(echoecho(例外是bash的变量不能容纳任意字节序列,因此不能容纳任意文件名)和xpg_echofile=$(echo "$var" | tr ' ' _) 6除外)。另一方面至少在yash的基本用法上更可靠。

printf '%s\n' "$var"


将输出ECHO_STYLE=raw的内容,后跟换行符s可能包含什么字符。

printf '%s' "$var"


将输出不带尾随换行符的字符。实现。 POSIX指定了功能的核心,但是随后有很多扩展。例如,有些支持yash引用参数,但是它的完成方式因外壳而异,有些支持Unicode字符zsh。对于echo -E - "$var",其行为在多字节语言环境中有所不同,对于printf至少存在三种不同的结果

但是最后,如果您坚持使用echo的POSIX功能集并且不要尝试做任何事情

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

可以使用$var来实现更可靠的printf,例如:

echo() ( # subshell for local scope for $IFS
  IFS=" " # needed for "$*"
  printf '%s\n' "$*"
)

echo_n() (
  IFS=" "
  printf %s "$*"
)

echo_e() (
  IFS=" "
  printf '%b\n' "$*"
)


可以避免使用%q来避免子外壳(这意味着在大多数外壳实现中产生了额外的过程),外壳,或通过以下方式编写:

echo() {
  if [ "$#" -gt 0 ]; then
     printf %s ""
     shift
     if [ "$#" -gt 0 ]; then
       printf ' %s' "$@"
     fi
  fi
  printf '\n'
}



注释

1。如何更改\uxxxxprintf '%10s\n' "$var"行为。 /> printf %b '3' printf选项以及echo是否处于posix模式。如果将printf称为local IFS,或者如果bash在环境中或使用echo选项,则可以启用bash模式:

大多数系统上的默认行为:

$ bash -c 'echo -n "01"'
01% # the % here denotes the absence of newline character


echo根据UNIX的要求扩展序列:

$ BASHOPTS=xpg_echo bash -c 'echo "01"'
A


它仍然尊重enable -n echoecho(和xpg_echo):

$ BASHOPTS=xpg_echo bash -c 'echo -n "01"'
A%


bash和POSIX模式下:

$ env BASHOPTS=xpg_echo POSIXLY_CORRECT=1 bash -c 'echo -n "01"'
-n A
$ env BASHOPTS=xpg_echo sh -c 'echo -n "01"' # (where sh is a symlink to bash)
-n A
$ env BASHOPTS=xpg_echo SHELLOPTS=posix bash -c 'echo -n "01"'
-n A


这次,bash既符合POSIX又符合UNIX。请注意,在POSIX模式下,posix仍不符合POSIX,因为它不会在以下位置输出bash
使用sh脚本的POSIXLY_CORRECTposix选项。这通常是最新版本的OS / X用来构建其xpg_echo的方式。尽管他们认为正确的Unix / Linux实现/发行版通常不会对-n做到这一点。实际上,事实并非如此,Oracle随Solaris 11提供的-e(在可选软件包中)似乎是由-E构建的(Solaris 10中不是这种情况)。

2。如何更改xpg_echobash行为。

bash中,-e是否扩展转义序列并识别选项取决于--enable-xpg-echo-default和/或--enable-strict-posix-default环境变量的内容。

如果configure包含在/bin/sh/bin/bash之前包含/bin/bash--enable-xpg-echo-default的组件。组件,然后它以SysV / UNIX方式运行(扩展序列,不接受选项)。如果它首先找到ksh93echo,或者ksh93 7包含echo,那么它将表现为BSD3方式($PATH启用扩展,识别$_AST_FEATURES)。在最新版本的ksh93中输出$PATH):

$ env SHELLOPTS=posix bash -c 'echo -e'

$


3.。 BSD for echo -e?

此处引用BSD来处理/5bin选项有点误导。大多数这些不同且不兼容的/xpg行为都是在AT&T上引入的:




/bin/usr/bin/ucb在Programmer's Work Bench UNIX(基于Unix V6)中,其余的行为(Unix系统IIIRef中的/bsd$_AST_FEATURES ...)。

Unix V7中的UNIVERSE = ucb(由Dennis RitchieRef设计) >
-e本身可能最初来自-n(版本1.13.5中的CWRU / CWRU.chlog提到Brian Fox在1992-10-18上添加了它,GNU builtin getconf; getconf UNIVERSE在sh-utils-1.8中发布了10天后不久就复制了它后来)

虽然BSD的-eecho内置于自90年代初开始使用Almquist shell的那天起就已经支持\n,但是迄今为止,独立的\cooo实用程序不支持它。那里(FreeBSD \b仍然不支持\r,尽管它像Unix V7一样支持-n(还有-e,但仅在t他最后一个参数的结尾))。

在2006年发布的ksh93r版本的BSD Universe中,对-E的处理已添加到bashecho中,并且可以在编译时禁用。

4。 GNU回声在8.31中的行为更改自coreutils 8.31(和此提交)以来,默认情况下,当POSIXLY_CORRECT在环境中时,GNU echo现在扩展转义序列,以匹配sh内置-e的行为(请参阅错误报告)。

5。 macOS echo


大多数版本的macOS已获得OpenGroup的UNIX认证。

它们内置的echo兼容-e,因为其内置的-n(非常旧的版本)默认情况下启用了\c,但未启用其独立的-e实用程序。 ksh93不输出而不是echoecho输出bash -o posix -O xpg_echo而不是echo

echo是FreeBSD中的一个,如果第一个参数是sh或(自1995年起)最后一个参数以结束,它将抑制换行输出echo,但不支持UNIX所需的任何其他反斜杠序列,甚至不支持bash

6。可以逐字输出任意数据的xpg_echo实现

严格来说,您还可以指望上面的FreeBSD / macOS echo(不是其外壳的env echo -n内置),其中-n<newline>env echo '\n'\n<newline><newline><newline>/bin/echo)可以可以这样写:

$ ksh93 -c 'echo -n' # default -> BSD (on Debian)
$ PATH=/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /xpg before /bin or /usr/bin -> XPG
-n
$ PATH=/5binary:$PATH ksh93 -c 'echo -n' # /5bin before /bin or /usr/bin -> XPG
-n
$ PATH=/5binary:$PATH _AST_FEATURES='UNIVERSE = ucb' ksh93 -c 'echo -n' # -> BSD
$ PATH=/ucb:/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /ucb first -> BSD
$ PATH=/bin:/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /bin before /xpg -> default -> BSD


-n\c\)可以这样写

/bin/echo "$var
\c"


支持echo/bin/echo(或可以配置为)也可以:

/bin/echo "$var\c"


等效于echo

7.。 zsh和AST echo -E - "$var"


yash并非旨在直接进行操作,而是用于在命令执行过程中传播AST配置设置。该配置应通过(未记录)ECHO_STYLE=raw echo "$var" API完成。在printf '%s\n' "$var"内部,内置的zsh(已启用echo -nE - "$var"或通过调用printf %s "$var"启用)是-E的接口。例如,您可以执行-nprintf '%s\n' "$var"设置更改为_AST_FEATURES(使UNIVERSE表现为SysV方式)。之后,您会发现_AST_FEATURES环境变量包含astgetconf()

评论


许多早期的unix开发都是孤立发生的,并且没有应用良好的软件工程原理,例如“当您更改界面时,更改名称”。

–汉克·兰维德
2014年3月14日在7:57

请注意,作为引用语法的一部分,让echo扩展\ x序列(而不是shell)的一个(也是唯一的)优势是您可以随后输出NUL字节(另一个可以说是Unix的错误设计是以空分隔的字符串,其中一半的系统调用(例如execve())不能采用任意字节序列

–StéphaneChazelas
2015年8月4日在12:15



我惊讶于您对echo和printf有多少了解!

– jrahhali
6月26日1:38

#2 楼

您可能要使用printf作为其格式化选项。在打印变量或(简单)行的值时,echo很有用,但仅此而已。 printf基本上可以完成C版本的功能。

示例用法和功能: br />

Echo

echo "*** Backup shell script ***"
echo
echo "Runtime: $(date) @ $(hostname)"
echo


来源: cyberciti.biz/guide/Echo_Command
man echo
man printf


评论


如果变量的值包含元字符,则使用echo打印变量可能会失败。

–基思·汤普森(Keith Thompson)
13年4月29日在15:22

我将包括这个printf例子:printf“ ***备份shell脚本*** \ n \ n运行时:%s @%s \ n \ n”“ $ {date)”“ $ {hostname)”#即与上面的回声线相同的显示

–奥利维尔·杜拉克(Olivier Dulac)
9月21日12:37



#3 楼

如果您想这样称呼它,那么“优势”就是您不必像echo那样告诉它来解释某些转义序列,例如\n。它知道要解释它们,不需要-e来解释。 \n选项)



printf "some\nmulti-lined\ntext\n"


请注意echo中的最后一个-n。一天结束时,您所用的物品取决于口味和要求:\nprintf

评论


对于/ usr / bin / echo和内置的bash为true。 dash,ksh和zsh内置的echo不需要-e开关即可扩展反斜杠转义的字符。

–manatwork
13年2月23日在10:35

为什么要害怕报价?您的措辞暗示它不一定是真正的优势。

–基思·汤普森(Keith Thompson)
13年4月29日在15:26

@KeithThompson:实际上,它们只意味着不是每个人都可以认为这是一种优势。

– 0xC0000022L
2013年4月29日15:33



你能详细谈谈?为什么没有优势呢?短语“如果您想称其为”则强烈暗示您认为不是。

–基思·汤普森(Keith Thompson)
13年4月29日在15:36

或者,您可以只执行printf'%s \ n''foo \ bar。

–nyuszika7h
2014年9月9日17:36

#4 楼

printf的一个缺点是性能,因为内置外壳echo快得多。这在Cygwin中尤为重要,因为新命令的每个实例都会导致大量Windows开销。当我将重回声程序从使用/bin/echo更改为外壳的回声时,性能几乎翻了一番。在可移植性和性能之间进行权衡。始终使用printf不是灌篮,

评论


如今,printf内置在大多数shell中(bash,dash,dash,ksh,zsh,yash,一些pdksh派生词...因此也包括通常在cygwin上发现的shell)。唯一值得注意的例外是某些pdksh派生类。

–StéphaneChazelas
2014年11月7日13:52



但是许多这些printf的实现都被破坏了。当您喜欢使用printf输出nul字节时,这是必不可少的,但是某些实现即使不应该以格式字符串解释“ \ c”。

–schily
18年7月3日在14:46

@schily,如果在格式字符串中使用\ c,则POSIX无法指定printf的行为,因此printf实现可以在这方面做任何想要的事情。例如,有些人将其与PWB回显相同(导致printf退出),ksh将其用于\ cA控制字符(用于printf格式参数和$'...',但不用于echo或print!) 。不知道与打印NUL字节有什么关系,或者不确定您是指ksh的printf'\ c @'吗?

–StéphaneChazelas
18/12/29在16:03