“-S”标志在Ruby中用于什么?

huangapple go评论60阅读模式
英文:

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:

  1. 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 which script are they referring to here- the script that I want to execute using the ruby command, or the ruby command script itself?

  2. 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 the man 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 &quot;supports ruby -S &lt;cmd&gt;&quot; {
  export RBENV_VERSION=&quot;2.0&quot;

  # emulate `ruby -S&#39; behavior
  create_executable &quot;ruby&quot; &lt;&lt;SH
#!$BASH
if [[ \$1 == &quot;-S&quot;* ]]; then
  found=&quot;$(PATH=&quot;${RUBYPATH:-$PATH}&quot; which \$2)&quot;
  # assert that the found executable has ruby for shebang
  if head -n1 &quot;$found&quot; | grep ruby &gt;/dev/null; then
    $BASH &quot;$found&quot;
  else
    echo &quot;ruby: no Ruby script found in input (LoadError)&quot; &gt;&amp;2
    exit 1
  fi
else
  echo &#39;ruby 2.0 (rbenv test)&#39;
fi
SH

  create_executable &quot;rake&quot; &lt;&lt;SH
#!/usr/bin/env ruby
echo hello rake
SH

  rbenv-rehash
  run ruby -S rake
  assert_success &quot;hello rake&quot;
}

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&#39;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+&quot;$@&quot;}, 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:

  1. 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 which script are they referring to here- the script that I want to execute using the ruby command, or the ruby command script itself?

  2. 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 the man entry).

答案1

得分: 6

  1. 通常,在shell中调用 ruby foo.rb 时,会搜索 $PATH(或者在Windows上等效的 %PATH%),并调用找到的第一个 ruby(或者在Windows上是 foo.exefoo.com)。然而,Ruby 接着只会调用当前目录中找到的 foo.rbruby -S foo.rb 会指示Ruby在路径中搜索 foo.rb

  2. "以下方式" 解释了这个问题。当你只执行 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在路径中搜索它。

英文:
  1. Normally, when you invoke ruby foo.rb in a shell, the $PATH (or, equivalently, %PATH% on Windows) is searched, and the first ruby found (or foo.exe, or foo.com on Windows) is invoked. However, Ruby then only invokes foo.rb found in the current directory. ruby -S foo.rb will instruct Ruby to search the path for foo.rb as well.

  2. The "following manner" explains it. When you do just foo.rb (not ruby 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 of foo.rb, it executes /usr/local/bin/env ruby foo.rb, which in turn finds ruby in path, which executes the foo.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.

huangapple
  • 本文由 发表于 2023年5月30日 02:13:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/76359523.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定