反引号意味着“运行反引号中的内容作为命令,然后将该命令的输出视为我在此处键入的内容”。单引号表示一个字面字符串。因此,在第一种情况下,发生的情况如下:
bash将
ls -v *.mkv
作为命令运行,输出类似于:
fileName1.mkv
fileName2.mkv
bash然后将其替换回反引号包围的命令所在的位置,即它有效地使您的for
语句变成了这样:
for i in fileName1.mkv fileName2.mkv; do echo $i; done
这里有两个“tokens”: "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
可以是任何命令。
ls
命令,直接使用for i in *.mkv; do echo "$i"; done
。 - anubhavaexpression
执行表达式的内容并输出结果。 - Zohar81