在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”等,所有文件名连接成一个长行(而不是每个都在自己的行上打印)。
我的问题是:
答案 0 :(得分:7)
1)执行反引号之间的文本并替换为所附命令的输出,因此:
echo `echo 42`
将扩展为:
echo 42
这称为Command Substitution,也可以使用语法$(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字符串,其中包含 space 等字符(还有其他引用字符串的方法)在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
之类的通配符表达式被称为Globbing的过程中的扩展文件名替换。在大多数命令中没有将表达式括在字符串中时,使用通配符激活Globbing:
echo *.mkv
将打印:
a.mkv b.mkv c.mkv
同时
echo "*.mkv"
打印:
*.mkv
i
循环中的for
变量取值“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的建议,您应该检查useless use of ls
,因为这被视为bash pitfall,并且有更好的方法来实现您的目标试图做。
答案 1 :(得分:1)
'expression',将在表达式中输出确切的字符串。
`表达式`,将执行表达式的内容并且echo输出它。
例如:
x="ls"
echo "$x" --> $x
echo `$x` --> file1 file2 ... (the content of your current dir)
答案 2 :(得分: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文件名的列表;这就是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
你的循环将运行四次(“file”,“1.mkv”,“file”,“2.mkv”)。有人提到的另一种形式的循环for i in *.mkv; do ...
没有这个问题。为什么?因为当bash扩展“* .mkv”时,它在幕后做了一件聪明的事情并将每个文件名视为一个单元,就像你在引号中说“file 1.mkv”“file 2.mkv”一样。在使用ls
的情况下,它无法执行此操作,因为在将扩展的文件名列表传递给ls
之后,bash无法知道返回的内容是那些相同文件名的列表。 ls
可能是任何命令。)