我正在阅读 RBENV 版本管理器的源代码,并且在命令的规范文件中遇到了此测试exec:
@test "supports ruby -S <cmd>" {
export RBENV_VERSION="2.0"
# emulate `ruby -S' behavior
create_executable "ruby" <<SH
#!$BASH
if [[ \$1 == "-S"* ]]; then
found="\$(PATH="\${RUBYPATH:-\$PATH}" which \$2)"
# assert that the found executable has ruby for shebang
if head -n1 "\$found" | grep ruby >/dev/null; then
\$BASH "\$found"
else
echo "ruby: no Ruby script found in input (LoadError)" >&2
exit 1
fi
else
echo 'ruby 2.0 (rbenv test)'
fi
SH
create_executable "rake" <<SH
#!/usr/bin/env ruby
echo hello rake
SH
rbenv-rehash
run ruby -S rake
assert_success "hello rake"
}
Run Code Online (Sandbox Code Playgroud)
这里已经有一个单独的测试,确保传递的所有参数都rbenv exec被转发。由于 的工作rbenv exec只是确保在将任何和所有参数转发到最初调用的命令之前使用正确的 Ruby 版本,所以我很困惑为什么需要专门针对该-S标志进行单独的测试。
我查找了引入测试的提交,但没有任何额外的信息解释为什么需要这个测试。
我查找了manRuby 的条目,然后滚动到标志部分-S。它包含以下信息:
-S Makes Ruby use the PATH environment variable to search for script, unless its name begins with a slash. This is used to emulate #! on
machines that don't support it, in the following manner:
#! /usr/local/bin/ruby
# This line makes the next one a comment in Ruby \
exec /usr/local/bin/ruby -S $0 $*
On some systems $0 does not always contain the full pathname, so you need the -S switch to tell Ruby to search for the script if necessary
(to handle embedded spaces and such). A better construct than $* would be ${1+"$@"}, but it does not work if the script is being
interpreted by csh(1).
Run Code Online (Sandbox Code Playgroud)
也许是man该条目的措辞让我困惑,但读完上面的内容后我并没有比之前更清楚。是否有一个简洁的“像我 5 岁一样解释”示例,说明使用 Ruby 时可能遇到的问题,只有该-S标志才能解决?
特别是有几点我不太清楚:
描述的第一句话man是“让 Ruby 使用 PATH 环境变量来搜索脚本,除非其名称以斜杠开头”。但script他们在这里指的是哪些 - 我想使用命令执行的脚本ruby,还是ruby命令脚本本身?
我(再次从man条目中)看到“这用于在不支持它的机器上模拟 #!”,但我不明白前者(即使用-S)如何导致后者(即所描述的模拟#!)在man条目中)。
通常,当您ruby foo.rb在 shell 中调用时,会搜索$PATH(或,在 Windows 上等效地),并调用第一个找到的 (或,或在 Windows 上)。但是,Ruby 随后仅调用在当前目录中找到的内容。也会指示 Ruby 搜索路径。%PATH%rubyfoo.exefoo.comfoo.rbruby -S foo.rbfoo.rb
“以下方式”解释了这一点。当您执行foo.rb(而不是ruby foo.rb)时,bash(和许多其他 shell)将在路径中查找它,然后尝试执行它。为此,它首先测试它是否是二进制可执行文件。如果不是,它会测试是否有“shebang line”(即第一行是否以 开头#!);如果是,则执行该指令。当你在#!/usr/local/bin/env ruby的开头时foo.rb,它会执行/usr/local/bin/env ruby foo.rb,它又会在路径中查找,然后在当前目录中ruby执行。foo.rb
但是,如果 shell 不支持 shebang,则联机帮助页中的代码片段会显示一种解决方法,假设 shell 自动将文件作为 shell 脚本执行,并假设该脚本位于路径中。对于 shell 来说,前两行是注释。就 Ruby 而言,前三行是注释(因为反斜杠会转义换行符,使第三行成为第二行的延续)。因此,shell 将执行第三行,该行调用 Ruby 并告诉它运行当前脚本 ( $0);但是,由于某些 shell 可能无法正确识别脚本的位置,因此-S让 Ruby 在路径上搜索它。