
我想編寫一個PROMPT_COMMAND
回應之前提供給命令提示字元的任何內容的程式碼。例如,要在廣泛、資訊豐富的提示或簡單、緊湊的提示之間切換,如下所示:
mikemol@serenity ~ $ echo hi
hi
$ echo ho
ho
$ echo hum
hum
$
mikemol@serenity ~ $
只有當值不為空時,值才會新增到 shell 的歷史記錄中,因此我不能簡單地測試最近的歷史記錄條目是否為空。set | grep some_command
運行後運行some_command
沒有給我任何結果,因此似乎不存在包含該資訊的環境變數。
我主要使用bash
,但對 POSIX 相容的解決方案和其他 shell 感到好奇。
答案1
PROMPT_COMMAND
我最終根本不需要。感謝克里斯多福為我指明了正確的方向。
相反,考慮這個文件ps1.prompt
:
${__cmdnbary[\#]+$(
echo '\u@\h: \w' # Your fancy prompt goes here, with all the usual special characters available.
) }${__cmdnbary[\#]=}\$
然後我可以將其輸入到我的PS1
:
PS1=$(cat ps1.prompt)
(你不必這樣做,但我發現它對於插圖和編輯來說很方便。)
所以我們看到:
mikemol@zoe:~$ echo hi
hi
mikemol@zoe:~$ echo ho
ho
mikemol@zoe:~$ echo hum
hum
mikemol@zoe:~$
mikemol@zoe:~$ PS1=$(cat ps1.prompt)
$
mikemol@zoe: ~ $ echo hi
hi
$ echo ho
ho
$ echo hum
hum
$
mikemol@zoe: ~ $
我們正在使用數組 hack在這裡展示bash
,但我們不使用的參數替換,而是${parameter:-word}
使用${parameter+word}
so 我們僅在沒有先前的命令運行時觸發。
這需要一些解釋,因為我們被迫在邏輯中使用雙重否定。
如何${__cmdnbary[\#]-word}${__cmdnbary[\#]=}
運作
在最初的陣列破解演示中,${__cmdnbary[\#]-word}${__cmdnbary[\#]=}
使用了該構造。 (為了清楚起見,我已替換$?
為word
)。如果您對參數擴展和數組不是特別熟悉(我不是),那麼根本不清楚發生了什麼。
首先,了解一下\#
每手冊:
\#
該指令的指令編號
…
命令編號是目前 shell 會話期間執行的命令序列中的位置。
這意味著\#
只會改變當且僅當執行命令。如果使用者在提示字元下輸入空白行,則不會執行任何命令,因此\#
不會變更。
${__cmdnbary[#]=}中空字串的設定
${__cmdnbary[\#]=}
使用參數擴展。回到手冊:
${parameter:=word}
指定預設值。如果參數未設定或為空,則單字的擴展將分配給參數。然後替換參數的值。
因此,如果__cmdnbary[\#]
未設定或為 null,則此構造將分配一個空字串(word
在我們的例子中是一個空字串),並且整個構造將在我們的輸出中替換為相同的空字串。
__cmdnbary[\#]
將要總是當我們第一次看到它時,它會被取消設定或為 null,因為 # 是單調的——它總是遞增或保持不變。 (也就是說,直到它循環,可能大約 2^31 或 2^63,但我們還會遇到其他問題長的在我們到達那裡之前。我將這個解決方案描述為有點黑客行為是有原因的。
條件式在${__cmdnbary[\#]-word}
${__cmdnbary[\#]-word}
是另一個參數擴展。從手冊中:
${parameter:-word}
使用預設值。如果參數未設定或為空,則取代單字的擴展。否則,將替換參數的值。
所以,如果數組條目\#
是未設定或為空,word
在其位置使用。由於我們在檢查之前不會嘗試分配__cmdnbary[\#]
(使用替換${parameter:=word}
),因此第一次檢查給定值時\#
應該會發現數組中的該位置未設定。
bash
使用稀疏數組
對於那些習慣 C 風格陣列的人需要澄清一點。bash
實際上使用稀疏數組;直到你分配某物到數組中的某個位置,該位置未設定。空字串與“null 或 unset”不同。
為什麼我們要用 ${__cmdnbary[#]+word}${__cmdnbary[#]=} 來代替
${__cmdnbary[\#]+word}${__cmdnbary[\#]=}
和 ${__cmdnbary[#]-word}${__cmdnbary[#]=} look very siilar. The *only* thing we change between the two constructs can be found in the first portion; we use
${parameter:+word} instead of
${parameter:-word}`。
請記住${parameter:-word}
,只有當為 null 或未設定word
時,才會出現- 在我們的例子中,只有當我們parameter
沒有設定數組中的位置,當且僅當\#
增加時我們不會這樣做,這只有在我們剛剛執行命令時才會發生。
這意味著,對於${parameter:-word}
,我們只會呈現,word
如果我們沒有執行了一個命令,這與我們想要做的恰恰相反。所以,我們用${parameter:-word}
它來代替。再次,來自手冊:
${parameter:+word}
使用替代值。如果參數為空或未設置,則不會取代任何內容,否則將取代單字的擴展。
這是(不幸的是)更需要理解的雙重否定邏輯,但你就是這樣。
提示本身
我們已經解釋了切換機制,但是提示本身又如何呢?
在這裡,我用來$( ... )
包含提示的實質內容。主要是為了我自己的可讀性;你不必那樣做。您可以替換$( ... )
為通常在變數賦值中填充的任何內容。
為什麼它是駭客攻擊?
還記得我們如何為稀疏數組添加條目嗎?我們不會刪除這些條目,因此數組將永遠增長,直到退出 shell 會話為止;外殼漏了PS1
。據我所知,沒有辦法未設定提示中的變數或陣列位置。你可以嘗試一下$()
,但你會發現它不起作用;對子 shell 內的變數命名空間所做的變更不會套用於派生子 shell 的空間。
你可能嘗試在結果文件中使用mktmp
Early in your .bashrc
、 before assignment 和 stuff 資訊;PS1
然後您可以將當前的內容\#
與儲存在其中的內容進行比較,但現在您的提示取決於磁碟 I/O,這是在緊急情況下將自己鎖定的好方法。