$BASH_COMMAND 變數有什麼用?

$BASH_COMMAND 變數有什麼用?

根據bash手冊,環境變數BASH_COMMAND包含

目前正在執行或即將執行的命令,除非 shell 正在執行作為陷阱結果的命令,在這種情況下,它是在陷阱時執行的命令。

撇開這個陷阱極端情況不談,如果我理解正確的話,這意味著當我執行命令時,變數BASH_COMMAND包含該命令。不完全清楚該變數在命令執行後是否被取消設定(即,僅可用儘管該命令正在運行,但不是在之後運行),儘管有人可能會爭辯說,因為它是“命令現在正在被處決或即將成為已執行”,這不是命令那隻是被執行。

但讓我們檢查一下:

$ set | grep BASH_COMMAND=
$ 

空的。我以為會看到,BASH_COMMAND='set | grep BASH_COMMAND='也許只是看到BASH_COMMAND='set',但空無一人讓我感到驚訝。

讓我們嘗試一下其他的東西:

$ echo $BASH_COMMAND
echo $BASH_COMMAND
$ 

嗯,這是有道理的。我執行命令echo $BASH_COMMAND,因此變數BASH_COMMAND包含字串echo $BASH_COMMAND。為什麼這次有效,之前卻不行?

讓我們再做set一次:

$ set | grep BASH_COMMAND=
BASH_COMMAND='echo $BASH_COMMAND'
$

所以等等。它曾是當我執行該命令時設置echo,它不是之後取消設定。但當我set再次執行時,BASH_COMMAND 不是設定為set命令。無論我set在這裡執行多少次命令,結果都保持不變。那麼,變數是在執行時設定的echo,而不是在執行時設定的set嗎?讓我們來看看。

$ echo Hello AskUbuntu
Hello AskUbuntu
$ set | grep BASH_COMMAND=
BASH_COMMAND='echo $BASH_COMMAND'
$

什麼?所以當我執行時變數就被設定了echo $BASH_COMMAND,但是不是當我執行echo Hello AskUbuntu?現在差別在哪裡?僅噹噹前命令本身實際上強制 shell 計算變數時才設定該變數嗎?讓我們嘗試一些不同的東西。也許這次是一些外部命令,而不是 bash 內建命令,以進行更改。

$ /bin/echo $BASH_COMMAND
/bin/echo $BASH_COMMAND
$ set | grep BASH_COMMAND=
BASH_COMMAND='/bin/echo $BASH_COMMAND'
$

嗯,好吧...變數又被設定了。那我現在的猜測正確嗎?變數是否僅在必須求值時才設定?為什麼?為什麼?出於性能原因?讓我們再試一次。我們將嘗試$BASH_COMMAND在一個檔案中 grep for ,並且由於$BASH_COMMAND應該包含一個grep命令,grep因此應該 grep for 該grep命令(即,對於它本身)。所以讓我們創建一個適當的文件:

$ echo -e "1 foo\n2 grep\n3 bar\n4 grep \$BASH_COMMAND tmp" > tmp
$ grep $BASH_COMMAND tmp
grep: $BASH_COMMAND: No such file or directory
tmp:2 grep                                      <-- here, the word "grep" is RED
tmp:4 grep $BASH_COMMAND tmp                    <-- here, the word "grep" is RED
tmp:2 grep                                      <-- here, the word "grep" is RED
tmp:4 grep $BASH_COMMAND tmp                    <-- here, the word "grep" is RED
$ set | grep BASH_COMMAND=
BASH_COMMAND='grep --color=auto $BASH_COMMAND tmp'
$

好吧,有趣。該命令grep $BASH_COMMAND tmp被擴展為grep grep $BASH_COMMAND tmp tmp(當然,變數只擴展了一次),所以我在一個不存在的grep檔案中 grep 一次,在檔案 中兩次。$BASH_COMMANDtmp

問題一:我目前的假設是否正確:

  • BASH_COMMAND僅當命令嘗試實際評估它時才設定;和
  • 這是不是執行命令後未設置,即使描述可能使我們相信如此?

問題2:如果是,為什麼?表現?如果不是,那麼如何解釋上述命令序列中的行為?

Q3:最後,是否有任何場景可以真正有意義地使用該變數?我實際上試圖在其中使用它$PROMPT_COMMAND來分析正在執行的命令(並根據該命令做一些事情),但我不能,因為一旦在我的內部$PROMPT_COMMAND,我執行一個命令來查看變量$BASH_COMMAND,變數獲取該命令的設定。即使我在 my , thenMYVARIABLE=$BASH_COMMAND的開頭就包含字串,因為賦值也是一個命令。 (這個問題不是關於我如何在執行中獲取當前命令。我知道還有其他方法。)$PROMPT_COMMANDMYVARIABLEMYVARIABLE=$BASH_COMMAND$PROMPT_COMMAND

這有點像海森堡的測不準原理。只需觀察變量,我就可以改變它。

答案1

回答第三個問題:當然可以按照 Bash 手冊中明確提示的方式有意義地使用它 - 在陷阱中,例如:

$ trap 'echo ‘$BASH_COMMAND’ failed with error code $?' ERR
$ fgfdjsa
fgfdjsa: command not found
‘fgfdjsa’ failed with error code 127
$ cat /etc/fgfdjsa
cat: /etc/fgfdjsa: No such file or directory
‘cat /etc/fgfdjsa’ failed with error code 1

答案2

現在 Q3 已經得到解答(在我看來,正確的是:BASH_COMMAND在陷阱中有用,在其他地方幾乎沒有用),讓我們嘗試 Q1 和 Q2。

Q1 的答案是:你的假設的正確性是不可判定的。這兩個要點的真實性都無法確定,因為它們詢問的是未指定的行為。根據其規範, 的值BASH_COMMAND在命令執行期間設定為該命令的文字。該規範沒有說明在任何其他情況下(即沒有執行任何命令時)其值必須是什麼。它可能有任何價值,也可能根本沒有價值。

Q2 的答案“如果不是,還可以如何解釋上述命令序列中的行為?”那麼邏輯上如下(如果有點迂腐):這是透過以下事實來解釋的: 的值BASH_COMMAND未定義。由於其值未定義,因此它可以具有任何值,這正是序列顯示的值。

後記

我認為您確實在規範中遇到了一個弱點。就是你說的地方:

即使當我在 $PROMPT_COMMAND 的開頭執行 MYVARIABLE=$BASH_COMMAND 時,MYVARIABLE 也會包含字串 MYVARIABLE=$BASH_COMMAND,因為賦值也是命令

根據我閱讀 bash 手冊頁的方式,斜體部分是不正確的。本節SIMPLE COMMAND EXPANSION解釋如何先保留命令列上的變數分配,然後

如果沒有命令名結果[換句話說,只有變數分配],則變數分配會影響目前的 shell 環境。

這對我來說意味著變數賦值不是命令(因此不會出現在 中BASH_COMMAND),就像其他程式語言一樣。這也可以解釋為什麼BASH_COMMAND=set的輸出中沒有一行setset本質上是變數賦值的語法「標記」。

OTOH,在該部分的最後一段中說

如果擴充後留下指令名,則如下所述繼續執行。否則,命令退出。

……這表示不然,變數賦值也是命令。

答案3

$BASH_COMMAND 的創意用途

最近發現這個$BASH_COMMAND 的令人印象深刻的使用在實作類似巨集的功能時。

這是別名的核心技巧,並取代了 DEBUG 陷阱的使用。如果您閱讀了上一篇文章中有關 DEBUG 陷阱的部分,您就會認識到 $BASH_COMMAND 變數。在那篇文章中,我說過在每次調用 DEBUG 陷阱之前將其設定為命令文字。好吧,事實證明它是在執行每個命令之前設置的,無論是否有 DEBUG 陷阱(例如運行“echo“this command = $BASH_COMMAND””來查看我在說什麼)。透過為其分配一個變數(僅針對該行),我們在命令的最外層範圍捕獲 BASH_COMMAND,其中將包含整個命令。

作者的上一篇文章在使用 DEBUG 實現技術時也提供了一些良好的背景traptrap在改進版本中被消除。

答案4

trap...debug 的一個看似常見的用途是改進當您使用「screen」時出現在視窗清單 (^A") 中的標題。

我試圖讓“螢幕”、“視窗列表”更有用,所以我開始尋找引用“陷阱...調試”的文章。

我發現使用 PROMPT_COMMAND 發送「空標題序列」的方法效果不太好,所以我恢復到 trap...debug 方法。

操作方法如下:

1 透過將「shelltitle '$|bash:'」放入「$HOME/.screenrc」中,告訴「screen」尋找轉義序列(啟用它)。

2 關閉調試陷阱向子 shell 的傳播。這很重要,因為如果啟用它,一切都會變得一團糟,請使用:「set +o functrace」。

3 發送「screen」的標題轉義序列進行解釋: trap 'printf "\ek$(date +%Y%m%d%H%M%S) $(whoami)@$(hostname):$(pwd) ${BASH_COMMAND}\e\"'“調試”

它並不完美,但它有幫助,您可以使用此方法將您喜歡的任何內容放入標題中

請參閱以下「視窗清單」(5 個畫面):

名稱標誌數量

1 bash:20161115232035 mcb@ken007:/home/mcb/ken007 RSYNCCMD="sudo rsync" myrsync.sh -R -r "${1}" "${BACKUPDIR}${2:+"/${2}" $ 2 bash:20161115230434 mcb@ken007:/home/mcb/ken007 ls --color=auto -la $ 3 bash:2016115230504 mcb@ken007:/home/mcb/ken007 cat bin/ps @ken007:/home/mcb/ken007 ssh ken009 $ 5 bash:20161115222450 mcb@ken007:/home/mcb/ken007 mycommoncleanup $

相關內容