Bash:為什麼遠端執行時會忽略換行符號後的別名?

Bash:為什麼遠端執行時會忽略換行符號後的別名?

在 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必須走上新的路線。

它仍然沒有解釋為什麼。但它看起來越來越像一個錯誤。

相關內容