如何获取在Ruby中使用命令的名称?
我写了一段很好的Ruby脚本,我很喜欢。 我想通过检查适当数量的参数来提高其健壮性:
if ARGV.length != 2 then puts "Usage: <command> arg1 arg2" end
当然这是伪代码。 无论如何,在C或C ++中,我可以使用argv[0]
获取用户用来访问我的命令的名称,无论他们是否将其称为./myScript.rb
或myScript.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.basename
, File.extname
和File.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库。 它可以让你定义命令行开关,参数等,它会为你做所有的繁重工作。