如何获得在Ruby中使用提示的命令名称?


83

不久前,我写了一个我很喜欢的漂亮的Ruby小脚本。我想通过检查适当数量的参数来提高其健壮性:

if ARGV.length != 2 then
  puts "Usage: <command> arg1 arg2"
end

当然那是伪代码。无论如何,在C或C ++中,我可以使用它argv[0]来获取用户用来获取我的命令的名称,无论他们将其称为like./myScript.rb还是myScript.rbor /usr/local/bin/myScript.rb。在Ruby中,我知道这ARGV[0]是第一个true参数,并且ARGV不包含命令名称。有什么办法可以让我得到这个吗?

Answers:


149

Ruby通过三种方式为我们提供被调用脚本的名称:

#!/usr/bin/env ruby

puts "$0            : #{$0}"
puts "__FILE__      : #{__FILE__}"
puts "$PROGRAM_NAME : #{$PROGRAM_NAME}"

将代码另存为“ test.rb”并进行多种调用,表明该脚本接收到操作系统传递给它的名称。脚本仅知道操作系统告诉它的内容:

$ ./test.rb 
$0            : ./test.rb
__FILE__      : ./test.rb
$PROGRAM_NAME : ./test.rb

$ ~/Desktop/test.rb 
$0            : /Users/ttm/Desktop/test.rb
__FILE__      : /Users/ttm/Desktop/test.rb
$PROGRAM_NAME : /Users/ttm/Desktop/test.rb

$ /Users/ttm/Desktop/test.rb 
$0            : /Users/ttm/Desktop/test.rb
__FILE__      : /Users/ttm/Desktop/test.rb
$PROGRAM_NAME : /Users/ttm/Desktop/test.rb

~在第二个示例中使用$ HOME的快捷方式调用它,将显示操作系统用扩展路径替换它,与第三个示例中的匹配。在所有情况下,这都是操作系统传入的内容。

使用硬链接和软链接链接到文件均显示一致的行为。我为test1.rb创建了一个硬链接,为test2.rb创建了一个软链接:

$ ./test1.rb 
$0            : ./test1.rb
__FILE__      : ./test1.rb
$PROGRAM_NAME : ./test1.rb

$ ./test2.rb 
$0            : ./test2.rb
__FILE__      : ./test2.rb
$PROGRAM_NAME : ./test2.rb

ruby test.rb使用脚本名称的任何变体启动将返回一致的结果。

如果只需要被调用的文件名,则可以将File的basename方法与变量之一一起使用,或者在定界符上拆分并采用最后一个元素。

$0__FILE__有一些细微的差异,但对于单个脚本,它们是等效的。

puts File.basename($0)

有使用一些好处File.basenameFile.extnameFile.dirname方法的套件。basename接受一个可选参数,这是剥离的扩展名,因此,如果您只需要基本名而没有扩展名

File.basename($0, File.extname($0)) 

这样做不需要重新设计轮子,也不必处理可变长度或缺少扩展名,或者可能会错误地截断扩展链“ .rb.txt”的可能性,例如:

ruby-1.9.2-p136 :004 > filename = '/path/to/file/name.ext'
 => "/path/to/file/name.ext" 
ruby-1.9.2-p136 :005 > File.basename(filename, File.extname(filename))
 => "name" 
ruby-1.9.2-p136 :006 > filename = '/path/to/file/name.ext' << '.txt'
 => "/path/to/file/name.ext.txt" 
ruby-1.9.2-p136 :007 > File.basename(filename, File.extname(filename))
 => "name.ext" 

非常感谢您提供完整的答案!
adam_0 2011年

2
请注意,如果您在包含的脚本中使用'require'或'require_relative,使用建议的选项,则会有不同的行为。使用$ 0和$ PROGRAM_NAME返回调用脚本的名称。将FILE选项与周围的双下划线一起使用(如何在注释中设置其格式?)将返回所包含脚本的名称。
mike663

18

这个答案可能会晚一点,但是我遇到了同样的问题,被接受的答案对我来说似乎并不令人满意,因此我进行了进一步的调查。

困扰我的是这样一个事实,$0$PROGRAM_NAME是否真正掌握了有关用户键入内容的正确信息。如果我的Ruby脚本位于PATH文件夹中,并且用户输入了可执行文件名称(没有任何路径定义,例如./script/bin/script),它将始终扩展为总路径。

我以为这是Ruby的不足,所以我在Python上尝试了同样的方法,但令我感到恼火的是,两者没有什么不同。

一位朋友建议我用骇客寻找real thingin /proc/self/cmdline,结果为:([ruby, /home/danyel/bin/myscript, arg1, arg2...]由null-char分隔)。此处的反派execve(1)将路径传递给解释器时,会将路径扩展为总路径。

示例C程序:

#include <stdlib.h>
#include <unistd.h>

extern char** environ;
int main() {
  char ** arr = malloc(10 * sizeof(char*));
  arr[0] = "myscript";
  arr[1] = "-h";
  arr[2] = NULL;
  execve("/home/danyel/bin/myscript", arr, environ);
}

输出:ʻUsage:/ home / danyel / bin / myscript FILE ...

为了证明这确实是execve一件事情,而不是bash的事情,我们可以创建一个虚拟解释器,该伪解释器只打印输出给它的参数:

// interpreter.c
int main(int argc, const char ** argv) {
  while(*argv)
    printf("%s\n", *(argv++));
}

我们将其编译并放在路径文件夹中(或将完整路径放在shebang之后),然后在其中创建一个虚拟脚本 ~/bin/myscript/

#!/usr/bin/env interpreter
Hi there!

现在,在我们的main.c中:

#include <stdlib.h>

extern char** environ;
int main() {
  char ** arr = malloc(10 * sizeof(char*));
  arr[0] = "This will be totally ignored by execve.";
  arr[1] = "-v";
  arr[2] = "/var/log/apache2.log";
  arr[3] = NULL;
  execve("/home/danyel/bin/myscript", arr, environ);
}

编译并运行./main:解释器/ home / danyel / bin / myscript -v /var/log/apache2.log

造成这种情况的原因很可能是,如果脚本位于您的PATH中并且提供完整路径,则解释程序会将此视为No such file错误,如果您这样做,则会这样做:ruby myrubyscript --options arg1并且您不在该脚本的文件夹中。


3

使用$0$PROGRAM_NAME获取当前正在执行的文件名。


这给了我完整的路径。我想知道用户输入了什么。例如,如果我有/usr/local/bin/myScript/usr/local/bin并且在中$PATH,而我只是输入myScript,我/usr/local/bin/myScript将从$0
adam_0'1

2
怎么$0.split("/").last
pierrotlefou 2011年

7
我并不是在索要程序名,而是要我准确地输入用户输入的程序名称。如果他们输入了./myScript,我想要一个给我的变量./myScript。如果他们输入/usr/bin/local/myScript,我就是想要的。等
adam_0 2011年

3

这并不是您所提问题的答案,但听起来您正在重新发明轮子。查看optparse库。它使您可以定义命令行开关,参数等,并为您完成所有繁重的工作。


2
谢谢,但是我不需要任何复杂的事情。我的意思是,我只有两个参数,所以要保证所有这些都太简单了。
adam_0 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.