在Ruby中,`if __FILE__ == $0`是什么意思?

58
if __FILE__ == $0
  $:.unshift File.join(File.dirname(__FILE__),'..')

我在 Ruby 中发现了这段代码,它的含义是什么?


FYI,rubocop 希望使用 $PROGRAM_NAME 而不是 $0。在我看来这很糟糕。 - akostadinov
6个回答

61
# Only run the following code when this file is the main file being run
# instead of having been required or loaded by another file
if __FILE__==$0
  # Find the parent directory of this file and add it to the front
  # of the list of locations to look in when using require
  $:.unshift File.expand_path("../../", __FILE__)  
end

需要注意的是,随着require_relative的出现,通常不需要采用将目录添加到LOAD_PATH的做法。而expand_path则是一种更简单的查找父目录的方法。


15
非常好的回答,我建议注明常量的含义。 __FILE__ :当前文件名,$0 :被运行的主文件,$: :LOAD_PATH。 - Nemo157

37

这是一些Ruby小型设计模式

它的意思是:

  • 如果文件直接被执行,即作为脚本,
  • 那么查找脚本所在的目录并将其添加到加载/需要搜索路径的开头。

条件部分可以用于许多事情。Ruby文件可以编写为为大型程序提供功能,但也可用作脚本(带有额外代码)。或者,脚本形式可以运行模块的测试,而组件形式实现一些类来为更大的程序调用测试,前提是名称引用已经存在。

路径添加具有一些通用用途,除了查找私有包含项的明显用途之外。如果您有几个不同版本或发行版的软件,则可以使用此方法优先调用任何“可执行文件”(脚本)。 /usr/local/release232/bin/whatever将从相同的bin目录运行所有内容,即使release220在默认路径上较早。


惊人的答案...对于那些好奇一些神秘变量的文档在哪里的人:https://ruby-doc.org/core-2.1.1/doc/globals_rdoc.html - Joseph Cho

18

来自ruby-lang.org的优秀解释:

__FILE__是一个特殊变量,它包含当前文件的名称。$0是启动程序所使用的文件名。 这个检查表示“如果正在使用的是主文件…” 这允许将文件用作库,而不在该上下文中执行代码,但如果该文件被用作可执行文件,则执行该代码。

参见:https://www.ruby-lang.org/en/documentation/quickstart/4/


14

像其他编程语言一样,Ruby也有一些保留关键字,包括ifreturnclass;其中之一是__FILE__。Ruby使用__FILE__来保存当前源文件的名称。

变量名前面加上$表示全局变量。在这种情况下,$0包含正在执行的脚本的名称。例如,如果你运行ruby hello_world.rb,那么$0的值将是hello_world.rb

if __FILE__ == $0
  ...
end

现在,仅当包含源代码的文件与您正在执行的文件相同时,if语句内的代码才会被执行。为了更好的解释,让我们创建2个文件。

第一个文件 hello_world.rb:

# hello_world

puts "Hello world"

还有第二个hello_pluto.rb文件:

# hello_pluto

require_relative "hello_world"
puts "Hello Pluto"

如果你运行 ruby hello_world.rb,它会简单地输出:

Hello world

现在如果你运行ruby hello_pluto.rb,它会输出:

Hello world
Hello Pluto

它实际上也运行了hello_world文件。现在将hello_world的代码放在if语句内部,然后再次运行hello_pluto:

# hello_world

if __FILE__ == $0
  puts "Hello world"
end

正如预期的那样,唯一被打印出来的东西是Hello Pluto。因此,该if语句保证了任何位于该语句内部的代码只有在源文件与您正在运行的脚本相同时才会运行。

参考:http://periclestheo.com/2014/02/what-is-up-with-FILE-and-$0.html


11
我建议您使用$PROGRAM_NAME作为$0的别名,因为当您使用它时,它会更清晰地表达您的意思——它们的含义相同。

那么,$PROGRAM_NAME/$0是什么意思呢? 请参考Ruby文档

$0
包含正在执行的脚本的名称。可以被分配。

让我们通过代码演示一下它是如何工作的。

1. 创建hello.rb

module Hello
  def self.world
    puts "Hello, world!"
    puts "$0       = #{$0}"
    puts "__FILE__ = #{__FILE__}"
  end
end

if $PROGRAM_NAME == __FILE__
  puts Hello.world
end

看看如果你单独运行这个文件会发生什么:

$ ruby hello.rb
Hello, world!
$0       = hello.rb
__FILE__ = hello.rb

2. 创建一个Rakefile

require 'rake'
require_relative 'hello'

task :hello do
  Hello.world
end

现在,从Rake任务的上下文中执行相同的代码:
$ rake hello
Hello, world!
$0       = /Users/sean/.rbenv/versions/2.3.0/bin/rake
__FILE__ = /Users/sean/Projects/hello/hello.rb

在Ruby文件的结尾处用if $PROGRAM_NAME == __FILE__条件语句包装代码是许多开发人员常用的习惯用法,意为“仅在单独运行此文件时才执行此代码”。
你会在这个条件语句中包装哪种类型的代码?我经常使用它来演示如何使用模块。这也非常方便,以便在上下文之外执行此文件,以确保它可以在您编写的更大程序的上下文之外正常工作。
如果您没有将示例代码包装在此条件语句中,则该代码将在您的更大程序的生命周期中每次需要/加载文件时执行,这几乎肯定不是您想要的结果。

$PROGRAM_NAME 的文档在哪里可以找到? - ShadSterling

5
这意味着如果直接运行文件而非通过require或加载,则将脚本父目录添加到$LOAD_PATH中(即ruby搜索所需文件的路径)。

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