如何获取在Ruby中使用命令的名称?

我写了一段很好的Ruby脚本,我很喜欢。 我想通过检查适当数量的参数来提高其健壮性:

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

当然这是伪代码。 无论如何,在C或C ++中,我可以使用argv[0]获取用户用来访问我的命令的名称,无论他们是否将其称为./myScript.rbmyScript.rb/usr/local/bin/myScript.rb 在Ruby中,我知道ARGV[0]是第一个真实的参数,而ARGV不包含命令名。 有什么方法可以得到这个?

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的~快捷方式调用它,显示操作系统用扩展pathreplace它,与第三个例子中的内容匹配。 在所有情况下,这是操作系统传入的内容。

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

 $ ./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将返回一致的结果。

如果你只想要被调用的文件名,你可以使用文件的basename方法和其中一个variables,或者在分隔符上进行分割,并取最后一个元素。

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

 puts File.basename($0) 

小增加:

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

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

没有重新发明轮子或不得不处理可变长度或缺less的扩展或不正确地截断扩展链“ .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" 

这个答案可能会迟一点,但是我也有同样的问题,我接受的答案似乎并不令人满意,所以我进一步调查了一下。

困扰我的是$0$PROGRAM_NAME没有真正掌握用户input内容的正确信息。 如果我的Ruby脚本在PATH文件夹中,并且用户input了可执行文件名(没有任何path定义,如./script/bin/script ),它总是会扩展到总path。

我认为这是一个ruby赤字,所以我尝试了与Python相同,在那里我的懊恼,这是没有什么不同。

一个朋友build议我在/proc/self/cmdline寻找real thing ,结果是: [ruby, /home/danyel/bin/myscript, arg1, arg2...] (由null-字符)。 这里的恶棍是execve(1) ,当它传递给翻译的时候,它扩展了通往总path的path。

示例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); } 

输出:`用法:/ home / danyel / bin / myscript FILE …

为了certificate这确实是一个execve事情,而不是从bash中,我们可以创build一个虚拟解释器,它什么也不做,只是打印传递给它的参数:

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

我们编译它并把它放在一个path文件夹(或放在shebang之后的完整path),并在~/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中,并且没有提供完整的path,那么解释器会认为这是一个No such file错误,如果你这样做: ruby myrubyscript --options arg1 and you '不在该脚本的文件夹中。

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

这不是你的问题的答案,但这听起来像是在重新发明轮子。 看看optparse库。 它可以让你定义命令行开关,参数等,它会为你做所有的繁重工作。