[英]What's the difference between ` and ' in bash?
在OS X終端中運行此語句
for i in `ls -v *.mkv`; do echo $i; done
將按名稱順序成功打印出目錄中的所有文件名,每個文件名都在其自己的行上。
但是,如果我在OS X終端中運行此語句
for i in 'ls -v *.mkv'; do echo $i; done
輸出是“ls -v fileName1.mkv fileName2.mkv”等,所有文件名連接成一個長行(而不是每個都在自己的行上打印)。
我的問題是:
1)執行反引號之間的文本並替換為所附命令的輸出,因此:
echo `echo 42`
將擴展為:
echo 42
這稱為命令替換 ,也可以使用語法$(command)
來實現。 在您的情況下,以下內容:
for i in `ls -v *.mkv`; do ...
被替換為類似的東西(如果您的目錄包含3個名為a.mkv , b.mkv和c.mkv的文件 ):
for i in a.mkv b.mkv c.mkv; do ...
引號或雙引號之間的文本只是普通的Bash字符串,其中包含空格中的字符(在Bash中引用字符串的其他方法在此處描述 ):
echo "This is just a plain and simple String"
echo 'and this is another string'
使用之間的差值'
和"
是之間包圍的字符串"
可以內插變量,例如:
variable=42
echo "Your value is $variable"
要么:
variable=42
echo "Your value is ${variable}"
打印:
Your value is 42
2) *.mkv
類的通配符表達式在稱為Globbing的過程中被擴展的文件名替換 。 在大多數命令中使用通配符激活Globbing, 而不將表達式括在字符串中:
echo *.mkv
將打印:
a.mkv b.mkv c.mkv
與此同時:
echo "*.mkv"
打印:
*.mkv
你的for
循環中的i
變量取值為“ ls -v *.mkv
”,但是循環體內的echo
命令需要$i
不帶引號,所以Bash在那里應用了globbing,你最終會得到以下結果:
for i in 'ls -v *.mkv'; do
# echo $i
#
# which is expanded to:
# echo ls -v *.mkv (no quotes)
#
# and the globbing process transform the above into:
echo ls -v a.mkv b.mkv c.mkv
在應用globbing之后,這只是一個帶有文件名的單行字符串。
3)這取決於您的鍵盤布局。
保持角色的一個技巧是使用程序ascii
,搜索角色96(十六進制60),復制它並將其保存在剪貼板上(您可以使用parcellite
或任何其他適合您需要的剪貼板管理器)。
更新:正如@triplee建議的那樣 ,你應該檢查無用的ls
因為這被認為是一個重擊陷阱,並且有更好的方法來實現你想要做的事情。
'expression',將在表達式中輸出確切的字符串。
`expression`,將執行表達式的內容並且echo輸出它。
例如:
x="ls"
echo "$x" --> $x
echo `$x` --> file1 file2 ... (the content of your current dir)
反引號意味着“在反引號之間運行作為命令,然后表現得好像我在這里輸入了該命令的輸出”。 正如其他人所說,單引號意味着只是一個字符串。 所以在第一種情況下,會發生什么:
bash運行ls -v *.mkv
作為命令,輸出如下內容:
fileName1.mkv
fileName2.mkv
然后bash將其替換回反引號包圍的命令所在的位置,即它有效地使你for
聲明成為:
for i in fileName1.mkv fileName2.mkv; do echo $i; done
這有兩個“令牌”:“fileName1.mkv”和“fileName2.mkv”,所以循環運行它的主體( echo $i
)兩次,每次一次:
echo fileName1.mkv
echo fileName2.mkv
默認情況下, echo
命令在完成回顯你告訴它回顯的內容后會輸出一個換行符,這樣你就可以獲得你想要的輸出,每個文件名都在它自己的行上。
但是,當您使用單引號而不是反引號時,單引號之間的內容不會被評估; 即bash不會將其視為一個命令(或任何特殊的命令;單引號告訴bash,“這個文字並不特別;不要試圖評估它或對它做任何事情”)。 這意味着你正在運行的是這樣的:
for i in 'ls -v *.mkv'; do echo $i; done
其中只有一個標記,即文字字符串“ls -v * .mkv”,因此循環體僅運行一次:
echo ls -v *.mkv
...但是在bash運行該echo
之前,它會擴展“* .mkv”。
我掩飾了這上面,但是當你做這樣的事情ls *.mkv
,它實際上不是ls
做的轉換*.mkv
到所有的.mkv文件名列表; 這就是bash。 ls
永遠不會看到*.mkv
; 在ls
運行時,bash已將其替換為“fileName1.mkv fileName2.mkv ...”。
同樣對於echo
:在運行此行之前,bash會擴展*.mkv
,因此實際運行的是:
echo ls -v fileName1.mkv fileName2.mkv
輸出此文本:
ls -v fileName1.mkv fileName2.mkv
(*腳注:還有另一件我掩飾過的東西,那是文件名中的空格。反引號之間的ls
輸出是一個文件名列表,每行一個。麻煩的是,bash看到任何空格 - 兩個空格和換行符 - 作為分隔符,所以如果你的文件名是:
file 1.mkv
file 2.mkv
你的循環將運行四次(“文件”,“1.mkv”,“文件”,“2.mkv”)。 有人提到的另一種形式的循環, for i in *.mkv; do ...
for i in *.mkv; do ...
沒有這個問題。 為什么? 因為當bash擴展“* .mkv”時,它在幕后做了一件聰明的事情並將每個文件名視為一個單元,就像你在引號中說“file 1.mkv”“file 2.mkv”一樣。 在你使用ls
的情況下,它不能這樣做,因為在將擴展的文件名列表傳遞給ls
,bash無法知道返回的是那些相同文件名的列表。 ls
本來可以是任何命令。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.