How can I get the name of the command called for usage prompts in Ruby?

前端 未结 4 1509
不思量自难忘°
不思量自难忘° 2020-12-07 14:20

I wrote a nice little Ruby script a while back that I\'m rather fond of. I\'d like to improve its robustness by checking for the proper number of arguments:

         


        
4条回答
  •  误落风尘
    2020-12-07 15:10

    this answer might come a bit late, but I have had the same issue and the accepted answer didn't seem quite satisfying to me, so I investigated a bit further.

    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 
    #include 
    
    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 
    
    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.

提交回复
热议问题