2015-08-23 93 views
3

运行在OS X终端这一说法bash中的'和'有什么区别?

for i in `ls -v *.mkv`; do echo $i; done 

将成功地打印出目录中的所有文件名的名称以便与自身线上的每个文件名。

来源:This StackOverFlow answer

但是,如果我在OS X终端运行该语句

for i in 'ls -v *.mkv'; do echo $i; done 

输出为 “LS -v fileName1.mkv fileName2.mkv” 等所有的文件名字拼接成一条长线(而不是每条线都打印在一条线上)。

我的问题是:

  1. 有什么'和'在bash有什么区别?
  2. 为什么这种差异会导致完全不同的输出?
  3. 什么键盘组合产生`? (键盘组合)
+2

不要使用'ls'。只需使用'for in in * .mkv;做回声“$我”; ' – anubhava

+1

'表达',将输出确切的字符串。 'expression'执行表达式的内容并且echo输出它。 – Zohar81

+1

看看这篇文章:http://askubuntu.com/a/20041/336375 – Cyrus

回答

7

1)文本之间由所附的命令的输出执行和更换,所以:

echo `echo 42` 

将扩大到:

echo 42 

这称为Command Substitution,也可以使用语法$(command)来实现。在你的情况,以下几点:

for i in `ls -v *.mkv`; do ... 

是由类似取代(如果您的目录包含3个文件名为a.mkvb.mkv℃。MKV):报价或双引号

for i in a.mkv b.mkv c.mkv; do ... 

文本之间与像空间内他们scaped字符只是普通击弦(还有其他的方式引用字符串Bash和有described here):

echo "This is just a plain and simple String" 
echo 'and this is another string' 

使用'"之间的差别在于之间包围的字符串个可以interpolate变量,例如:

variable=42 
echo "Your value is $variable" 

或者:

variable=42 
echo "Your value is ${variable}" 

打印:

Your value is 42 

2)*.mkv通配符表达式由扩展名替换在程序中ss被称为Globbing。通配用在大多数的命令的通配符激活而不包围一个字符串内的表达式:

echo *.mkv 

将打印:

a.mkv b.mkv c.mkv 

同时:

echo "*.mkv" 

打印:

*.mkv 

for循环的i变量取值“ls -v *.mkv”,但在循环体内的echo命令采用$i没有引号,所以猛砸施加通配符那里,你结束了以下内容:

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 

哪只是应用了通配符后的文件名的单行字符串。


3)这取决于你的键盘布局。

保持字符的一个窍门是使用程序ascii,搜索字符96(十六进制60),将其复制并保存在剪贴板上(您可以使用parcellite或任何其他符合您需要的剪贴板管理器)。


更新:正如@triplee建议,你应该检查useless use of ls,因为这被认为是一个bash pitfall和有更好的方法来达到你想要做什么。

+0

也许可以提到[无用的'ls'](http://www.iki.fi/era/unix/award.html#ls)以及http:// mywiki .wooledge.org/ParsingLs – tripleee

+0

我不确定你想要使用对复合命令的引用做什么;没有做特殊的处理。而且,引号不会创建一个字符串; 'foo'和'“foo”'都创建了相同的字符串。引号所包含的每个*字符都隐含地转义。 'foo \ bar'和'“foo bar”'创建相同的字符串。 – chepner

+0

@chepner非常感谢那些不错的观察结果。更新了答案的字符串部分。想要详细阐述复合命令部分?我试图想出一个解释,为什么在'in'之后的字符串中应用globbing,而不是在其他命令中,我确信这是由于复合命令。 – higuaro

1

'expression',将输出表达式中的确切字符串。

`expression`,将执行表达式的内容并回显它的输出。

例如:反引号

x="ls" 
echo "$x" --> $x 
echo `$x` --> file1 file2 ... (the content of your current dir) 
1

反引号意思是“在反引号之间运行一个命令作为命令,然后就像我在这里输入该命令的输出一样”。正如其他人所说,单引号的意思是一个文字串。因此,在第一种情况下,会发生什么情况是这样的:

的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文件名列表;这是真的。 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将任何空格 - 包括空格和换行符都视为分隔符,s o如果你的文件名是:

file 1.mkv 
file 2.mkv 

你的循环将运行四次( “文件”, “1.mkv”, “文件”, “2.mkv”)。有人提到的其他形式的循环,for i in *.mkv; do ...没有这个问题。为什么?因为当bash扩展“* .mkv”时,它会在幕后做一个聪明的事情,并将每个文件名作为一个单位来对待,就好像你在引号中所说的“file 1.mkv”“file 2.mkv”一样。在使用ls的情况下,它不能这样做,因为在将扩展的文件名列表传递给ls后,bash无法知道返回的是那些相同文件名的列表。 ls可能是任何命令。)