development

Ruby에서 사용 프롬프트를 위해 호출되는 명령의 이름을 어떻게 얻을 수 있습니까?

big-blog 2020. 10. 16. 07:36
반응형

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"로 저장하고 몇 가지 방법으로 호출하면 스크립트가 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" 

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

What bothered me was the fact that $0 or $PROGRAM_NAME did not really hold the correct information about what the user had typed. If my Ruby script was in a PATH folder and the user entered the executable name (without any path definitions such as ./script or /bin/script), it would always expand to the total path.

I thought this was a Ruby deficite, so I tried the same with Python and to my chagrin there, it was no different.

A friend suggested me a hack to look for the real thing in /proc/self/cmdline, and the result was: [ruby, /home/danyel/bin/myscript, arg1, arg2...] (separated by the null-char). The villain here is execve(1) which expands the path to the total path when it passes it to an interpreter.

Example C program:

#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);
}

Output: `Usage: /home/danyel/bin/myscript FILE...

To prove that this is indeed a execve thing and not from bash, we can create a dummy interpreter that does nothing but print out the arguments passed to it:

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

We compile it and put it in a path folder (or put the full path after the shebang) and create a dummy script in ~/bin/myscript/

#!/usr/bin/env interpreter
Hi there!

Now, in our 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);
}

Compiling and running ./main: interpreter /home/danyel/bin/myscript -v /var/log/apache2.log

The reason behind this most likely is that if the script is in your PATH and the full path were not provided, the interpreter would recognize this as a No such file error, which it does if you do: ruby myrubyscript --options arg1 and you're not in the folder with that script.


Use $0or $PROGRAM_NAME to get the file name that is currently being executed.


This isn't quite an answer to your question, but it sounds like you're reinventing a wheel. Look at the optparse library. It lets you define command line switches, arguments, etc, and it'll do all the heavy lifting for you.

참고URL : https://stackoverflow.com/questions/4834821/how-can-i-get-the-name-of-the-command-called-for-usage-prompts-in-ruby

반응형