Rubyで使用プロンプト用に呼び出されたコマンドの名前を取得するにはどうすればよいですか?


83

私はしばらく前に、私がかなり好きな素敵な小さな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、コマンド名が含まれていないことを私は知っています。これを入手する方法はありますか?

回答:


149

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スクリプト名のバリエーションのいずれかを使用して起動すると、一貫した結果が返されます。

呼び出されたファイル名のみが必要な場合basenameは、変数の1つでFileのメソッドを使用するか、区切り文字で分割して最後の要素を取得できます。

$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" 

非常に完全な答えをありがとう!
adam_0 2011年

2
'require'または 'require_relativeを使用して、含まれているスクリプトで提案されたオプションを使用すると、動作が異なることに注意してください。$ 0と$ PROGRAM_NAMEを使用すると、呼び出し元のスクリプトの名前が返されます。周囲の二重アンダーバーでFILEオプションを使用すると(コメントでどのようにフォーマットしますか?)、含まれているスクリプトの名前が返されます。
mike663 2018

18

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

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

これはRubyの不足だと思ったので、Pythonでも同じことを試してみましたが、残念ながら違いはありませんでした。

友人が私にreal thinginを探すためのハックを提案/proc/self/cmdlineし、結果は次のようになりました:([ruby, /home/danyel/bin/myscript, arg1, arg2...]ヌル文字で区切られています)。ここでの悪役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 .. ..

これが実際にexecvebashからのものではなく、物であることを証明するために、渡された引数を出力するだけのダミーインタープリターを作成できます。

// 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にあり、フルパスが指定されていない場合、インタープリターがこれをNo such fileエラーとして認識するためです。エラーが発生した場合は、次のように認識されruby myrubyscript --options arg1ます。そのスクリプトのフォルダーにいない場合。


3

$0または$PROGRAM_NAMEを使用して、現在実行されているファイル名を取得します。


これは私に完全な道を与えます。ユーザーが何を入力したのか知りたいのですが。例えば、私が持っている場合/usr/local/bin/myScript/usr/local/bin、私の中にある$PATH、と私はちょうど入力しmyScript、I GET/usr/local/bin/myScriptから$0
adam_0

2
どう$0.split("/").lastですか?
pierrotlefou 2011年

7
私はプログラム名だけを求めているのではありません。つまり、ユーザーが入力した内容を正確に入力してプログラムを実行したいということです。彼らがタイプした場合./myScript、私は私に与える変数が欲しいです./myScript。彼らがタイプした場合/usr/bin/local/myScript、私はまさにそれが欲しいです。など
adam_0 2011年

3

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


2
ありがとう、でもそんなに複雑なものは必要ありません。つまり、私には2つの引数しかないので、それをすべて正当化するには単純すぎるケースです。
adam_0 2011年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.