checking a bash command in a if statement in Ruby - ruby

How can I check the return value (true/false) of bash command in an if statement in Ruby. I want something like this to work,
if ("/usr/bin/fs wscell > /dev/null 2>&1")
has_afs = "true"
else
has_afs = "false"
end
It complains with the following error meaning, it will always return true.
(irb):5: warning: string literal in condition
What's the correct syntax ?
UPDATE :
/usr/bin/fs wscell
looks for afs installed and running condition. It will throw a string like this,
This workstation belongs to cell <afs_server_name>
If afs is not running, the command exits with status 1

You want backticks rather than double-quotes. To check a program's output:
has_afs = `/usr/bin/fs wscell > /dev/null 2>&1` == SOMETHING ? 'true' : 'false'
Where SOMETHING is filled in with what you're looking for.

You should probably use system() or Backticks and then check the exit status of the command ($?.exitstatus):
Heres a good quicktip read: http://rubyquicktips.com/post/5862861056/execute-shell-commands)
UPDATE:
system("/usr/bin/fs wscell > /dev/null 2>&1") # Returns false if command failed
has_afs = $?.exitstatus != 1 # Check if afs is running

Related

Checking if current file is being sourced using return yields reversed result after command returning 1

I know the title is a bit weird, but I'm not sure how to better word it.
I'm using $(return &> /dev/null) to detect if a file is sourced or not.
I know that this method is not 100% reliable, but it has worked for me without issues before now. I've figured out how to work around the problem, but I can't figure out why this is happening.
Edit: This is for an internal company project and is not intended to be POSIX compliant or portable to other shells.
I've tried this on three different systems and had the same results:
Redhat 6.9; $BASH_VERSION=4.1.2(1)-release
Mint 18.2; $BASH_VERSION=4.3.48(1)-release
Arch; $BASE_VERSION=4.4.23(1)-release
If $? is 1 (false) when $(return &> /dev/null) the return code is reversed.
$ hr | cat test_0source - test_0source.sh; hr; ./test_0source
#!/bin/bash
# test_0source
false
test_0source.sh
false
source test_0source.sh
true
test_0source.sh
true
source test_0source.sh
========================================
#!/bin/bash
# test_0source.sh
# shellcheck disable=SC2091
$(return &> /dev/null)
echo "$?"
========================================
1
1
1
0
I expected to be seeing
1
0
1
0
My workaround is to add true right before the return check. While this gives me the correct results, adding false instead of true cause the return check to always return 1.
I realize I'm missing something basic here, but I'm not seeing it. Why is this doing this?
Edit: I had an incorrect value for $? in my initial explanation above.
Update:
First, using $(return 0 &> /dev/null) resolves the problem, thanks to #ondre-k.
Ondre also pointed out that comparing BASH_SOURCE against $0 is a more idiomatic way to do this:
$ hr | cat test_0bs - test_0bs.sh; hr; test_0bs | less
#!/bin/bash
# test_0bs
false
test_0bs.sh
hr
false
source test_0bs.sh
hr
true
test_0bs.sh
hr
true
source test_0bs.sh
========================================
#!/bin/bash
# test_0bs.sh
if [[ ${BASH_SOURCE[0]} == "$0" ]]; then
echo "We are not being sourced."
else
echo "We are being sourced."
fi
========================================
We are not being sourced.
========================================
We are being sourced.
========================================
We are not being sourced.
========================================
We are being sourced.
I suspect there may be an edge case or two around this, but it also seems to me that these cases will be less of a concern.
What you are doing is basically "abusing" the fact that return can only happen from function or a sourced script and assuming it yields 0 when a return was possible (we're sourced) and 1 if not, while suppressing the error output. You also do that in a subshell. Oddly enough, bash is still OK with placement of the return but does not return from a sourced script and keeps going. So far, so god.
The problem is, that "naked" (w/o explicit value specified) return, just like exit propagate last seen return code (return code of the command immediately preceding it).
In other words false; return would be the same thing as return 1. You can also try this out by replacing true and false by (exit 255) and see what happens. The 1 you are seeing for return following a false is not the one of "error: you cannot say return now" as your test is looking for, but just an ordinary return that has returned the last from the command preceding it (false). This difference would also become obvious if you've dropped the stderr redirection.
TL;DR for this construct to work as you expected, change it to return 0.
I hope I have not missed some corner case, but this should work as an alternative with bash by comparing name of the executed script and a source file. [[ "${BASH_SOURCE}" = ${0} ]] evaluates to 0 if file has not been sourced and 1 if it has. Or replace = with != to get same meaning of values as in the return case above.

What is the idea in this bash statement ( command || true )?

I saw the following bash statement being used on the internet:
PYTHON_BIN_PATH=$(which python || which python3 || true)
I understand that if which python fails, then which python3 will be executed, but I don't understand the purpose of the true at the end of the condition. Any idea?
try running:(Note the bla)
which python_bla || which python3_bla_bla || true
echo $?
0
You will get RC=0. It means it a construct to successfully proceed to next command. Here we know python_bla or python3_bla_bla does not exist,but still command gave rc=0
Example: Check the RC of following three commands, I have changed the spelling of date command to incorrect but true is causing RC to remain 0.
date;echo $?
Thu Nov 9 01:40:44 CST 2017
0
datea;echo $?
If 'datea' is not a typo you can use command-not-found to lookup the package that contains it, like this:
cnf datea
127
datea||true;echo $?
If 'datea' is not a typo you can use command-not-found to lookup the package that contains it, like this:
cnf datea
0
Note: You can also use : operator instead of true to get the same results.Example:
command || :
To be more rigorous I guess.
for example:
if aaa isn't an existed global binary file.
After executing which aaa,you can execute echo $? and the result is 1.
But if you execute which aaa | true the result will be 0.
Simple. It will check either your system has out of the box python(version of python which has come with your O.S) or it has python version 3 in it. It will also confirm python's executable path too, you could simply print variable named PYTHON_BIN_PATH by doing echo "$PYTHON_BIN_PATH" and could check it too once.
EDIT: Here is a simple example to understand. Let's say we have a variable named val with NULL value and we do this:
echo $val || true
Output will be NULL then as it's previous command didn't give any output.
Let's say we have val=4 then we run it as follows.
val="4"
echo $val || true
4
The idea is to set the PYTHON_BIN_PATH to nothing if both which fails.
Visibly there is no difference between
PYTHON_BIN_PATH=$(which python || which python3 || true)
and
PYTHON_BIN_PATH=$(which python || which python3)
But running the command alone makes things more obvious. Suppose that python doesn't exist on the system.
$(which python || which python3)
echo $? #Returning the exit status of the previous command
1 # A non zero status generally means the previous statement failed
$(which python || which python3 || true)
echo $?
0
In short, using true at the end always give a zero exit status for
$( command || true )

How to avoid calling external utility (grep) twice while maintaining return code & output?

I have following Bash function which return property value from Java-style property file. It property wasn't found, it should return non-zero. If found, property's value will be printed & return code must be zero.
function property_get() {
local pfile="$1"
local pname="$2"
if egrep "^${pname}=" "$pfile" 2>&1 >/dev/null; then
local line="$(egrep "^${pname}=" "$pfile")"
printf "${line#*=}"
return 0 # success
else
return 1 # property not found
fi
}
The question is: how to avoid from calling egrep twice? First exec is for status code, 2nd is for property value. If I use $(grep parameters) notation, then grep will be launched in subshell and I can't get it's return code and won't be able to determine success or failure of property searching.
This should work:
...
local line
if line=$(egrep "^${pname}=" "$pfile" 2>/dev/null); then
...
As #matt points out, you can just get the line then check the exit status of your command:
line=$(...)
if test $? -eq 0; then
# success
...
else
# property not found
...
fi
Edit:
To summarize:
You can use var=$(cmd), to set var to the standard output of cmd and $? to the exit status of cmd
With local var=$(cmd), $? will be set to the exit status of local which is 0 as explained here: Why does "local" sweep the return code of a command?

Get PowerShell output in Ruby

I am writing some automation script that needs to run PowerShell commands on a remote machine using Ruby. In Ruby I have the following code:
def run_powershell(powershell_command)
puts %Q-Executing powershell #{powershell_command}-
output = system("powershell.exe #{powershell_command}")
puts "Executed powershell output #{output}"
end
I can pass in Invoke-Command based ps1 files and everything works as expected. I can see the output in the console when I run the command.
The only problem is that there is no way to find out if the command run was successful; sometimes PowerShell is clearly throwing errors (like not able to get to the machine), but the output is always true.
Is there a way to know if the command ran successfully?
system(...) will actually return a value saying if it succeeded, not the output of the call.
So you can simply say
success = system("powershell.exe #{powershell_command}")
if success then
...
end
If you want both the output and return code, you can use `backticks` and query $? for the exit status (not the same $? as linked to in the comment to the question, by the way.)
output = `powershell.exe #{powershell_command}`
success = $?.exitstatus == 0
If you want a more reliable way that will escape things better, I'd use IO::popen
output = IO::popen(["powershell.exe", powershell_command]) {|io| io.read}
success = $?.exitstatus == 0
If the problem is that powershell itself isn't exiting with an error, you should have a look at this question
There is another option, and that is running the PowerShell from cmd. Here is the (pretty hard to figure out) syntax:
def powershell_output_true?()
ps_command = "(1+1) -eq 2"
cmd_str = "powershell -Command \" " + ps_command + " \" "
cmd = shell_out(cmd_str, { :returns => [0] })
if(cmd.stdout =~ /true/i)
Chef::Log.debug "PowerShell output is true"
return true
else
Chef::Log.debug "PowerShell output is false"
return false
end
end
I am comparing the stdout to true, but you can compare it to whatever you need.
described in blog

ruby system command check exit code

I have a bunch of system calls in ruby such as the following and I want to check their exit codes simultaneously so that my script exits out if that command fails.
system("VBoxManage createvm --name test1")
system("ruby test.rb")
I want something like
system("VBoxManage createvm --name test1", 0) <-- where the second parameter checks the exit code and confirms that that system call was successful, and if not, it'll raise an error or do something of that sort.
Is that possible at all?
I've tried something along the lines of this and that didn't work either.
system("ruby test.rb")
system("echo $?")
or
`ruby test.rb`
exit_code = `echo $?`
if exit_code != 0
raise 'Exit code is not zero'
end
From the documentation:
system returns true if the command gives zero exit status, false for
non zero exit status. Returns nil if command execution fails.
system("unknown command") #=> nil
system("echo foo") #=> true
system("echo foo | grep bar") #=> false
Furthermore
An error status is available in $?.
system("VBoxManage createvm --invalid-option")
$? #=> #<Process::Status: pid 9926 exit 2>
$?.exitstatus #=> 2
For me, I preferred use `` to call the shell commands and check $? to get process status. The $? is a process status object, you can get the command's process information from this object, including: status code, execution status, pid, etc.
Some useful methods of the $? object:
$?.exitstatus => return error code
$?.success? => return true if error code is 0, otherwise false
$?.pid => created process pid
system returns false if the command has an non-zero exit code, or nil if there is no command.
Therefore
system( "foo" ) or exit
or
system( "foo" ) or raise "Something went wrong with foo"
should work, and are reasonably concise.
You're not capturing the result of your system call, which is where the result code is returned:
exit_code = system("ruby test.rb")
Remember each system call or equivalent, which includes the backtick-method, spawns a new shell, so it's not possible to capture the result of a previous shell's environment. In this case exit_code is true if everything worked out, nil otherwise.
The popen3 command provides more low-level detail.
One way to do this is to chain them using and or &&:
system("VBoxManage createvm --name test1") and system("ruby test.rb")
The second call won't be run if the first fails.
You can wrap those in an if () to give you some flow-control:
if (
system("VBoxManage createvm --name test1") &&
system("ruby test.rb")
)
# do something
else
# do something with $?
end
Ruby 2.6 added option to raise exception in Kernel#system:
system("command", exception: true)
I want something like
system("VBoxManage createvm --name test1", 0) <-- where the second parameter checks the exit code and confirms that that system call was successful, and if not, it'll raise an error or do something of that sort.
You can add exception: true to your system call to have an error raised on non 0 exit codes.
For example, consider this small wrapper around system which prints the command (similar to bash -x, fails if there's a non 0 exit code (like bash -e) and returns the actual exit code:
def sys(cmd, *args, **kwargs)
puts("\e[1m\e[33m#{cmd} #{args}\e[0m\e[22m")
system(cmd, *args, exception: true, **kwargs)
return $?.exitstatus
end
To be called like: sys("hg", "update")
If you want to call a program that uses a different convention for exit codes, you can suppress raising the exception:
sys("robocopy", src, dst, "/COPYALL", "/E", "/R:0", "/DCOPY:T", exception: false)
You can also suppress stdout and stderr for noisy programs:
sys("hg", "update", "default", :out => File::NULL, :err => File::NULL)

Resources