Bash是编译型的还是解释型的?

10
当Bash运行您的脚本时,它会将其解析为代码还是命令?Bash是否像Python一样编译并运行您的脚本,还是只是通过其命令解析器运行它?

3
哪些脚本语言可以编译它们的代码? - PM 77-1
5
Python,是其中之一。 - paxdiablo
1
Ruby、Python等编程语言并不会真正地“编译”成文件,而是将文件转换为编译语言,并转化为机器码。这类语言被称为脚本语言,类似于临时编译。可以想象一下,如果你有一个纯文本的C文件,那么gcc会将其编译成一个临时文件并运行它。这就是脚本语言的基本原理。 - Jon .C
Perl会“预编译”解析树——在初始化时尽可能完成所有工作,并可以将该版本保存为“预编译”副本以在某些情况下加快速度。我认为Groovy使用一些JIT字节码生成。不过,我认为Bash是真正的逐行解释器,对吗? - Paul Hodges
我知道多年前我使用的一版 HPUX ksh 解释器会将整个文件预读到缓冲区中,而另一版则只是在文件中移动一个指针;这意味着对于第二个版本,你可以运行该文件并在文件早期进行更改,但正在运行的副本可能会因为指针不再指向一行开头而变得混乱,因为你已更改了偏移量 - 另一版继续运行缓存的副本。两种方法都有优点和缺点,但我更喜欢缓存。 - Paul Hodges
3个回答

19

Bash是一种单通道解释器,这意味着它一次只读取一个命令,进行解释和运行。其他类型的shell(sh、ksh、zsh、csh等)也是如此。

以下是一个示例。我有一个名为test.sh的三行脚本,其内容如下:

echo one
echo two
'

当以 bash test.sh 的方式运行时,它会输出以下内容:

one
two
test.sh: line 3: unexpected EOF while looking for matching `''
test.sh: line 4: syntax error: unexpected end of file

它成功运行了第一个和第二个命令,然后遇到了悬空的单引号并抛出了错误。

假设我们在Perl中编写相同的代码,test.pl

print "one\n"
print "two\n"
'

并使用 perl test.pl 运行它。我们得到:

syntax error at test.pl line 2, near "print"
Can't find string terminator "'" anywhere before EOF at test.pl line 3.

所以,尽管前两行语法上是正确的,但它们根本没有运行。这是因为Perl进行了两次通行证。在第一遍中,它进行语法检查并将脚本转换为内部形式。在第二遍中,它才运行。
Shell单遍执行的简洁性也是其最大的局限性。容忍语法错误,即使运行,也很难用shell语言构建大型和健壮的代码。然而,shell脚本是快速且一次性代码的理想选择,特别是需要使用大量命令行实用程序的情况。

相关:


1
有趣的事实:您甚至可以在解释器(bash)运行时修改脚本,更改将生效:echo“Hello”;echo“echo World”>> $0 - el.pescado - нет войне
这简直是不正确的——“所以,它根本没有运行前两行,尽管它们在语法上是正确的。这是因为Perl进行了两次遍历。在第一次遍历中,它进行语法检查并将脚本转换为内部形式。在第二次遍历中,它运行它。”Perl是单遍编译器。它只进行一次遍历,将所有内容编译成optree(一种AST和opcode),然后将其交给Perl VM执行optree。它不会再进行另一次遍历。现在,在eval的情况下,解析器可以重新调用并修改optree,但那是另一件事。 - Evan Carroll
换句话说,Perl 是一种后期绑定编译器,而不是双通解释器。正因为如此,有些东西看起来有点奇怪。 - Evan Carroll

9
目前(版本4.4),bash shell以纯文本方式运行您的脚本。它不会将文件预编译为某种形式的字节码。
根据源代码,shell本身只使用reader_loop()来处理输入。这在循环内调用read_command(),然后调用execute_command()
由于read_command()还包含对parse_command()的调用,该函数调用YACC解析器函数yyparse(),这意味着解析是基于一行一行进行的,而不是在某个大型编译阶段中提前完成。

8
这就是为什么在Bash中,与编译型语言或即时编译型语言相比,变量名称长度实际上会明显影响性能。例如,如果你将i重命名为更长的名称,time for ((i=0;i<100000;++i)); do :; done将会慢得多。请注意,此处不提供解释。 - Jeffrey Cash

3

另一种看出 bash 每次处理一行输入的方法是创建一个明显有语法错误的脚本,并在一个有效的 echo 语句之后运行该脚本,然后运行该脚本。在产生语法错误之前,bash 将会产生一些输出:

echo output
()

产生

$ bash tmp.sh
output
tmp.sh: line 2: syntax error near unexpected token ')'
tmp.sh: line 2: `()'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接