web-dev-qa-db-ja.com

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にはコマンド名が含まれていないことを知っています。これを取得する方法はありますか?

75
adam_0

Rubyには、呼び出されたスクリプトの名前を与える3つの方法があります。

#!/usr/bin/env Ruby

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

そのコードを「test.rb」として保存し、いくつかの方法で呼び出すと、スクリプトがOSから渡された名前を受け取ることがわかります。スクリプトは、OSがそれを伝えることのみを知っています。

$ ./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

2番目の例で$ HOMEの~ショートカットを使用して呼び出すと、OSが3番目の例にあるものと一致する拡張パスに置き換えます。すべての場合において、OSが渡したものです。

ハードリンクとソフトリンクの両方を使用してファイルにリンクすると、一貫した動作が示されます。 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メソッドを変数の1つと共に使用するか、区切り文字で分割して最後の要素を取得できます。

$0__FILE__には若干の違いがありますが、単一のスクリプトの場合は同等です。

puts File.basename($0)

マイナーな追加:

メソッドのFile.basenameFile.extnameおよびFile.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" 
140
the Tin Man

この答えは少し遅れる可能性がありますが、私は同じ問題を抱えており、受け入れられた答えは私にはまったく満足のいくものではなかったので、もう少し調査しました。

気になったのは、_$0_または_$PROGRAM_NAME_がユーザーの持っているものに関する正しい情報を実際には保持していなかったという事実でしたtyped。 my RubyスクリプトがPATHフォルダーにあり、ユーザーが実行可能ファイル名(_./script_や_/bin/script_などのパス定義なし)を入力した場合、常に展開されます。合計パス。

これはRuby赤字だと思ったので、同じことをPythonで試してみました。

友人から_real thing_で_/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);
}
_

出力: `使用法:/ home/danyel/bin/myscript FILE ...

これが実際にexecveであってbashのものではないことを証明するために、渡された引数を出力するだけで何もしないダミーインタープリターを作成できます。

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

それをコンパイルしてパスフォルダーに入れ(またはフルパスをシェバンの後に置きます)、ダミースクリプトを_~/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にあり、フルパスがnotである場合、インタープリターはこれを_No such file_エラーとして認識するためです。 :_Ruby myrubyscript --options arg1_で、そのスクリプトのあるフォルダーにいない。

16
Danyel

使用する $0or $PROGRAM_NAME現在実行中のファイル名を取得します。

3
pierrotlefou

これはあなたの質問に対する答えではありませんが、車輪を再発明しているようです。 optparse ライブラリを見てください。コマンドラインスイッチ、引数などを定義でき、すべての面倒な作業を行います。

2
Chris Heald