Ruby frozen string literal pragma order caused error - ruby

Given a file with Ruby 2.3.0p0:
#!/usr/bin/env ruby
# frozen_string_literal: true
# Exit cleanly from an early interrupt
Signal.trap("INT") { abort }
This is fine.
# frozen_string_literal: true
#!/usr/bin/env ruby
# Exit cleanly from an early interrupt
Signal.trap("INT") { abort }
will result in error:
syntax error near unexpected token `"INT"'
`Signal.trap("INT") { abort }'
Why?

A shebang has to appear on the file's initial line.
A file test.rb containing:
#!/usr/bin/env ruby
# foo bar
puts "hello from #{RbConfig.ruby}"
will be run via Ruby:
$ ./test.rb
hello from /.../ruby-2.3.0/bin/ruby
But if test.rb contains: (1st and 2nd line swapped)
# foo bar
#!/usr/bin/env ruby
echo "hello from $SHELL"
it will be run as an ordinary shell script:
$ ./test.rb
hello from /.../bin/zsh
Therefore, the error you are getting is no Ruby error, it's from your shell.

Related

How to pass array as an argument to a Ruby function from command line?

I want to call Ruby function from command line with array as an argument.
Script name is test.rb
In below code Environments are like test,dev,uat.', am passing as ['test','dev','uat']
I have tried as below:
ruby -r "./test.rb" -e "start_services '['dev','test','uat']','developer','welcome123'"
def start_services(environments,node_user_name,node_password)
environments.each do |env|
puts env
end
puts node_user_name
puts node_password
end
Output:
-e:1: syntax error, unexpected tIDENTIFIER, expecting end-of-input start_services '['dev','test','uat']','developer',' ^
You clearly want to pass an array as the first parameter into start_services method, so you should do it like this:
$ ruby -r "./test.rb" -e "start_services ['dev','test','uat'],'developer','welcome123'"
# output:
dev
test
uat
developer
welcome123
What you've been trying so far was attempt to pass '[\'dev\',\'test\',\'uat\']' string, which was malformed, because you didn't escape ' characters.
Don't pass your credentials as arguments, any user on your system would be able to see them.
Instead, you could save them as environment variables or in a config file.
if ARGV.size == 0
puts "Here's how to launch this script : ruby #{__FILE__} env_name1 env_name2 ..."
exit
end
# Define those environment variables before launching the script.
# Alternative : write credentials in a json or yml file.
node_username = ENV["NODE_USERNAME"]
node_password = ENV["NODE_PASSWORD"]
ARGV.each do |env|
puts "Launching environment #{env}"
end

How to source a shell script and execute its functions in ruby

I'm new to ruby . I'm trying to source my shell script in ruby and execute functions in sourced shell script.
below is my shell script /tmp/test.sh
#!/bin/bash
function hello {
echo "hello, this script is being called from ruby"
}
below is my ruby script test.rb
#!/usr/bin/ruby
system("source /tmp/test.sh")
puts $?.exitstatus
system("hello")
puts $?.exitstatus
output using system
[root#localhost ~]# ruby test.rb
127
127
I even tried the back tick approach, but i got below error
code :
#!/usr/bin/ruby
status=`source /root/test.sh`
puts status
status2=`hello`
puts status2
error:
ruby test.rb
test.rb:3:in ``': No such file or directory - source (Errno::ENOENT)
from test.rb:3:in `<main>'
can anyone tell what is wrong in my code.
You can use session gem, or write a solution yourself.
script.sh:
#!/bin/bash
function hello() {
echo "Hello, World!"
}
Ruby file:
IO.popen('bash', 'r+') do |sh|
sh.puts 'source script.sh'
sh.puts 'hello'
sh.close_write
puts sh.gets
end
# => Hello, World!

Prevent Rake's sh command from echoing the command

Whenever I call sh from rake it often echos the command that will be ran right before it is run. How can I prevent sh from logging the commands to stdout. I'd like to prevent this as I have api keys in the command I am calling, and I don't want to expose them in my build log.
There are two parts to solving this. The first is to pass the verbose: false option, which will prevent the command from being printed before it's executed:
$ cat Rakefile
SECRET = 'foobarbaz'
task :foo do
sh "echo #{SECRET} > secrets.txt", verbose: false
end
$ rake foo
(no output)
However, this doesn't help if there's an error, since Rake will print the failed command if it returns an error:
$ cat Rakefile
SECRET = 'foobarbaz'
task :foo do
sh "echo #{SECRET} > secrets.txt; exit 1", verbose: false
end
$ rake foo
rake aborted!
Command failed with status (1): [echo foobarbaz > secrets.txt; exit 1...]
...
The solution is hinted at in the docs for sh:
If a block is given, upon command completion the block is called with an OK flag (true on a zero exit status) and a Process::Status object. Without a block a RuntimeError is raised when the command exits non-zero.
You can see where the default behavior comes from in the Rake source. The solution, then, is to supply our own block:
$ cat Rakefile
SECRET = "foobarbaz"
task :foo do
sh "echo #{SECRET} > secrets.txt; exit 1", verbose: false do |ok, status|
unless ok
fail "Command failed with status (#{status.exitstatus}): [command hidden]"
end
end
end
$ rake foo
rake aborted!
Command failed with status (1): [command hidden]
...
Looks good!
If you find yourself needing this in multiple places, you could write a convenience method; something like this:
def quiet_sh(*cmd)
options = (Hash === cmd.last) ? cmd.pop : {}
options = { verbose: false }.merge(options)
sh *cmd, options do |ok, status|
unless ok
fail "Command failed with status (#{status.exitstatus}): [command hidden]"
end
end
end
SECRET = "foobarbaz"
task :foo do
quiet_sh "do_secret_things"
end

OptionParser with subcommands

I'm trying to create a command line program with sub-commands using OptionParser. I'm following "ruby's OptionParser to get subcommands".
The problem is that it does not allow for a use case like this:
ruby main.rb --version
#=> main.rb in `<main>': undefined method `order!' for nil:NilClass (NoMethodError)
But it does allow for this:
ruby main.rb foo --options
ruby main.rb --options foo
ruby main.rb --options foo --options
How would I be properly handle command line arguments, in the case that no subcommand is given.
My example code is:
global = OptionParser.new do |opts|
opts.banner = "Usage: opt.rb [options] [subcommand [options]]"
opts.on("-v", "--version", "Print the version") do |v|
options[:version] = v
end
opts.separator ""
opts.separator subtext
end
The lines with the error:
global.order!
command = ARGV.shift
subcommands[command].order!
If global.order! uses all of ARGV, then command is nil. So... check for that.
global.order!
command = ARGV.shift
unless command
STDERR.puts "ERROR: no subcommand"
STDERR.puts global # prints usage
exit(-1)
end
subcommands[command].order!
Maybe this'll help:
require 'optparse'
VERSION = '1.0.0'
options = {}
OptionParser.new do |opt|
opt.on('-f', '--foo', 'Foo it') { |o| options[:foo] = o }
opt.on_tail('-v', '--version') do
puts VERSION
exit
end
end.parse!
puts options
Saving it as "test.rb" and running it with ruby test.rb returns:
{}
Running it with ruby test.rb -f or --foo returns:
{:foo=>true}
Running it with ruby test.rb -v or --version returns:
1.0.0
For more fun, running ruby test.rb -h or --help returns:
Usage: test [options]
-f, --foo Foo it
even though I didn't define -h or --help.
If I wanted the -v and --version flags to appear in the list then I'd change them from a on_tail method to a normal on method:
require 'optparse'
VERSION = '1.0.0'
options = {}
OptionParser.new do |opt|
opt.on('-f', '--foo', 'Foo it') { |o| options[:foo] = o }
opt.on('-v', '--version', 'Returns the version') do
puts VERSION
exit
end
end.parse!
puts options
which would return:
Usage: test [options]
-f, --foo Foo it
-v, --version Returns the version
I can add:
puts ARGV
to the end of the script and see that OptionParser is correctly handling flags and parameters:
>ruby test.rb bar --foo
{:foo=>true}
bar
>ruby test.rb --foo bar
{:foo=>true}
bar
See "Pass variables to Ruby script via command line" for more information.
There is no way your example code will handle your sample inputs using --options. No handler for --options is defined. Nor is subtext. Your code returns:
undefined local variable or method `subtext' for main:Object (NameError)
Stripping the block to:
global = OptionParser.new do |opts|
opts.on("-v", "--version", "Print the version") do |v|
options[:version] = v
end
end
and running again returns:
invalid option: --options (OptionParser::InvalidOption)
So, again, your example doesn't match the results you say you're getting.

How can a Ruby script detect that it is running in irb?

I have a Ruby script that defines a class. I would like the script to execute the statement
BoolParser.generate :file_base=>'bool_parser'
only when the script is invoked as an executable, not when it is require'd from irb (or passed on the command line via -r). What can I wrap around the statement above to prevent it from executing whenever my Ruby file is loaded?
The condition $0 == __FILE__ ...
!/usr/bin/ruby1.8
class BoolParser
def self.generate(args)
p ['BoolParser.generate', args]
end
end
if $0 == __FILE__
BoolParser.generate(:file_base=>__FILE__)
end
... is true when the script is run from the command line...
$ /tmp/foo.rb
["BoolParser.generate", {:file_base=>"/tmp/foo.rb"}]
... but false when the file is required or loaded by another ruby script.
$ irb1.8
irb(main):001:0> require '/tmp/foo'
=> true
irb(main):002:0>
use $0
in irb the value of $0 is "irb"
in your file is "/path/to/file"
an explanation here

Resources