У Ruby есть три способа дать нам имя вызываемого скрипта:
#!/usr/bin/env ruby
puts "$0 : #{$0}"
puts "__FILE__ : #{__FILE__}"
puts "$PROGRAM_NAME : #{$PROGRAM_NAME}"
Сохранение этого кода как «test.rb» и его вызов несколькими способами показывает, что сценарий получает имя в том виде, в каком оно было передано ему ОС. Сценарий знает только то, что ему сообщает ОС:
$ ./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 во втором примере показывает, что ОС заменяет его расширенным путем, совпадающим с тем, что находится в третьем примере. Во всех случаях это то, что передала ОС.
Связывание с файлом с использованием как жестких, так и программных ссылок показывает последовательное поведение. Я создал жесткую ссылку для 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.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"
этот ответ может быть немного запоздалым, но у меня была такая же проблема, и принятый ответ не казался мне вполне удовлетворительным, поэтому я исследовал немного дальше.
Что меня беспокоило, так это то, что на самом деле
$0
или$PROGRAM_NAME
не было правильной информации о том, что пользователь ввел . Если мой сценарий Ruby находится в папке PATH и пользователь вводит имя исполняемого файла (без каких-либо определений пути, таких как./script
или/bin/script
), он всегда будет расширяться до общего пути.Я думал, что это недостаток Ruby, поэтому я попробовал то же самое с Python, и, к моему огорчению, ничего не изменилось.
Друг предложил мне взломать поиск
real thing
внутри/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 ФАЙЛ ...
Чтобы доказать, что это действительно так
execve
, а не из bash, мы можем создать фиктивный интерпретатор, который ничего не делает, кроме как распечатывает переданные ему аргументы:// 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
и вы не в папке с этим сценарием .источник
Используйте
$0
или,$PROGRAM_NAME
чтобы получить имя файла, который в данный момент выполняется.источник
/usr/local/bin/myScript
и/usr/local/bin
в моем$PATH
, и я просто набратьmyScript
, я получаю/usr/local/bin/myScript
от$0
$0.split("/").last
?./myScript
, мне нужна переменная, которая дает мне./myScript
. Если они напечатали/usr/bin/local/myScript
, я хочу именно это. и т. д.Это не совсем ответ на ваш вопрос, но похоже, что вы изобретаете колесо. Посмотрите библиотеку optparse . Он позволяет вам определять параметры командной строки, аргументы и т.д., и он сделает всю тяжелую работу за вас.
источник