I would like to run 2 commands in ruby but only if the first one succeeds.
In bash I would use && operator. I have tried this one and and keyword but && has thrown an error and and operator didn't works as expected.
The example I want to use it for:
#!/usr/bin/ruby
#
puts "asd" and puts "xxx"
executed as:
$ ./asd.rb
asd
The keyword and has lower precedence than &&. Both use short-circuit evaluation.
First, note that puts always returns nil. In ruby, nil is falsey.
2.2.0 :002 > puts "asdf"
asdf
=> nil
Now we try your example:
2.2.0 :002 > puts "asd" and puts "xxx"
asd
=> nil
This is the same as:
puts("asd") && puts("xxx")
asd
=> nil
In both cases puts "asd" and puts("asd") return nil so puts "xxx" and puts("xxx") are never evaulated because nil is falsey and there is short-circuit evaulation being used.
You also tried puts "asd" && puts "xxx", but this is a syntax error because of the higher precendence of the && operator.
puts "asd" && puts "xxx"
SyntaxError: (irb):3: syntax error, unexpected tSTRING_BEG, expecting keyword_do or '{' or '('
puts "asd" && puts "xxx"
^
That's because puts "asd" && puts "xxx" is the same as puts("asd" && puts) "xxx".
2.2.0 :012 > puts("asd" && puts) "xxx"
SyntaxError: (irb):12: syntax error, unexpected tSTRING_BEG, expecting end-of-input
puts("asd" && puts) "xxx"
^
See also: this related post
Related
Why is this a syntax error in ruby?
#!/usr/bin/ruby
servers = [
"xyz1-3-l"
, "xyz1-2-l"
, "dws-zxy-l"
, "abcl"
]
hostname_input = ARGV[0]
hostname = hostname_input.gsub( /.example.com/, "" )
servers.each do |server|
if hostname == server then
puts "that's the one"
break
end
end
... when I execute this script I get this output ...
$ ./test.rb abc1
./test.rb:5: syntax error, unexpected ',', expecting ']'
, "xyz1-2-l"
^
./test.rb:6: syntax error, unexpected ',', expecting $end
, "dws-zxy-l"
^
... if I simply put everything on the same line its ok ...
$ cat test.rb
#!/usr/bin/ruby
servers = [ "xyz1-3-l" , "xyz1-2-l" , "dws-zxy-l" , "abcl" ]
hostname_input = ARGV[0]
hostname = hostname_input.gsub( /.example.com/, "" )
servers.each do |server|
if hostname == server then
puts "that's the one"
break
end
end
$ ./test.rb dws-zxy-l
that's the one
Look ma, no commas (or quotes):
servers = %W[
xyz1-3-l
xyz1-2-l
dws-zxy-l
abcl
]
# => ["xyz1-3-l", "xyz1-2-l", "dws-zxy-l", "abcl"]
Newlines are significant in Ruby. You need to put the comma at the end of the line or use a backslash before your newline to indicate that the line is continuing (of course, in that case, what's the point in moving the comma to the next line?).
It seems that if I put code in my ternary evaluation it fails, but placing true or false it works.
Here is my code:
>test = [nil]
=> [nil]
>test.any? ? puts "AAA" : puts "BBB"
SyntaxError: (irb):16: syntax error, unexpected tSTRING_BEG, expecting keyword_do or '{' or '('
test.any? ? puts "AAA" : puts "BBB"
^
(irb):16: syntax error, unexpected ':', expecting $end
test.any? ? puts "AAA" : puts "BBB"
>test.any? ? true : false
=> false
>test << 1
=> [nil, 1]
>test.any? ? true : false
=> true
>test.any? ? puts "AAA" : puts "BBB"
SyntaxError: (irb):14: syntax error, unexpected tSTRING_BEG, expecting keyword_do or '{' or '('
test.any? ? puts "AAA" : puts "BBB"
^
(irb):14: syntax error, unexpected ':', expecting $end
test.any? ? puts "AAA" : puts "BBB"
^
You need parentheses.
>> test.any? ? puts("AAA") : puts("BBB")
BBB
=> nil
You should avoid parentheseless call in inline functions.
Say I have a simple command line interpreter like this:
while true
print '> '
cmd = gets.chomp
break if cmd =~ /^(exit|quit)/
system(cmd) || puts('Command not found or invalid.')
end
I would like to, instead of the "Command not found or invalid." message get an actual error message, like one you would get from bash. How would I do this?
well, if it's unix-like system you could actually append 2>&1 to your command:
system(cmd + ' 2>&1 ')
which would redirect your stderr to stdout
another way is using %x[...] :
irb(main):027:0> def hello
irb(main):029:2* %x[hello]
irb(main):030:2> rescue Exception => e
irb(main):031:2> puts e.message
irb(main):033:1> end
=> nil
irb(main):034:0> hello
No such file or directory - hello
=> nil
irb(main):035:0>
meaning, you can rescue the command execution and return the exception message
Is there a way to capture a "command not found" error in a Ruby script? For instance, given:
output = `foo`
How do I trap the situation where foo isn't installed? I expected that I could rescue an exception, but this doesn't seem to work on 1.8.7. Is there a different way of calling the subprocess that will do what I want? Or is there a different approach?
Update
My apologies, I forgot to mention a hidden requirement: I would prefer that the interpreter doesn't leak the command line to the user (it can contain sensitive data), hence why the exception catching method is preferred. Apologies again for leaving this out the first time.
Use the return code!
irb(main):001:0> `date`
=> "Mo 24. Jan 16:07:15 CET 2011\n"
irb(main):002:0> $?
=> #<Process::Status: pid=11556,exited(0)>
irb(main):003:0> $?.to_i
=> 0
irb(main):004:0> `foo`
(irb):4: command not found: foo
=> ""
irb(main):005:0> $?.to_i
=> 32512
http://corelib.rubyonrails.org/classes/Process/Status.html
Redirecting STDERR to STDOUT will give you the output as return value instead of bloating it just out:
irb(main):010:0> `foo 2>&1`
=> "sh: foo: not found\n"
irb(main):011:0> $?.to_i
=> 32512
Since:
irb --help
Usage: irb.rb [options] [programfile] [arguments]
I know I can pass arguments to ARGV if I include a programfile
eg:
irb test.rb A B C
where test.irb is simply "p ARGV"
produces:
["a", "b", "c"]
Making programfile be con in DOS... I can do following
irb con A B C
con(main):001:0> ARGV
produces:
ARGV
=> ["A", "B", "C"]
but this is system dependent and has the side effect of echoing input :-(
What i really like is something like
irb -- a b c
BTW: I know I can set ARGV inside irb but I my intention is to alias special == irb -rSpecialLibrary" so I can do something like:
special A B C
<input goes here>
Any suggestions?
Looking at the source of the irb executable:
#!/usr/bin/env ruby
require "irb"
if __FILE__ == $0
IRB.start(__FILE__)
else
# check -e option
if /^-e$/ =~ $0
IRB.start(__FILE__)
else
IRB.setup(__FILE__)
end
end
The at the source of the IRB module:
# File lib/irb/init.rb, line 15
def IRB.setup(ap_path)
IRB.init_config(ap_path)
IRB.init_error
IRB.parse_opts
IRB.run_config
IRB.load_modules
unless #CONF[:PROMPT][#CONF[:PROMPT_MODE]]
IRB.fail(UndefinedPromptMode, #CONF[:PROMPT_MODE])
end
end
Down to parse_opts, our problem method:
# File lib/irb/init.rb, line 126
def IRB.parse_opts
load_path = []
while opt = ARGV.shift
case opt
when "-f"
#CONF[:RC] = false
when "-m"
#CONF[:MATH_MODE] = true
when "-d"
$DEBUG = true
when /^-r(.+)?/
opt = $1 || ARGV.shift
#CONF[:LOAD_MODULES].push opt if opt
when /^-I(.+)?/
opt = $1 || ARGV.shift
load_path.concat(opt.split(File::PATH_SEPARATOR)) if opt
when '-U'
set_encoding("UTF-8", "UTF-8")
when /^-E(.+)?/, /^--encoding(?:=(.+))?/
opt = $1 || ARGV.shift
set_encoding(*opt.split(':', 2))
when "--inspect"
#CONF[:INSPECT_MODE] = true
when "--noinspect"
#CONF[:INSPECT_MODE] = false
when "--readline"
#CONF[:USE_READLINE] = true
when "--noreadline"
#CONF[:USE_READLINE] = false
when "--echo"
#CONF[:ECHO] = true
when "--noecho"
#CONF[:ECHO] = false
when "--verbose"
#CONF[:VERBOSE] = true
when "--noverbose"
#CONF[:VERBOSE] = false
when /^--prompt-mode(?:=(.+))?/, /^--prompt(?:=(.+))?/
opt = $1 || ARGV.shift
prompt_mode = opt.upcase.tr("-", "_").intern
#CONF[:PROMPT_MODE] = prompt_mode
when "--noprompt"
#CONF[:PROMPT_MODE] = :NULL
when "--inf-ruby-mode"
#CONF[:PROMPT_MODE] = :INF_RUBY
when "--sample-book-mode", "--simple-prompt"
#CONF[:PROMPT_MODE] = :SIMPLE
when "--tracer"
#CONF[:USE_TRACER] = true
when /^--back-trace-limit(?:=(.+))?/
#CONF[:BACK_TRACE_LIMIT] = ($1 || ARGV.shift).to_i
when /^--context-mode(?:=(.+))?/
#CONF[:CONTEXT_MODE] = ($1 || ARGV.shift).to_i
when "--single-irb"
#CONF[:SINGLE_IRB] = true
when /^--irb_debug=(?:=(.+))?/
#CONF[:DEBUG_LEVEL] = ($1 || ARGV.shift).to_i
when "-v", "--version"
print IRB.version, "\n"
exit 0
when "-h", "--help"
require "irb/help"
IRB.print_usage
exit 0
when "--"
if opt = ARGV.shfit
#CONF[:SCRIPT] = opt
$0 = opt
end
break
when /^-/
IRB.fail UnrecognizedSwitch, opt
else
#CONF[:SCRIPT] = opt
$0 = opt
break
end
end
if RUBY_VERSION >= FEATURE_IOPT_CHANGE_VERSION
load_path.collect! do |path|
/\A\.\// =~ path ? path : File.expand_path(path)
end
end
$LOAD_PATH.unshift(*load_path)
end
It is hardcoded to take that option as the script name (#CONF[:SCRIPT] = opt). Luckily, this is Ruby. The first idea I had was using a different script to launch IRB that modifies the module first.
~/bin/custom-irb:
#!/usr/bin/env ruby
require 'irb'
module IRB
class << self
# sort of lame way to reset the parts we don't like about
# parse_opts after it does the parts we do like
def parse_opts_with_ignoring_script
arg = ARGV.first
script = $0
parse_opts_without_ignoring_script
#CONF[:SCRIPT] = nil
$0 = script
ARGV.unshift arg
end
alias_method :parse_opts_without_ignoring_script, :parse_opts
alias_method :parse_opts, :parse_opts_with_ignoring_script
end
end
if __FILE__ == $0
IRB.start(__FILE__)
else
# check -e option
if /^-e$/ =~ $0
IRB.start(__FILE__)
else
IRB.setup(__FILE__)
end
end
You can launch this with custom-irb foo bar baz and ARGV will be ['foo', 'bar', 'baz'].
quite strange solution is to create file with variables
# defaults.rb
#a = "hello world"
And
# terminal
=> irb -r defaults.rb
irb=> #a
irb=> "hello world"
You can make a file that modifies ARGV and then use '-r' to include it.
$ echo 'ARGV = ["testing", "1","2","3"]' > ~/blah.rb && irb -r ./blah test.rb
/home/me/blah.rb:1: warning: already initialized constant ARGV
test.rb(main):001:0> require 'pp'
=> true
test.rb(main):002:0* pp ARGV
["testing", "1", "2", "3"]
=> ["testing", "1", "2", "3"]
test.rb(main):003:0>
You could even redirect it to your your ~/.irbrc and leave out the '-r ./blah'.