Returning true/false value to bash from node script - node.js

Is it possible to get a true/false value from a node script which also writes something into std output?
// node script
console.log('doing something');
if (doSomething()) {
return true; // I would like to get this in bash
}
return false; // Or this
Then in my bash I read it like this
SUCCESS=$(node script.js)
but the problem is that SUCCESS contains all the logging from console.log but not the return value.
In my environment, I can't use exit codes for this, otherwise the whole script fails.

There's a two main options for this I can see:
Return a status code, and use it carefully to ensure it doesn't crash your script, e.g:
SUCCESS=$((node script.js && echo 'ok') || echo 'fail')
This line always returns successfully, even if node returns a non-zero exit code. After this runs, SUCCESS will be set to either ok or fail, depending on the result - you can replace that with whatever logic you'd prefer.
Print output as the last line from your comand, and use node script.js | tail -n1 to collect only the last line of the output, and ignore the rest of the logging
I would recommend 1 if you can, but if you really need to avoid using status codes, or you need more complex output, you may want to go for option 2.

Related

Process.stdout.write appends "true" to the console output

Using process process.stdout.write to print to the console returns the string with true appended to the end
process.stdout.write is appending true to the value I want to print to the console.
I tried running the same command using node in the cli - same result
Command: node -p "process.stdout.write('hello')"
Console: hellotrue
I expected hello to appear.
I think you need to take another look at your code. some of the common advice for programming forums is to get an example simplified enough that your problem still occurs.
In this case if I write process.stdout.write('hello') to a file, and then ask node to run that file, I get the correct response of hello back, the word true doesn't show up.
According to the options from node, using -p instead of -e means you're asking it to print the result of that function call, which was a success.
-e, --eval script evaluate script
-p, --print evaluate script and print result
For now try the -e flag with your code.

Why does behavior of set -e in a function change when that function is called in a compound command w/ || or &&?

I narrowed my problem to a simple example which puzzles me.
I have tested it with GNU bash 4.2.46 on Centos and 4.3.46 on Ubuntu.
Here is a bash function that returns non-zero (error) return code when called alone but reverses its behavior when I use either && or || to chain another command. It looks like a bug to me. Can someone explain why it is behaving as such?
$ echo $0
/bin/bash
$ function TEST() {( set -e; set -o pipefail; echo OK; false; echo NOT REACHED; )}
$ type TEST
TEST is a function
TEST ()
{
( set -e;
set -o pipefail;
echo OK;
false;
echo NOT REACHED )
}
$ TEST
OK
$ echo $?
1
$ TEST || echo "NON ZERO"
OK
NOT REACHED
$ echo $?
0
$ TEST && echo "UNEXPECTED"
OK
NOT REACHED
UNEXPECTED
$ echo $?
0
What you are seeing is the shell doing what it is specified to do. Non-zero return codes in if statements and loops, and || && logical operators do not trigger detection by set -e or traps. This makes serious error handling more difficult than in other languages.
The root of all problems is that, in the shell, there is no difference between returning a non-zero code as a meaningful and intended status, or as the result of a command failing in an uncontrolled manner. Furthermore the special cases the shell has will disable checking at all depths in the call stack, not just the first one, entirely hiding nested failures from set -e and traps (this is pure evil if you ask me).
Here is a short example that shows what I mean.
#!/bin/bash
nested_function()
{
returnn 0 ; # Voluntarily misspelled
}
test_function()
{
if
[[ some_test ]]
then
nested_function
else
return 1
fi
}
set -e
trap 'echo Will never be called' ERR
if
test_function
then
echo "Test OK"
else
echo "Test failed"
fi
There is an obvious bug in the first function. This function contains nothing that disables error checking, but since it is nested inside an if block (and not even directly, mind you), that error is completely ignored.
You do not have that problem in, say, Java, where a return value is one thing, and an exception is another thing, and where evaluating a return value in an if statement will not prevent an exception at any level in the call stack from doing its job. You have try/catch to handle exceptions, and there is no way to mix exceptions with return codes, they are fundamentally different things (exceptions can be used as return values, but do not trigger the exception mechanism then as when thrown).
If you want to have the same thing in shell programming, you have to build it for yourself. It can be done using a "try" function that is used in front of all calls and keeps state for each nested call, a "throw" equivalent that allows exceptions to be thrown (not as non-zero return codes, but stored inside variables), and trap ... ERR to intercept non-zero return codes and be able to do things like generate a stack trace and trigger a controlled exit (e.g. deleting temporary files, releasing other resources, performing notifications).
With this approach, "exceptions" are explicitly handled failures, and non-zero return codes are bugs. You trade a bit of performance I guess, it is not trivial to implement, and it requires a lot of discipline. In terms of ease of debugging and the level of complexity you can build in your script without being overwhelmed when trying to trace the source of a problem, it is a game changer though.
Handling error codes is the intended behavior of || and &&.
set -e is a great practice in Bash scripting to alert you to any unwanted errors. When using it sometime to chain commands like
set -e
possibly_failing_command || true
echo "This is always reached"
in order to avoid the program stopping.

Accessing the value returned by a shell script in the parent script

I am trying to access a string returned by a shell script which was called from a parent shell script. Something like this:
ex.sh:
echo "Hemanth"
ex2.sh:
sh ex.sh
if [ $? == "Hemanth" ]; then
echo "Hurray!!"
else
echo "Sorry Bro!"
fi
Is there a way to do this? Any help would be appreciated.
Thank you.
Use a command substitution syntax on ex2.sh
valueFromOtherScript="$(sh ex.sh)"
printf "%s\n" "$valueFromOtherScript"
echo by default outputs a new-line character after the string passed, if you don't need it in the above variable use printf as
printf "Hemanth"
on first script. Also worth adding $? will return only the exit code of the last executed command. Its values are interpreted as 0 being a successful run and a non-zero on failure. It will NEVER have a string value as you tried to use.
A Bash script does not really "return" a string. What you want to do is capture the output of a script (or external program, or function, they all act the same in this respect).
Command substitution is a common way to capture output.
captured_output="$(sh ex.sh)"
This initializes variable captured_output with the string containing all that is output by ex.sh. Well, not exactly all. Any script (or command, or function) actually has two output channels, usually called "standard out" (file descriptor number 1) and "standard error" (file descriptor number 2). When executing from a terminal, both typically end up on the screen. But they can be handled separately if needed.
For instance, if you want to capture really all output (including error messages), you would add a "redirection" after your command that tells the shell you want standard error to go to the same place as standard out.
captured_output="$(sh ex.sh 2>&1)"
If you omit that redirection, and the script outputs something on standard error, then this will still show on screen, and will not be captured.
Another way to capture output is sending it to a file, and then read back that file to a variable, like this :
sh ex.sh > output_file.log
captured_output="$(<output_file.log)"
A script (or external program, or function) does have something called a return code, which is an integer. By convention, a value of 0 means "success", and any other value indicates abnormal execution (but not necessarily failure) : the meaning of that return code is not standardized, it is ultimately specific to each script, program or function.
This return code is available in the $? special shell variable immediately after the execution terminates.
sh ex.sh
return_code=$?
echo "Return code is $return_code"

Generate specific, non-zero return code?

I am working on some piece of python code that calls various linux tools (like ssh) for automation purposes. Right now I am looking into "return code" handling.
Thus: I am looking for a simple way to run some command that gives me a specific non-zero return code; something like
echo "this is a testcommand, that should return with rc=5"
for example. But of course, the above comes back with rc=0.
I know that I can call false, but this will always return with rc=1. I am looking for something that gives me an rc that I can control.
Edit: first answers suggest to exit; but the problem with that: exit is a bash function. So, when I try to run that from within a python script, I get "No such file or directory: exit".
So, I am actually looking for some "binary" tool that gives me that (obviously one can write some simple script to get that; I am just looking if there is something similar to false that is already shipped with any Linux/Unix).
Run exit in a subshell.
$ (exit 5) ; echo $?
5
I have this function defined in .bashrc:
return_errorcode ()
{
return $1
}
So, I can directly use something like
$ return_errorcode 5
$ echo $?
5
Compared to (exit 5); echo $? option, this mechanism saves you a subshell.
This is not exactly what you are asking but custom rc can be achieved through exit command.
echo "this is a test command, that should return with " ;exit 5
echo $?
5

How to run a script using diff for a command?

I'm writing a script to test a program and I'm getting caught at this portion
if (("$x"==22)); then
echo "Checking for whether wrong input file is detected."
if diff ${arr[$x]} <(./compare ); then
echo Output is as expected.
else
echo Output is not as expected. Check for errors.
fi
else
if diff -q ${arr[$x]} <(./compare $i); then
echo Output is as expected.
else
echo Output is not as expected. Check for errors.
fi
fi
So what it's doing is testing my program against known output. However, for the case where I use ./compare without an argument, I want to receive an error message from my program specifying that the argument is missing. The test file it's using, "22" let's call it result22.txt, has the exact same output my program would give from just running ./compare (no arguments). However, when I run it using the script, it says that result22.txt differs from just running ./compare. I'm pretty sure I"m running the script wrong, any ideas?
Additionaly information, i is a known input test file that's from an array, x is an incremental variable to count which loop we're on. So arr[$x] is just accessing the nth file from the known output files.
Compare is my own compare program to run.

Resources