在 Bash 中我運行:
alias myalias='echo foo
echo bar
echo baz'
myalias
返回:
foo
bar
baz
但:
ssh localhost "shopt -s expand_aliases &>/dev/null;
alias myalias='echo foo
echo bar
echo baz'
myalias"
返回:
foo
為什麼?
答案1
我認為你發現了 Bash 的一個錯誤。此錯誤特定於選項-c
。
遠端運行與您的多行別名問題無關。您可以在本地 bash 中嘗試。但不是在 bash 腳本或互動式 bash 中,請嘗試使用-c
選項,如下所示
bash -c "shopt -s expand_aliases &>/dev/null;
alias myalias='echo foo
echo bar
echo baz'
myalias"
與您的問題相同的輸出。僅foo
列印。
為了獲得正確的(預期的)輸出,您必須至少在 後面添加一行myalias
,如 @cuonglm 建議的那樣。
bash -c "shopt -s expand_aliases &>/dev/null;
alias myalias='echo foo
echo bar
echo baz'
myalias
:"
為什麼會這樣呢?為什麼help後面多了一行myalias
?
我只想說這沒有道理。 Bash 中沒有任何文件解釋或提及這種情況,一點也沒有。它不應該以這種方式運行。這是一個錯誤。閱讀完程式碼後,您就會確定這一點。
返回第一個有問題的命令。這次什麼都不用改,重新編譯bash即可“ONESHOT”未定義,那麼您將得到正確的(預期的)輸出。是的,你沒聽錯,由於不同的編譯時配置,該指令有兩種不同的行為。
無論是否定義ONESHOT
,都會導致 Bash 代碼中出現兩條完全不同的路由-c "command"
。如果取消定義 ONESHOT,-c "command"
將運行正常的程式碼路由,這是幾乎所有 bash 執行的程式碼路由,例如互動式命令和 bash 腳本。但如果定義了ONESHOT,-c "command"
則會運行另一條專門為其設計的特定路線,以透過避免分叉來提高其效能。
對於這種情況,正常且最常用的方式可以給出正確的輸出,而特定方式則不能。我認為不一致的行為並不是 Bash 作者想要的。至於哪一種行為是正確的,我傾向於認為正常的方式是正確的。
有關此錯誤的一些詳細信息
以下程式碼段與該錯誤相關。它來自檔案builtins/evalstring.c中的函數parse_and_execute()
while (*(bash_input.location.string))
{
...
}
該while
循環將按行運行,在一個循環中處理一行。在命令中的最後一行read 之後myalias
(見上文),條件while
將變為 false。myalias
擴展為三行回顯,但本次循環只處理了一條回顯;另外兩個迴聲將在下一個循環中處理,但是......沒有另一個循環。
myalias
如果在read 之後再添加一行,myalias
則 in 中的條件while
將保持為 true,因此另外兩個 echo 將有機會在下一個循環中運行。之後的最後一行將在處理myalias
所有擴展的迴聲後進行處理。myalias
更新
忘了說這個問題牽涉到的Bash版本是
GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
答案2
解法(受@cuonglm啟發):
ssh localhost "shopt -s expand_aliases &>/dev/null;
alias myalias='ls
echo foo
echo bar
echo baz'
myalias &&
true"
這將保留退出代碼。然而true
,必須走上新的路線。
它仍然沒有解釋為什麼。但它看起來越來越像一個錯誤。