bash
的最佳參考(包括引用的工作方式)是bash手冊本身,它幾乎可以安裝在您的機器上,您可以通過輸入man bash
而無需互聯網連接即可閱讀。閱讀起來很多,但沒有真正的替代品。
儘管如此,我會盡力解釋這個特殊問題。有兩件重要的事情需要知道:首先,bash
如何(以及何時)將命令行分割爲單獨的「單詞」(或命令行參數);第二,什麼[email protected]
和$*
的意思。這些並不完全無關。
分詞部分由特殊參數IFS
控制,但我只是提到這一點;我假設它沒有被改變。有關更多詳細信息,請參閱man bash
。
下面,我打電話引用用雙引號("..."
)弱引用一個字符串,用單引號('...'
)強引用引用。反斜槓(\
)也是一種強烈的引用形式。
字分裂發生的情況:
後參數(殼變量)被取代成它們的值,
只要有空白字符的序列,
除非以任何方式引用空格(" "
,' '
,\
是三種方式),
在報價被刪除之前。
一旦命令已經被分成的話,第一個詞被用來尋找程序或函數來調用,且剩餘的詞成爲程序的參數。 (我忽略了許多像shell元字符,重定向,管道等等的東西。有關更多詳細信息,請參閱man bash
。)
參數取代有它們的值(步驟1)如果它們的名稱是由前面$
除非$name
強烈引用(即,'$name'
或,例如,\$name
)。還有更多形式的參數替換。有關更多詳細信息,請參閱man bash
。
現在,[email protected]
和$*
都表示「當前命令/函數的所有位置參數」,並且如果它們沒有引號使用,則它們完全相同。它們被所有的位置參數取代,每個參數之間有一個空格。由於這是一種參數替換(如上所述),所以在替換後發生分詞,除非替換用引號引起,如上面的列表中所示。
如果替換是用引號括起來的,那麼根據上述規則,在參數之間插入的空格不受分詞的限制。這正是$*
的工作原理。 $*
被空格分隔的命令行參數取代,結果是分詞; "$*"
被空格分隔的命令行參數替換爲單個單詞。
"[email protected]"
是一個例外。事實上,這就是爲什麼[email protected]
存在。如果[email protected]
位於弱引號內("[email protected]"
),則刪除引號,並且單獨引用每個位置參數的。這些引用的位置參數然後被間隔分開並代替[email protected]
。由於[email protected]
不再被引用,插入的空格確實會導致分詞。最終的結果是個別參數保留爲單個詞。
如果不是完全清楚的話,下面是一個例子。 printf
具有重複提供的格式的優點,直到它用完參數,這使得很容易看到發生了什麼。
showargs() {
echo -n '$*: '; printf "<%s> " $*; echo
echo -n '"$*": '; printf "<%s> " "$*"; echo
echo -n '"[email protected]": '; printf "<%s> " "[email protected]"; echo
}
showargs one two three
showargs "one two" three
(揣摩你執行它之前是什麼打印。)
人們常常說,你幾乎總是需要"[email protected]"
,幾乎從不"[email protected]"
或$*
。這通常是正確的,但也是這種情況,你幾乎從不想要"something with [email protected] inside of it"
。要了解這一點,您需要知道"something with [email protected] inside of it"
的作用。這有點奇怪,但它不應該是意想不到的。我們將採取的sbt
調用從OP作爲一個例子:
sbt "run-main com.longpackagename.mainclass [email protected] arg3"
與提供給函數的兩個位置參數,以使得$1
是arg1
和$2
是arg2
。
首先,bash刪除[email protected]
左右的引號。但是,它不能完全刪除它們,因爲那裏也有引用文本。因此,它具有事後封閉引用的文本並重新打開引號,生產:
sbt "run-main com.longpackagename.mainclass "[email protected]" arg3"
現在,它可以在所列出的,間隔分開的參數代替:
sbt "run-main com.longpackagename.mainclass ""arg1" "arg2"" arg3"
這是現在字處理駁:
sbt
"run-main com.longpackagename.mainclass ""arg1"
"arg2"" arg3"
和引號被刪除:
sbt
run-main com.longpackagename.mainclass arg1
arg2 arg3
sbt
僅需要一個位置參數。你給了它兩個,它忽略了第二個。
現在,假設函數是用一個參數"arg1 arg2"
調用的。在這種情況下,的[email protected]
結果在替代:
sbt "run-main com.longpackagename.mainclass ""arg1 arg2"" arg3"
和字分割產生
sbt
"run-main com.longpackagename.mainclass ""arg1 arg2"" arg3"
沒有引號:
sbt
run-main com.longpackagename.mainclass arg1 arg2 arg3"
和只存在一個用於sbt
位置參數。
爲什麼是,他們*被'sbt'吃掉了。引用本身並不會幫助你。 –