How can I get the name of the command called for usage prompts in Ruby?
Ruby has three ways of giving us the name of the called script:
#!/usr/bin/env ruby
puts "$0 : #{$0}"
puts "__FILE__ : #{__FILE__}"
puts "$PROGRAM_NAME : #{$PROGRAM_NAME}"
Saving that code as "test.rb" and calling it a couple ways shows that the script receives the name as it was passed to it by the OS. A script only knows what the OS tells it:
$ ./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
Calling it using the ~
shortcut for $HOME in the second example shows the OS replacing it with the expanded path, matching what is in the third example. In all cases it's what the OS passed in.
Linking to the file using both hard and soft links shows consistent behavior. I created a hard link for test1.rb and a soft link for 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
Launching ruby test.rb
with any of the variations on the script name returns consistent results.
If you only want the called filename, you can use File's basename
method with one of the variables or split on the delimiter and take the last element.
$0
and __FILE__
have some minor differences but for single scripts they're equivalent.
puts File.basename($0)
There are some benefits to using the File.basename
,File.extname
and File.dirname
suite of methods. basename
takes an optional parameter, which is the extension to strip, so if you need just the basename without the extension
File.basename($0, File.extname($0))
does it without reinventing the wheel or having to deal with variable-length or missing extensions or the possibility of incorrectly truncating extension chains ".rb.txt
" for instance:
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"
Use a command line option to call a method
There are a lot of ways to do this, depending on the use case. The below code is taken from the Ruby docs with the extra method added.
Realistically you'd probably want a class that handles the different options and encapsulates the method instead of having them at the file scope.
#!/usr/bin/env ruby
require 'optparse'
options = {}
OptionParser.new do |opts|
opts.banner = "Usage: example.rb [options]"
opts.on("-b", "Run method 'b'") do |v|
options[:b] = true
end
end.parse!
def b
puts "Hello Brian"
end
if options[:b]
b
end
I've also added a shebang at the top that will automatically call ruby. As long as your script file is executable you can call it directly (ie. ./mine.rb -b
).
How can same program in ruby accept input from user as well as command line arguments
gets
will only read from the real $stdin
(ie the user) if there are no command line arguments. Otherwise it'll take those arguments as files to concatenate as a virtual $stdin
.
You can use STDIN.gets
to make sure you're always using the right function.
You can also delete the arguments from ARGV
as you go (or remove them all in one go), your gets
should work correctly.
One liner in Ruby for displaying a prompt, getting input, and assigning to a variable?
Check out highline:
require "highline/import"
input = ask "Input text: "
Call ruby function from command-line
First the name of the class needs to start with a capital letter, and since you really want to use a static method, the function name definition needs to start with self.
.
class TestClass
def self.test_function(someVar)
puts "I got the following variable: " + someVar
end
end
Then to invoke that from the command line you can do:
ruby -r "./test.rb" -e "TestClass.test_function 'hi'"
If you instead had test_function
as an instance method, you'd have:
class TestClass
def test_function(someVar)
puts "I got the following variable: " + someVar
end
end
then you'd invoke it with:
ruby -r "./test.rb" -e "TestClass.new.test_function 'hi'"
Find declared function from user input
You can write a mapping for the different inputs in the form of a case:
case command
when "pwd"
Dir.pwd # or move it into another method and call it here
when "ls"
# etc
end
or a hash, which is slightly more concise
actions = {
pwd: -> { Dir.pwd },
ls: -> { <foobar> }
}
actions[command.to_sym].call
You can also make method names matching the commands and use send
, but don't do this if the input is coming from strangers:
def pwd
Dir.pwd
end
command = command.to_sym
send(command)
Pre-Filled Prompt in Ruby
You can do it with RbReadline:
require 'rubygems'
require 'rb-readline'
module RbReadline
def self.prefill_prompt(str)
@rl_prefill = str
@rl_startup_hook = :rl_prefill_hook
end
def self.rl_prefill_hook
rl_insert_text @rl_prefill if @rl_prefill
@rl_startup_hook = nil
end
end
RbReadline.prefill_prompt("Previous query")
str = Readline.readline("Enter query: ", true)
puts "You entered: #{str}"
Using a different name for calling a ruby gem from the command line
Your gem name and the executables it bundles don't have to be the same at all. In your gemspec, you can define a list of executables via executables
:
Gem::Specification.new do |s|
s.name = "super_awesome_gem"
# other gemspec stuff
s.executables = ["sup_awe"]
end
As long as sup_awe
is listed in the gemspec's files
list, and is executable, that will be in the user's path after they install your gem. Bundler, when bootstrapping your gemspec, makes this even simpler
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
Anything in bin/
will be treated as an executable.
That is the long way of saying that your exectuable/bin file can be named whatever you want, and doesn't have to be named for your gem.
Related Topics
Why Isn't 'Method=' Treated the Same as Any Other Method
Call Controller from Rake Task
How to Set Http_Referer When Testing in Rails
Ruby 1.9 + Sinatra Incompatible Character Encodings: Ascii-8Bit and Utf-8
How to Convert a Ruby Hash to Xml
Keep Form Fields Filled After an Error (Ror)
Ruby on Rails Rake Assets:Precompile Error
In 'Require': No Such File to Load -- Iconv (Loaderror)
How to Dynamically Alter Inheritance in Ruby
Node.Js Not Found by Rails/Execjs
Changing Table Name at Query Run Time in a Rails Application
How to Iterate Activerecord Attributes, Including Attr_Accessor Methods
Shell Out from Ruby While Setting an Environment Variable
What's the Difference Between Colon ":" and Fat Arrow "=>"
Ruby Datetime Parsing from 'Mm/Dd/Yyyy' Format