井号“#!”是什么意思?为什么还有“!#”?

10

我已经使用 shebang #! 一段时间了,今天遇到了一个让我困惑的用例。

有几种方法可以运行 Scala 脚本,比如:

#!/usr/bin/env scala 
println("hello world")

不过,我遇到了这个版本的 shebang

#!/bin/sh
exec scala "$0" "$@"
!#
println("hello world")

看起来这个解决方案基本上首先调用bash,然后运行 exec scala "$0" "$@" ,其中$0表示当前文件名,$@是一个位置数组中的输入参数。

我的问题是,这是否意味着在#!!#之间的所有内容都可以在bash中执行?

#!/bin/sh
exec scala "$0" "$@"
echo "oh Yeah"
!#
println("hello world")

它没有报错,但标准输出没有显示 "oh Yeah",有人能解释一下这里发生了什么吗?


更新:在意识到 !# 是 scala 的东西之后,我下载了 scala 的源代码并意识到它只出现在由 Lex Spoon 编写的 ScriptRunner.scala 部分的注释中。


3
exec 用新进程替换当前进程,执行完 exec 后之后的代码不会再执行。 - Etan Reisner
@EtanReisner,那么在这种情况下,新的解释器(Scala解释器)如何知道它应该从println()而不是echo开始,这就是!#用于的地方吗? - B.Mr.W.
换句话说,如果在 exec 之前放置 echo,则可能会使其出现。Perl 在 perldoc perlrun 中记录了一些相关的符号。 - Jonathan Leffler
1个回答

9
!# 这一行对于 shell 而言没有任何意义。 #!/bin/sh 这一行表示脚本将由 /bin/sh 执行。而 exec scala "$0" "$@" 这一行会调用 scala,并将脚本的名称和参数传递给 scala 命令。由于 exec 不会返回,因此 shell 不会看到脚本的其余部分。
虽然我不太了解 Scala,但我的猜测是 Scala 解释器会将从 #!!# 之间的所有内容都视为注释。然后,它会使用 Scala 语句 println("hello world") 开始执行。
简而言之,!# 是 Scala 语法,而不是 shell 语法(但 Scala 语法被设计成可以在 shell 脚本中使用)。
在快速查看Scala 语言规范时,我没有找到这是如何定义的。在这个问题中提到了它,但并没有解释。正如 chepner 3 的评论所建议的那样,这很可能是 Scala 解释器中的一个 hack,而不是实际语言语法的一部分。
som-snytt 在 Scala 解释器中找到了实现这个功能的代码(链接)
object ScriptSourceFile {
  /** Length of the script header from the given content, if there is one.
   *  The header begins with "#!" or "::#!" and ends with a line starting
   *  with "!#" or "::!#".
   */
...

但我想知道它是否有记录。


3
似乎是解释器本身存在漏洞,因为在Scala中shebang本身不是注释。 - chepner
1
在这里:https://github.com/scala/scala/blob/2.11.x/src/reflect/scala/reflect/internal/util/SourceFile.scala#L71 - som-snytt
@som-snytt:你是否知道它是否有文档记录? - Keith Thompson

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