Nick Bull Asked: 2021-09-12 01:48:25 +0800 CST2021-09-12 01:48:25 +0800 CST 2021-09-12 01:48:25 +0800 CST 为什么 zsh printf 不尊重八进制表示法? 772 sh -c 'printf "%d " 024' bash -c 'printf "%d " 024' zsh -c 'printf "%d" 024' 以上输出20 20 24。为什么 zsh 不尊重八进制表示法?有没有办法改变这个? zsh printf 3 个回答 Voted Best Answer Stéphane Chazelas 2021-09-13T03:28:06+08:002021-09-13T03:28:06+08:00 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, or ksh(或任何以s(或bBourne)c或k) 开头的东西,或在带有 的较新版本中调用--emulate sh/ksh/csh,它将进入相应的仿真模式,但这不是您通常使用的方式。要解释sh脚本,您将运行sh,而不是zsh. 当你想在zsh中解释sh代码时,你宁愿run emulate shinsidezsh来切换仿真模式,你也可以在仿真中运行解释。shemulate sh -c 'some code'some codesh 所以,在zsh: emulate sh -c 'printf "%d\n" 024' 将以printf更 POSIX 的方式运行。 至于printf,大约在 1986 年,Research Unix 第九版(来自 AT&T Research/Bell Labs,未广泛使用)中首次出现了一个实用程序,以使用libc 函数printf获得与 C 中可用的相同文本格式化 API 。printf 虽然C它采用格式第一个参数作为指向以 NUL 分隔的字节数组和不同类型(指针、整数、双精度...)的额外参数的指针,但可执行文件只能将参数作为字符串。因此,要格式化一个数字,例如%d,需要给它一个数字的文本表示形式,并将其转换回二进制整数printf并将其转换回文本以进行输出。 在V9的原始实现中,printf识别出与 C 语言中相同的数字(dec -123、+123、 octal 0123、 hex 0x123、 float 1.2e-2、 even 'x'or "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,POSIXsh规范所基于的 shell 从来没有printf内置函数(也没有它的 pdksh 克隆)。它有自己的print内置函数作为echo. 在 ksh93 中添加了一个-f选项 for以及.printprintfprint -f 在 ksh 中,大多数将数字作为输入的内置函数或构造都将接受任何算术表达式,而不仅仅是数字常量。所以在 ksh93 printf '%d\n' '1 + 1' 会打印2。 在早期版本中,printf '%d\n' 010将打印 10,而不是 8。 在 ksh shell 算术表达式中,最初,带有前导 0 的数字不被视为八进制,因为在 shell 中,处理 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,它只是在 2000 年添加了一个新选项 ( octal_zeroes),在sh仿真中启用,但不是在其他情况下。 2001 年晚些时候添加了一个printf内置函数,最初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仿真中)。 Random832 2021-09-12T02:17:41+08:002021-09-12T02:17:41+08:00 用于command printf ...运行标准 printf 实用程序。zsh 的 printf 有自己的与 POSIX 标准冲突的参数解释, %d 将参数解释为算术表达式,因此您可以将八进制值转换为printf %d '8#24'. schily 2021-09-12T02:21:19+08:002021-09-12T02:21:19+08:00 默认情况下,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. ZSH_EMULATIONBTW:在评论中讨论后已经介绍了当前的文本。
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
, orksh
(或任何以s
(或b
Bourne)c
或k
) 开头的东西,或在带有 的较新版本中调用--emulate sh/ksh/csh
,它将进入相应的仿真模式,但这不是您通常使用的方式。要解释sh
脚本,您将运行sh
,而不是zsh
.当你想在zsh中解释sh代码时,你宁愿run
emulate sh
insidezsh
来切换仿真模式,你也可以在仿真中运行解释。sh
emulate sh -c 'some code'
some code
sh
所以,在
zsh
:将以
printf
更 POSIX 的方式运行。至于
printf
,大约在 1986 年,Research Unix 第九版(来自 AT&T Research/Bell Labs,未广泛使用)中首次出现了一个实用程序,以使用libc 函数printf
获得与 C 中可用的相同文本格式化 API 。printf
虽然
C
它采用格式第一个参数作为指向以 NUL 分隔的字节数组和不同类型(指针、整数、双精度...)的额外参数的指针,但可执行文件只能将参数作为字符串。因此,要格式化一个数字,例如%d
,需要给它一个数字的文本表示形式,并将其转换回二进制整数printf
并将其转换回文本以进行输出。在V9的原始实现中,
printf
识别出与 C 语言中相同的数字(dec-123
、+123
、 octal0123
、 hex0x123
、 float1.2e-2
、 even'x'
or"foo"
(其中使用的第一个字符的值))。SVR4(源自 V7)也有一个非常愚蠢的
printf
实用程序:所以它只对
%s
-type 格式化有用,因为printf()
它只给出了指向字符串参数的指针。POSIX.2(写于 80 年代后期(大部分)闭门造车并于 1992 年发布(当时不可免费获得)),指定了一个
printf
命令。由于该echo
实用程序非常不便携且不可靠,因此非常需要替代方案。出于某种原因,他们没有选择ksh
'print
内置,而是指定了一个printf
主要基于 V9 实现的实用程序,并在将字符串转换为数字时参考了 C 语言。ksh88,POSIX
sh
规范所基于的 shell 从来没有printf
内置函数(也没有它的 pdksh 克隆)。它有自己的print
内置函数作为echo
. 在 ksh93 中添加了一个-f
选项 for以及.print
printf
print -f
在 ksh 中,大多数将数字作为输入的内置函数或构造都将接受任何算术表达式,而不仅仅是数字常量。所以在 ksh93
会打印
2
。在早期版本中,
printf '%d\n' 010
将打印 10,而不是 8。在 ksh shell 算术表达式中,最初,带有前导 0 的数字不被视为八进制,因为在 shell 中,处理 0 填充的十进制数字(如日期/时间、文件名)比处理更常见八进制数。
然而,POSIX 规范确实要求将那些被视为大多数 shell 忽略的八进制(因为标准所基于的 shell 的原始实现没有)。然而,在PASC 解释请求使其更清晰之后,大多数 shell 开始改变它们的行为(包括 ksh93和
zsh
,尽管这已被恢复),造成了很大的痛苦。如果您查看ksh93 的发布历史记录,您会注意到以 0 为前缀的数字作为八进制的处理一直处于开启和关闭状态。即使在今天,你会发现算术表达式中的 010 在
((...))
or中是 8$((...))
,但在大多数其他地方,包括let '...'
、array[...]
和[[ ... -eq ... ]]
...中都是 10printf
。set -o posix
使它在大多数地方变为 8,一个值得注意的例外是......printf
再次。由于 zsh 从未声称自己是 POSIX shell,它只是在 2000 年添加了一个新选项 (
octal_zeroes
),在sh
仿真中启用,但不是在其他情况下。2001 年晚些时候添加了一个
printf
内置函数,最初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
仿真中)。用于
command printf ...
运行标准 printf 实用程序。zsh 的 printf 有自己的与 POSIX 标准冲突的参数解释, %d 将参数解释为算术表达式,因此您可以将八进制值转换为printf %d '8#24'
.默认情况下,zsh 不符合 POSIX 标准,而且偏差并不小。
这是使用 zsh 的一般问题。
尝试:
它会打印 20
但请注意,这仅适用于最新
zsh
版本。如果您喜欢一种甚至适用于旧
zsh
版本的方法,那么有一种方法可以做到这一点。创建一个$HOME/.zshenv
包含以下内容的文件:如果你有那个文件,你可以调用:
这将适用于任何版本的
zsh
.ZSH_EMULATION
BTW:在评论中讨论后已经介绍了当前的文本。