sh -c 'printf "%d " 024'
bash -c 'printf "%d " 024'
zsh -c 'printf "%d" 024'
以上输出20 20 24
。为什么 zsh 不尊重八进制表示法?有办法改变这个吗?
答案1
zsh
不是 POSIX shell,它是在 POSIX 规范sh
发布之前编写的,采用了 ksh、csh、tcsh、rc 的功能和语法,并添加了许多自己的功能和语法。它的语法在很大程度上类似于 Korn,但这并不意味着它与 Korn shell 完全兼容。无论如何,它不是也从来没有打算成为 sh 语言的 POSIX 兼容解释器(至少在默认模式下不是)。
然而,它有许多模拟模式(csh、ksh 和 sh(以前尝试遵循 SysV sh,现在遵循 POSIX sh)),可用于提高与其他 shell 的兼容性并解释为它们编写的代码。这意味着它可以拥有独立于 sh 或任何其他 shell(如 csh、rc、fish、perl、python...)的自己的语法,并且仍然能够解释为 sh 编写的代码。
如果以sh
、csh
、 或(或以(或对于 Bourne)或)ksh
开头的任何内容调用,或者在较新版本中以,它将进入相应的模拟模式,但这不是您通常使用它们的方式。要解释脚本,您需要运行,而不是。s
b
c
k
--emulate sh/ksh/csh
sh
sh
zsh
当您想要在 zsh 中解释 sh 代码时,您宁愿运行emulate sh
insidezsh
来切换模拟模式sh
,也可以运行emulate sh -c 'some code'
以在模拟some code
中解释。sh
所以,在 内zsh
:
emulate sh -c 'printf "%d\n" 024'
printf
将以更 POSIX 的方式运行。
至于printf
,大约 1986 年,一个printf
实用程序首次出现在 Research Unix 第九版(来自 AT&T Research / Bell Labs,未广泛使用)中,用于通过 libc 函数获得与 C 中可用的相同文本格式化 API printf
。
虽然它C
采用格式第一个参数作为指向 NUL 分隔的字节数组和不同类型(指针、整数、双精度……)的额外参数的指针,但可执行文件只能将参数作为字符串。因此,要格式化数字(例如 )%d
,需要为其提供数字的文本表示形式,并将其转换回二进制整数,然后printf
将其转换回文本以进行输出。
在里面V9 中的原始实现,printf
识别与 C 语言中相同的数字( dec -123
、+123
、 Octal 0123
、 hex 0x123
、 float 1.2e-2
、 Even'x'
或"foo"
(其中它是使用的第一个字符的值))。
SVR4(V7 的后代)也有一个非常愚蠢的printf
实用程序,它只是执行以下操作:
printf(fmt, argv[2], argv[3], argv[4], argv[5],
argv[6], argv[7], argv[8], argv[9],
argv[10], argv[11], argv[12], argv[13],
argv[14], argv[15], argv[16], argv[17],
argv[18], argv[19], argv[20]);
%s
因此它仅对-type 格式化有用,因为printf()
仅给出了指向字符串参数的指针。
POSIX.2(在 80 年代末闭门编写并于 1992 年发布(当时还不能免费使用))指定了一个printf
命令。由于该echo
实用程序非常不便于携带且不可靠,因此非常需要一种替代方案。由于某种原因,他们没有使用ksh
的print
内置函数,而是指定了一个printf
主要基于 V9 实现的实用程序,并在将字符串转换为数字时参考了 C 语言。
ksh88,POSIX 规范所基于的 shellsh
从来没有printf
内置的(它的 pdksh 克隆也没有)。它有自己的print
内置函数作为echo
. ksh93 中添加了-f
的选项以及print
的printf
别名print -f
。
在 ksh 中,大多数以数字作为输入的内置函数或构造都将接受任何算术表达式,而不仅仅是数字常量。所以在 ksh93 中
printf '%d\n' '1 + 1'
会打印2
.
在早期版本中,printf '%d\n' 010
会打印 10,而不是 8。
在 ksh shell 算术表达式中,最初,带有前导 0 的数字不被视为八进制,因为在 shell 中,处理以 0 填充的十进制数字(例如日期/时间、文件名)比处理以 0 填充的十进制数字更常见。八进制数。
然而,POSIX 规范确实要求将它们视为八进制,而大多数 shell 都忽略了这一点(因为标准所基于的 shell 的原始实现并未这样做)。然而之后PASC 解释请求这更清楚地表明,大多数 shell 开始改变它们的行为(包括 ksh93、并且zsh
,尽管这已被恢复),造成很大的痛苦。
如果你看一下ksh93 的发布历史,您会注意到以 0 为前缀的数字作为八进制的处理已经打开和关闭。即使在今天,您也会发现算术表达式中的 010 在((...))
or内部是 8 $((...))
,但在大多数其他地方(包括let '...'
、array[...]
和[[ ... -eq ... ]]
...)是 10 printf
。set -o posix
使得大部分地方变成8,一个值得注意的例外是……printf
再次。
由于 zsh 从未声称自己是 POSIX shell,它只是添加了一个新选项(octal_zeroes
)2000 年,在sh
仿真中启用,但在其他方面则不然。
Aprintf
2001 年晚些时候添加了最初使用strtod()
将文本转换为数字,因此将前导 0 的数字视为八进制,但后来更改为接受像 ksh93 中的任何算术表达式,因此受选项的约束octal_zeroes
,并允许更多数字格式,例如0b10010
、ksh 风格的任意基于数字(12#A001
, 2#10010
... ), 1_000_000
...
因此,在 中zsh
,要将八进制数传递给printf
,选项是:
- 模拟
sh
并使用前导 0:emulate sh -c 'printf %d 024'
- 设置
octalzeroes
选项:set -o octalzeroes; printf %d 024
- 仅在本地范围内相同:(
(){ set -o localoptions -o octalzeroes; printf %d 024; }
此处在匿名函数中)。 - 使用
8#oooo
始终被认可的符号:printf %d '8#24'
- 禁用
printf
内置函数,并假设系统的printf
实用程序在这方面符合 POSIX:disable printf; printf %d 024
。 - 调用独立的其他方法
printf
:(command printf %d 024
不在sh
仿真中),=printf %d 024
(不在sh
仿真中)。
答案2
用于command printf ...
运行标准 printf 实用程序。 zsh 的 printf 有自己的参数解释,与 POSIX 标准冲突,%d 将参数解释为算术表达式,因此您可以使用printf %d '8#24'
.
答案3
默认情况下,zsh 不符合 POSIX 标准,而且偏差也不小。
这是使用 zsh 的常见问题。
尝试:
zsh --emulate sh -c 'printf "%d\n" 024'
它将打印 20
但请注意,这仅适用于最新zsh
版本。
如果您喜欢一种甚至适用于旧zsh
版本的方法,那么有一种方法可以做到这一点。创建一个$HOME/.zshenv
包含以下内容的文件:
if [ -n "$ZSH_EMULATION" ]; then
emulate "$ZSH_EMULATION"
fi
如果您有该文件,您可以调用:
ZSH_EMULATION=sh zsh -c 'printf "%d\n" 024'
这适用于任何版本的zsh
.
BTW:当前相关文本ZSH_EMULATION
已经在评论中讨论后引入。