英文:
What is the "-S" flag used for in Ruby?
问题
I'm reading through the source code for the RBENV version manager, and I encountered this test in the spec file for the exec
command:
@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"
}
There's already a separate test here which ensures that all arguments passed to rbenv exec
are forwarded. And since the job of rbenv exec
is just to ensure the correct Ruby version is used before forwarding any and all args to the originally-invoked command, I was confused why a separate test would be needed specifically for the -S
flag.
I looked up the commit which introduced the test, but there wasn't any extra information explaining why this test was needed.
I looked up the man
entry for Ruby and scrolled to the section on the -S
flag. It contained the following info:
-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).
Maybe it's the wording of the man
entry that is tripping me up, but I'm no more clear after reading the above than I was beforehand. Is there a concise, "Explain Like I'm 5" example of a problem that might be encountered while using Ruby, which only the -S
flag could solve?
A few things in particular that I'm unclear on:
-
The first sentence of the
man
description says "Makes Ruby use the PATH environment variable to search for script, unless its name begins with a slash." But whichscript
are they referring to here- the script that I want to execute using theruby
command, or theruby
command script itself? -
I see (again from the
man
entry) that "This is used to emulate #! on machines that don't support it", but I don't understand how the former (i.e. using-S
) leads to the latter (i.e. the emulation of#!
described in theman
entry).
英文:
I'm reading through the source code for the RBENV version manager, and I encountered this test in the spec file for the exec
command:
@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"
}
There's already a separate test here which ensures that all arguments passed to rbenv exec
are forwarded. And since the job of rbenv exec
is just to ensure the correct Ruby version is used before forwarding any and all args to the originally-invoked command, I was confused why a separate test would be needed specifically for the -S
flag.
I looked up the commit which introduced the test, but there wasn't any extra information explaining why this test was needed.
I looked up the man
entry for Ruby and scrolled to the section on the -S
flag. It contained the following info:
-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).
Maybe it's the wording of the man
entry that is tripping me up, but I'm no more clear after reading the above than I was beforehand. Is there a concise, "Explain Like I'm 5" example of a problem that might be encountered while using Ruby, which only the -S
flag could solve?
A few things in particular that I'm unclear on:
-
The first sentence of the
man
description says "Makes Ruby use the PATH environment variable to search for script, unless its name begins with a slash." But whichscript
are they referring to here- the script that I want to execute using theruby
command, or theruby
command script itself? -
I see (again from the
man
entry) that "This is used to emulate #! on machines that don't support it", but I don't understand how the former (i.e. using-S
) leads to the latter (i.e. the emulation of#!
described in theman
entry).
答案1
得分: 6
-
通常,在shell中调用
ruby foo.rb
时,会搜索$PATH
(或者在Windows上等效的%PATH%
),并调用找到的第一个ruby
(或者在Windows上是foo.exe
或foo.com
)。然而,Ruby 接着只会调用当前目录中找到的foo.rb
。ruby -S foo.rb
会指示Ruby在路径中搜索foo.rb
。 -
"以下方式" 解释了这个问题。当你只执行
foo.rb
(而不是ruby foo.rb
)时,bash(以及许多其他shell)会在路径中查找它,然后尝试执行它。为了执行它,首先测试它是否是一个二进制可执行文件。如果不是,它会测试它是否具有 "shebang 行"(即第一行是否以#!
开头);如果有,它会执行该指令。当你在foo.rb
的开头有#!/usr/local/bin/env ruby
时,它会执行/usr/local/bin/env ruby foo.rb
,然后在路径中找到ruby
,它会执行当前目录中的foo.rb
。
但是,如果一个shell不支持shebang,manpage中的代码片段显示了一种解决方法,假设shell会自动将文件视为shell脚本并假设该脚本在路径中。就shell而言,前两行是注释。对于Ruby而言,前三行是注释(因为反斜杠会转义换行符,使第三行成为第二行的延续)。shell因此会执行第三行,该行调用Ruby并告诉它运行当前脚本($0
);但是,由于某些shell可能无法正确识别脚本的位置,-S
让Ruby在路径中搜索它。
英文:
-
Normally, when you invoke
ruby foo.rb
in a shell, the$PATH
(or, equivalently,%PATH%
on Windows) is searched, and the firstruby
found (orfoo.exe
, orfoo.com
on Windows) is invoked. However, Ruby then only invokesfoo.rb
found in the current directory.ruby -S foo.rb
will instruct Ruby to search the path forfoo.rb
as well. -
The "following manner" explains it. When you do just
foo.rb
(notruby foo.rb
), bash (and many other shells) will look find it in path, then try to execute it. To do this, it first tests if it is a binary executable. If it is not, it tests if it has the "shebang line" (i.e. if the first line starts with#!
); if it does, it executes that instruction. When you have#!/usr/local/bin/env ruby
at the start offoo.rb
, it executes/usr/local/bin/env ruby foo.rb
, which in turn findsruby
in path, which executes thefoo.rb
in the current directory.However, if a shell does not support the shebang, the code snippet in the manpage shows a workaround, assuming the shell automatically executes the file as a shell script, and assuming that the script is in path. As far as the shell is concerned, the first two lines are comments. As far as Ruby is concerned, the first three lines are comments (because the backslash will escape the newline, making the third line a continuation of the second). The shell will thus execute the third line, which calls Ruby and tells it to run the current script (
$0
); however, because some shells might not correctly identify where the script is,-S
lets Ruby search for it on the path.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论