Zsh

為什麼 zsh printf 不尊重八進製表示法?

  • September 12, 2021
sh -c 'printf "%d " 024'
bash -c 'printf "%d " 024'
zsh -c 'printf "%d" 024'

以上輸出20 20 24。為什麼 zsh 不尊重八進製表示法?有沒有辦法改變這個?

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)ck) 開頭的東西,或在帶有 的較新版本中呼叫--emulate sh/ksh/csh,它將進入相應的仿真模式,但這不是您通常使用的方式。要解釋sh腳本,您將執行sh,而不是zsh.

當你想在zsh中解釋sh程式碼時,你寧願run emulate shinsidezsh來切換仿真模式,你也可以在仿真中執行解釋。sh``emulate sh -c 'some code'``some code``sh

所以,在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實用程序非常不便攜且不可靠,因此非常需要替代方案。出於某種原因,他們沒有選擇kshprint內置,而是指定了一個printf主要基於 V9 實現的實用程序,並在將字元串轉換為數字時參考了 C 語言。

ksh88,POSIXsh規範所基於的 shell 從來沒有printf內置函式(也沒有它的 pdksh 複製)。它有自己的print內置函式作為echo. 在 ksh93 中添加了一個-f選項 for以及.print``printf``print -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 開始改變它們的行為(包括 ksh93zsh,儘管這已被恢復),造成了很大的痛苦。

如果您查看ksh93 的發布歷史記錄,您會注意到以 0 為前綴的數字作為八進制的處理一直處於開啟和關閉狀態。即使在今天,你會發現算術表達式中的 010 在((...))or中是 8 $((...)),但在大多數其他地方,包括let '...'array[...][[ ... -eq ... ]]…中都是 10 printfset -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仿真中)。

引用自:https://unix.stackexchange.com/questions/668558