![$BASH_COMMAND 變數有什麼用?](https://rvso.com/image/1047576/%24BASH_COMMAND%20%E8%AE%8A%E6%95%B8%E6%9C%89%E4%BB%80%E9%BA%BC%E7%94%A8%EF%BC%9F.png)
根據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_COMMAND
tmp
問題一:我目前的假設是否正確:
BASH_COMMAND
僅當命令嘗試實際評估它時才設定;和- 這是不是執行命令後未設置,即使描述可能使我們相信如此?
問題2:如果是,為什麼?表現?如果不是,那麼如何解釋上述命令序列中的行為?
Q3:最後,是否有任何場景可以真正有意義地使用該變數?我實際上試圖在其中使用它$PROMPT_COMMAND
來分析正在執行的命令(並根據該命令做一些事情),但我不能,因為一旦在我的內部$PROMPT_COMMAND
,我執行一個命令來查看變量$BASH_COMMAND
,變數獲取該命令的設定。即使我在 my , thenMYVARIABLE=$BASH_COMMAND
的開頭就包含字串,因為賦值也是一個命令。 (這個問題不是關於我如何在執行中獲取當前命令。我知道還有其他方法。)$PROMPT_COMMAND
MYVARIABLE
MYVARIABLE=$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
的輸出中沒有一行set
,set
本質上是變數賦值的語法「標記」。
OTOH,在該部分的最後一段中說
如果擴充後留下指令名,則如下所述繼續執行。否則,命令退出。
……這表示不然,變數賦值也是命令。
答案3
$BASH_COMMAND 的創意用途
最近發現這個$BASH_COMMAND 的令人印象深刻的使用在實作類似巨集的功能時。
這是別名的核心技巧,並取代了 DEBUG 陷阱的使用。如果您閱讀了上一篇文章中有關 DEBUG 陷阱的部分,您就會認識到 $BASH_COMMAND 變數。在那篇文章中,我說過在每次調用 DEBUG 陷阱之前將其設定為命令文字。好吧,事實證明它是在執行每個命令之前設置的,無論是否有 DEBUG 陷阱(例如運行“echo“this command = $BASH_COMMAND””來查看我在說什麼)。透過為其分配一個變數(僅針對該行),我們在命令的最外層範圍捕獲 BASH_COMMAND,其中將包含整個命令。
作者的上一篇文章在使用 DEBUG 實現技術時也提供了一些良好的背景trap
。trap
在改進版本中被消除。
答案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 $