如何檢測是否向 shell 提示符號提供了命令

如何檢測是否向 shell 提示符號提供了命令

我想編寫一個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 的空間。

可能嘗試在結果文件中使用mktmpEarly in your .bashrc、 before assignment 和 stuff 資訊;PS1然後您可以將當前的內容\#與儲存在其中的內容進行比較,但現在您的提示取決於磁碟 I/O,這是在緊急情況下將自己鎖定的好方法。

相關內容