一般問題:
myvar
在 Bash 中,我知道可以透過兩種方式使用變數:
# Define a variable:
bash$ myvar="two words"
# Method one to dereference:
bash$ echo $myvar
two words
# Method two to dereference:
bash$ echo "$myvar"
two words
在上述情況下,行為是相同的。這是因為echo
工作原理。在其他 Unix 實用程式中,單字是否用雙引號組合在一起將會產生巨大的差異:
bash$ myfile="Cool Song.mp3"
bash$ rm "$myfile" # Deletes "Cool Song.mp3".
bash$ rm $myfile # Tries to delete "Cool" and "Song.mp3".
我想知道這種差異的更深層意義是什麼。最重要的是,我如何準確地查看將傳遞給命令的內容,以便我可以查看它是否被正確引用?
具體奇數範例:
我將只編寫具有觀察到的行為的程式碼:
bash$ mydate="--date=format:\"%Y-%m-%d T%H\""
bash$ git log "$mydate" # This works great.
bash$ git log $mydate
fatal: ambiguous argument 'T%H"': unknown revision or path not in the working tree.
為什麼我需要雙引號?在沒有雙引號的情況下取消引用變數後,git-log 到底看到了什麼?
但現在看到這個:
bash$ nospace="--date=format:\"%Y-%m-%d\""
bash$ git log $nospace # Now THIS works great.
bash$ git log "$nospace" # This kind of works, here is a snippet:
# From git-log output:
Date: "2018-04-12"
糟糕,為什麼現在列印的輸出中有雙引號?看起來如果雙引號是不必要的,它們不會被刪除,當且僅當它們不需要時,它們才會被解釋為文字引號字元。
Git 作為參數傳遞的是什麼?我希望我知道如何找到答案。
為了讓事情變得更複雜,我編寫了一個 Python 腳本,argparse
它只列印所有參數(正如Bash 解釋的那樣,因此使用雙引號文字,Bash 認為它們是參數的一部分,並且將單字分組或不分組為Bash )認為合適),並且 Pythonargparse
腳本的行為非常合理。可悲的是,我認為argparse
可能會默默地修復 Bash 的一個已知問題,從而掩蓋 Bash 傳遞給它的混亂內容。這只是一個猜測,我不知道。也許 git-log 正在秘密搞砸 Bash 傳遞給它的內容。
或者也許我根本不知道發生了什麼事。
謝謝。
編輯編輯:在得到任何答案之前,讓我現在這麼說:我知道我可以或許在整個內容中使用單引號,然後不要轉義雙引號。實際上,這對於我使用 git-log 的最初問題確實效果更好,但我在其他一些上下文中測試了它,它幾乎同樣不可預測和不可靠。引用內部變數會發生一些奇怪的事情。我什至不會發布所有用單引號發生的奇怪的事情。
編輯2 - 這也不起作用:我剛剛有了這個絕妙的想法,但它根本行不通:
bash$ mydate="--date=format:%Y-%m-%d\ T%H"
bash$ git log "$mydate"
# Git log output has this:
Date: 2018-04-12\ T23
所以它沒有引號括起來,但它在日期字串中有一個文字反斜線字元。另外,git log $mydate
如果變數中沒有引號,則會出現反斜線空格的錯誤。
答案1
不同的方法:
當您執行時git log --format="foo bar"
,這些引號不會被 git 解釋——它們會被 shell 刪除(並保護引用的文字不會分割)。這會產生一個參數:
--format=foo bar
然而,當不加引號時變數被擴展,結果經過分詞,但是不是透過取消引用。因此,如果您的變數包含--format="foo bar"
,它將擴展為以下參數:
--format="foo
bar"
這可以使用以下方法進行驗證:
printf '%s\n' $變量
...以及列印其收到的參數的任何簡單腳本。
#!/usr/bin/env perl 對於 $i (0..$#ARGV) { 印 ($i+1)." = ".$ARGV[$i]."\n"; }
#!/usr/bin/env python3 導入系統 對於 i,枚舉中的 arg(sys.argv): 印(我,“=”,arg)
如果您始終有 bash 可用,則首選解決方法是使用大批變數:
myvar=( --format="foo bar" )
這樣,通常的解析是在賦值期間完成的,而不是在擴展期間完成的。您可以使用此語法來擴展變數的內容,每個元素都有自己的參數:
git log "${myvar[@]}"
答案2
為什麼你原來的命令不起作用?
bash$ mydate="--date=format:\"%Y-%m-%d T%H\""
bash$ git log "$mydate" # This works great.
bash$ git log $mydate
fatal: ambiguous argument 'T%H"': unknown revision or path not in the working tree.
你問:
為什麼我需要雙引號?在沒有雙引號的情況下取消引用變數後,git-log 到底看到了什麼?
如果在 周圍不使用雙引號$mydate
,則該變數將逐字展開,並且 shell 行在執行之前將如下所示:
git log --date=format:"%Y-%m-%d T%H"
^————————————^—————— literal quotes
在這裡,您(不必要地)透過\"
在變數賦值中添加了文字引號。
由於該命令將經過分詞,git
將收到三個參數,log
,--date-format:"%Y-%m%-d
和T%H"
,因此抱怨找不到任何名為 的提交或物件T%H"
。
正確的做法是什麼?
如果要將參數保留在一起,且該參數包含空格,則必須將參數以引號引起來。通常,始終將變數括在雙引號中。
即使變數內部有空格,這也有效:
mydate="--date=format:%Y-%m-%d T%H"
git log "$mydate"
現在,第三個參數將為git
,$mydate
包括您最初指定的空格。所有引號在傳遞給 之前都會被 shell 刪除git
。
您根本不需要額外的引號 - 如果您只想git
查看一個參數,請在傳遞變數時將該參數括在引號中"$mydate"
。
另外,你問:
bash$ nospace="--date=format:\"%Y-%m-%d\""
bash$ git log $nospace # Now THIS works great.
bash$ git log "$nospace" # This kind of works, here is a snippet:
# From git-log output:
Date: "2018-04-12"
你的問題:
糟糕,為什麼現在列印的輸出中有雙引號?
因為你又包含了文字參數中的引號(透過轉義它們),當您忘記在實際命令中引用變數時,它們會變成「真正的」引號。我說「忘記」是因為在 shell 命令中使用不含引號的變數通常只會給您帶來麻煩,而這裡它是在逆轉您在首先指定變數時所犯的錯誤。
PS:我知道這很令人困惑,但這就是 Bash,它遵循一些明確的規則。這裡沒有錯誤。 A相關論文關於 shell 中的檔案名稱也非常有啟發性,因為它涉及 Bash 中的空白處理問題。