我正在阅读RBENV版本管理器的源代码,在exec
命令的spec文件中遇到了以下测试:
@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"
}
这里已经有一个单独的测试,它确保传递给rbenv exec
的所有参数都被转发。由于rbenv exec
的工作只是确保在将任何和所有参数转发到最初调用的命令之前使用正确的Ruby版本,所以我很困惑为什么需要专门针对-S
标志进行单独的测试。
我查找了the commit which introduced the test,但是没有任何额外的信息来解释为什么需要这个测试。
我查找了Ruby的man
条目,并滚动到-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).
也许是man
条目的措辞绊倒了我,但我在阅读上述内容后并没有比之前更清楚。有没有一个简洁的例子,“像我5岁一样解释”,说明在使用Ruby时可能遇到的问题,只有-S
标志才能解决?
有几件事我不太清楚:
man
描述的第一句话是“使Ruby使用PATH环境变量来搜索脚本,除非它的名称以斜杠开头。”但是他们在这里指的是哪个script
-我想使用ruby
命令执行的脚本,还是ruby
命令脚本本身?
1.我看到(再次从man
条目)“这是用来模拟#!在不支持它的机器上”,但我不明白前者(即使用-S
)导致后者(即,man
条目中描述的#!
仿真)。
1条答案
按热度按时间nue99wik1#
1.通常,当您在shell中调用
ruby foo.rb
时,会搜索$PATH
(或者,在Windows上相当于%PATH%
),并调用找到的第一个ruby
(或者在Windows上是foo.exe
或foo.com
)。但是,Ruby只调用当前目录中的foo.rb
。ruby -S foo.rb
也会指示Ruby搜索foo.rb
的路径。1.“下面的方式”说明了这一点。当你只执行
foo.rb
(而不是ruby foo.rb
)时,bash(和许多其他shell)会在path中查找它,然后尝试执行它。为此,它首先测试它是否是二进制可执行文件。如果不是,则测试其是否具有“shebang线”(即,如果第一行以#!
开始);如果是,则执行该指令。当你在foo.rb
的开头有#!/usr/local/bin/env ruby
时,它会执行/usr/local/bin/env ruby foo.rb
,然后在path中找到ruby
,再执行当前目录 * 中的foo.rb
*。但是,如果shell不支持shebang,则手册页中的代码片段显示了一个解决方案,假设shell自动将文件作为shell脚本执行,并假设脚本在路径中。就shell而言,前两行是注解。就Ruby而言,前三行是注解(因为反斜杠将转义换行符,使第三行成为第二行的延续)。因此,shell将执行第三行,这将调用Ruby并告诉它运行当前脚本(
$0
);但是,由于某些shell可能无法正确识别脚本的位置,-S
允许Ruby在路径上搜索它。