I have a piece of code like:
output = `shell-command-to-run`
unless $?.success?
raise "Failure running shell command!"
end
I've mocked the backtick method to prevent the external shell command from running during my specs, but I've not found a way to set the $? global variable to exercise the failure side of the spec.
Maybe you should have a script that returns the sorts of errors you're expecting to uncover so you can test a variety of conditions. For example:
#!/usr/bin/env ruby
exit(ARGV[0].to_i)
You can then pass in the exit code you want to trap.
Instead of mocking the call, you just run a different script entirely to validate it handles errors correctly.
Related
I'm using bash for writing a script.
I use set -ein order to have the script exit (in error) when a line results in an error (i like this approach, that I find less error prone). I almost always combine it with set -u for also "raising" an error when it is attempted to read an undefined variable.
It has worked like a charm since a long time. But today, I found a bug in my script that I could not catch with "set -eu" before, because this does not seem to raise an error like usual.
For the following, I will use the false command that always returns an error (in my script this is another personnal command, but this makes it easier for you to reproduce).
set -eu
A=$(false)
The code above exits bash with an error. Cool this is what I expect.
BUT :
set -eu
export A=$(false)
does not raise any error, bash is totally fine with it !
It even sets the variable A with an empty string, so further reads of $A will not raise an error either !
This is really weird for me, is there a way of getting the expected behaviour, maybe another option for ```set`` ?
I can do this for having it raise an error, but I will have to write it this way every time, so it is less useful for catching bugs :
set -eu
A=$(false)
export A=$A
God bash is complicated :-)
You're not getting an error because the export itself is successful. I can't find explicit documentation of the behavior, but command substitution when building the arguments to another command seems to be one of the special cases where a non-zero exit status doesn't make set -e trigger.
I'm new to ruby, and I'm writing a pretty basic Rakefile. It contains a class Installer that has a method shell which calls sh like this:
class Installer
def shell(command)
sh command
end
end
When I run it, it throws
NoMethodError: undefined method `sh' for #<Installer:0x00007ffde8098b78>`
I have done some research to see what's wrong but most similar errors appear to be framework/library related, whereas my problem seems to be that it's just not possible to call sh from inside a method.
Is this the problem? if so, what options do I have (considering I want the program to crash if the command failed (which I believe system or backticks won't help here).
Change the sh invocation to Rake.sh. At least, that seemed to work for me.
The system method from the Kernel module returns false if the command run from the shell returns a non-zero value.
irb(main):002:0> system('exit 1')
=> false
irb(main):003:0> system('exit 0')
=> true
You can you use this as a way to exit on non-zero returns from your shell commands.
I'm trying to write a script in Fish that runs a Make recipe and then executes all of the resultant binaries. The problem I'm having is that I would like to have the script exit with an error code if the make command encounters an error. Whenever I try to capture Make's return value, I end up with its output log instead.
For example:
if test (make allUnitTests) -eq 0
echo "success"
else
echo "fail"
end
returns an error because "test" is seeing the build process, not the terminating condition.
I wrote the script so that I could easily make Jenkins run all my unit tests whenever I trigger a build. Since I haven't been able to get the above section of the script working correctly, I've instead instructed Jenkins to run the make command as a separate command, which does exactly what I want: halting the entire build process without executing any binaries if anything fails to compile. Thus, at this point my question is more of an academic exercise, but I would like to add building the unit test binaries into the script (and have it cleanly terminate on a build error) for the benefit of any humans who might check out the code and would like to run the unit tests.
I played a little with something like:
if test (count (make allUnitTests | grep "Stop")) -eq 0
but this has two problems:
I'm apparently piping stdout when I need to pipe stderr. (Come to think of it, if I could just check to see if anything was written to stderr, then I wouldn't need grep at all.)
Grep is swallowing all the log data piped to it, which I really want to be visible on the console.
You are misunderstanding the parentheses - these run a command substitution. What this does is capture the output of the process running in the substitution, which it will then use as arguments (separated by newlines by default) to the process outside.
This means your test will receive the full output of make.
What you instead want to do is just run if make allUnitTests without any parens, since you are just interested in the return value.
If you would like to do something between running make and checking its return value, the "$status" variable always contains the return value of the last command, so you can save that:
make allUnitTests
set -l makestatus $status
# Do something else
if test $makestatus -eq 0
# Do the if-thing
else
# Do the else-thing
end
In a Ruby script, there are various ways to call system commands / command lines
backticks: `command arg1 arg2`
delimited form, e.g. %x(command arg1 arg2) (other delimiters available)
Kernel#system method: system('command arg1 arg2')
Kernel#exec method: exec('command arg1 arg2')
If I want the Ruby script to fail (with an exception) when a called command fails (with a non-zero exit code) I can either check for the exit code in the special variable $? for the first two variants:
`command arg1 arg2`
fail unless $? == 0
or
%x,command arg1 arg2,
fail unless $? == 0
If I'm fine with the command's standard output going to the Ruby script's standard output (and I am), I can use variant 3 and check its return value:
unless system('command arg1 arg2')
fail
end
If I don't care about the ability to rescue the exception nor about the stacktrace printing behavior of unrescued exceptions, I can of course use exit(1) or in the first two variants exit($?) in place of fail.
If further the execution of the command is the last thing the Ruby script should do, even when the command succeeds (exit code 0), I can use the fourth variant:
exec('command arg1 arg2')
This will replace the Ruby process with the new process created by invoking the command, but the effect for the caller of the Ruby script will be the same: He sees a non-zero exit code exactly if the called command caused a non-zero exit code.
I very much like the conciseness of the fourth variant, but if executing the command isn't the last thing to do in case it succeeds, I can unfortunately not use it. The conditional fail or exit invocations of the other variants look very unclean in comparison and in one usecase of mine more often than not violate the single level of abstraction and single responsibility principles.
I could of course easily write a wrapper function for any of the first three approaches to make their usage look just as concise, but as this seems like such a fundamental modus operandi, I wondered whether Ruby already has something like that built in ... be it a utility function I could use instead of a wrapper of my own, or a mechanism that changes the behavior of one or several of the command invocation methods to cause an error or a non-zero exit when a command fails, analogous to sh's and bash's option to set -e.
As far as I know there is no built-in way to do this, but you can almost get the behavior you want with a little metaprogramming magic
def set_exit enable
if enable
define_method :system do |*args|
Kernel.system *args
exit $?.exitstatus unless $?.success?
end
else
define_method :system, Kernel.instance_method(:system)
end
end
set_exit true
# ...
# any failed system calls here will cause your script to exit
# ...
set_exit false
# now system is back to normal
This works by redefining system for Object, while explicitly using Kernel.system when the built-in behavior is needed.
I'm writing some scripts in Ruby, and I need to interface with some non-Ruby code via shell commands. I know there are at least 6 different ways of executing shell commands from Ruby, unfortunately, none of these seem to stop execution when a shell command fails.
Basically, I'm looking for something that does the equivalent of:
set -o errexit
...in a Bash script. Ideally, the solution would raise an exception when the command fails (i.e., by checking for a non-zero return value), maybe with stderr as a message. This wouldn't be too hard to write, but it seems like this should exist already. Is there an option that I'm just not finding?
Ruby 2.6 adds an exception: argument:
system('ctat nonexistent.txt', exception: true) # Errno::ENOENT (No such file or directory - ctat)
Easiest way would be to create a new function (or redefine an existing one) to call system() and check the error code.
Something like:
old_sys = system
def system(...)
old_system(...)
if $? != 0 then raise :some_exception
end
This should do what you want.
You can use one of ruby's special variables. The $? (analogous to the same shell script var).
`ls`
if $? == 0
# Ok to go
else
# Not ok
end
Almost every program sets this var to 0 if everything went fine.
Tiny bit simpler: you don't need to check $? w/ system, and since the command you ran will output to stderr itself you can usually just non-zero-exit rather than raising an exception w/ an ugly stack-trace:
system("<command>") || exit(1)
So you could take that a step further and do:
(system("<command 1>") &&
system("<command 2>") &&
system("<command 3>")) || exit(1)
...which would short-circuit and fail on error (in addition to being hard to read).
Ref: From Ruby 2.0 doc for system (although true of 1.8.7 as well):
system returns true if the command gives zero exit status, false for non zero exit status.
http://www.ruby-doc.org/core-2.0.0/Kernel.html#method-i-system