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에는 호출 된 스크립트의 이름을 제공하는 세 가지 방법이 있습니다.

#!/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

~두 번째 예에서 $ HOME 의 바로 가기를 사용하여 호출 하면 OS가 세 번째 예와 일치하는 확장 된 경로로 대체되는 것을 보여줍니다. 모든 경우에 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변수 중 하나와 함께 File의 메서드를 사용 하거나 구분 기호로 분할하고 마지막 요소를 사용할 수 있습니다.

$0그리고 __FILE__약간의 차이가 있지만 하나의 스크립트 그들은 동등한 것.

puts File.basename($0)

File.basename, File.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

2
포함 된 스크립트에서 'require'또는 'require_relative를 사용하여 제안 된 옵션을 사용하는 경우 다른 동작이 있습니다. $ 0 및 $ PROGRAM_NAME을 사용하면 호출 스크립트의 이름이 반환됩니다. 이중 밑줄을 둘러싼 FILE 옵션을 사용하면 (주석에서 형식을 어떻게 지정합니까?) 포함 된 스크립트의 이름을 반환합니다.
mike663

18

이 답변은 조금 늦었을지 모르지만 같은 문제가 있었고 받아 들여진 답변이 나에게 그다지 만족스럽지 않아서 조금 더 조사했습니다.

무엇 나를 성가 시게하는 것은 사실이었다 $0$PROGRAM_NAME정말 사용자가 있었는지에 대한 정확한 정보를 보유하지 않은 입력을 . 내 Ruby 스크립트가 PATH 폴더에 있고 사용자가 실행 파일 이름 ( ./script또는 같은 경로 정의없이)을 입력하면 /bin/script항상 전체 경로로 확장됩니다.

나는 이것이 루비가 부족하다고 생각했기 때문에 파이썬으로 똑같이 시도했고 거기에서 내 억울함에도 다르지 않았습니다.

친구가 나에게 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);
}

출력 :`사용법 : / home / danyel / bin / myscript FILE ...

이것이 실제로 execvebash가 아니라 사실임을 증명하기 위해 전달 된 인수를 출력하는 것 외에는 아무것도하지 않는 더미 인터프리터를 만들 수 있습니다.

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

2
어때요 $0.split("/").last?
pierrotlefou 2011 년

7
프로그램 이름 만 묻는 것이 아니라 사용자가 프로그램을 실행하기 위해 입력 한 내용을 정확히 원한다는 의미입니다. 그들이 입력했다면 ./myScript, 나는 나에게 제공하는 변수를 원합니다 ./myScript. 그들이 타자했다면 /usr/bin/local/myScript정확히 그것을 원합니다. 등
adam_0

3

이것은 귀하의 질문에 대한 답은 아니지만 바퀴를 재발 명하는 것 같습니다. 상기 봐 optparse의 라이브러리입니다. 명령 줄 스위치, 인수 등을 정의 할 수 있으며 모든 무거운 작업을 수행합니다.


2
고마워요하지만 그렇게 복잡한 건 필요 없어요 내 말은, 나는 단지 2 개의 주장 만 가지고 있기 때문에 모든 것을 보증하기에는 너무 간단한 경우입니다.
adam_0
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.