Any help is appreciated:
I am running a simple java currency converter application and trying to test it using conditional bash statements. For this test I am seeking to test when the user doesn't enter any value into the application what the resulting output is. Here is the bash script:
#!/bin/bash
input=""
expectedOuput="Please enter correct input"
actualOutput=$(java CurrencyConverter $input)
if [ $expectedOutput == $actualOutput ]; then
echo "Test 1 Passed"
else
echo "Test 1 Failed"
fi
I am aiming for the test to fail and echo this, however the script just returns the java error instead of echoing, like this:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
at CurrencyConverter.main(CurrencyConverter.java:12)
I am newer to bash scripting so have been changing the syntax, I just cant seem to stop the java error being printed
The exception message is printed to stderr, so if you want to get it in actualOutput (perhaps misleading name now):
actualOutput=$(java CurrencyConverter "$input" 2>&1)
you can also check the exit status of the java command.
Also check misspelled expectedOuput and use of quotes.
Use https://www.shellcheck.net/.
And the ArrayIndexOutOfBoundsException it's definitely something you should fix in your java code.
Related
I have a bash script that has set -ex, which means the running script will exit once any command in it hits an error.
My use case is that there's a subcommand in this script for which I want to catch its error, instead of making the running script shutdown.
E.g., here's myscript.sh
#!/bin/bash
set -ex
# sudo code here
error = $( some command )
if [[ -n $error ]] ; then
#do something
fi
Any idea how I can achieve this?
You can override the output of a single command
this_will_fail || true
Or for an entire block of code
set +e
this_will_fail
set -e
Beware, however, that if you decide you don't want to use set -e in the script anymore this won't work.
If you want to handle a particular command's error status yourself, you can use as the condition in an if statement:
if ! some command; then
echo 'An error occurred!' >&2
# handle error here
fi
Since the command is part of a condition, it won't exit on error. Note that other than the ! (which negates it, so the then clause will run if the command fails rather than it succeeds), you just include the command directly in the if statement (no brackets, parentheses, etc).
BTW, in your pseudocode example, you seem to be treating it as an error if the command produces any output; usually that's not what you want, and I'm assuming you actually want to test the exit status to detect errors.
For currently unknown reasons, one of our bash-scripts produces "Cannot fork" errors when running a simple line like:
myvar=`mycmd || echo "error"; exit 2`
Obviously the problem is that no new process can be created (forked) so that command fails.
However bash just ignores the error and continues in the script which caused unexpected problems.
As you can see, we already check for errors in the command itself, but the "Cannot fork" error appears before the command is even run.
Is there a way to catch that error and stop bash from execution?
There are actually several problems with this error check, that'll prevent it from properly handing any error, not just "Cannot fork" errors.
The first problem is that || has higher precedence than ;, so mycmd || echo "error"; exit 2 will run echo "error" only if mycmd fails, but it'll run exit 2 unconditionally, whether or not mycmd succeeds or fails.
In order to fix this, you should group the error handling commands with { }. Something like: mycmd || { echo "error"; exit 2; }. Note that a ; or line break before } is required, or the } will be treated as an argument to exit.
(BTW, I sometimes see this shorthanded as mycmd || echo "error" && exit 2. Don't do this. If the echo fails for some weird reason, it won't run the exit.)
Also all of this, the echo and the exit, is run in the subshell created by the backticks (or would be, if that subshell had forked successfully). That means the error message will get saved in myvar rather than printed (error messages should generally be sent to standard error, e.g. echo "error" >&2); and more importantly it'll be the subshell that exits, not the shell that's running the script. The main script will note that the subshell exited with an error... and blithely keep running. (Well, unless you have -e set, but that's a whole other ball of potential bugs.)
The solution to that is to put the || stuff outside the backticks (or `$( ), since that's generally preferred over backticks). That way it happens in the main shell, that's what prints the error, that's what exits if there's an error, etc. This should also solve the "Cannot fork" problem, although I haven't tested it.
So, with all these corrections, it should look something like this:
myvar=$(mycmd) || {
echo "error" >&2
exit 2
}
Oh, and as Charles Duffy pointed out in a comment, if you use something like local myvar=$(mycmd) or export myvar=$(mycmd), the local/export/whatever command will override the exit status from mycmd. If you need to do that, set the variable's properties separately from its value:
local myvar
myvar=$(mycmd) || {
...
I'm new here, so I apologize in advance if I'm not following the protocol, but the message said to ask a new question. I had asked an earlier question: How can a bash script try to load one module file and if that fails, load another?, but it is not a duplicate of Bash conditional based on exit code of command as marked.
The reason is that the module load does not return a non-zero exit code if it fails to load. These are the Environment Modules that I am trying to use.
For example,
#!/bin/bash
if module load fake_module; then
echo "Should never have gotten here"
else
echo "This is what I should see."
fi
results in
ModuleCmd_Load.c(213):ERROR:105: Unable to locate a modulefile for 'fake_module'
Should never have gotten here
How can I attempt to load fake_module and if that fails attempt to do something else? This is specifically in a bash script. Thank you!
Edit: I want to be clear that I don't have the ability to modify the module files directly.
Use the command output/error instead of its return value, and check for the keyword ERROR matches your output/error
#!/bin/bash
RES=$( { module load fake_module; } 2>&1 )
if [[ "$RES" != *"ERROR"* ]]; then
echo "Should never have gotten here" # the command has no errors
else
echo "This is what I should see." # the command has an error
fi
Old versions of Modules, like version 3.2 you use, always return 0 whether it has failed or it is successful. With this version you have to parse output as proposed by #franzisk. Modules returns its output on stderr (as stdout is used to trap environment changes to apply)
If you do not want to rely on error messages, you can list loaded modules after the module load command with module list command. If module is not found in module list command output it means module load attempt failed.
module load fake_module
if [[ "`module list -t 2>&1`" = *"fake_module"* ]]; then
echo "Should never have gotten here" # the command has no errors
else
echo "This is what I should see." # the command has an error
fi
Newer versions of Modules (>= 4.0) now return an appropriate exit code. So your initial example will work this these newer versions.
I have a bash script I want to self-destruct on execution. So far it works great but I'd like some final check that if no errors have occurred (any output to stderr), then go ahead and self destruct. Otherwise, I'd like to leave the script in tact. I have the code for everything except the error check. Not sure if I can just output err to a file and check if file is empty. I'm sure it's a simple solution.
How could I do this?
Thanks for any help.
You can try this out. $? contains the return code for the process last executed by command. Moreover standard nix derivatives demarcate 0 as (no error) and 1 - 255 as some kind of errors that happened. Note that this will report errors that do not necessarily have any stderr output.
command
if [ "$?" -ne 0 ]; then
echo "command failed";
# your termination logic here
exit 1;
fi
Assuming that your script returns the value 0 on success, a value from 1 to 255 if an error occur you can use the following command
if /path/to/myscript; then
echo success
else
echo failed
fi
you can also use the following (shorter) command
[[ /path/to/myscript ]] && echo success || echo failed
Sorry I cannot give a clear title for what's happening but here is the simplified problem code.
#!/bin/bash
# get the absolute path of .conf directory
get_conf_dir() {
local path=$(some_command) || { echo "please install some_command first."; exit 100; }
echo "$path"
}
# process the configuration
read_conf() {
local conf_path="$(get_conf_dir)/foo.conf"
[ -r "$conf_path" ] || { echo "conf file not found"; exit 200; }
# more code ...
}
read_conf
So basically here what I am trying to do is, reading a simple configuration file in bash script, and I have some trouble in error handling.
The some_command is a command which comes from a 3rd party library (i.e. greadlink from coreutils), required for obtain the path.
When running the code above, I expect it outputs "command not found" because that's where the FIRST error occurs, but actually it always prints "conf file not found".
I am very confused about such behavior, and I think BASH probably intent to handle thing like this but I don't know why. And most importantly, how to fix it?
Any idea would be greatly appreciated.
Do you see your please install some_command first message anywhere? Is it in $conf_path from the local conf_path="$(get_conf_dir)/foo.conf" line? Do you have a $conf_path value of please install some_command first/foo.conf? Which then fails the -r test?
No, you don't. (But feel free to echo the value of $conf_path in that exit 200 block to confirm this fact.) (Also Error messages should, in general, get sent to standard error and not standard output anyway. So they should be echo "..." 2>&1. That way they don't be caught by the normal command substitution at all.)
The reason you don't is because that exit 100 block is never happening.
You can see this with set -x at the top of your script also. Go try it.
See what I mean?
The reason it isn't happening is that the failure return of some_command is being swallowed by the local path=$(some_command) assignment statement.
Try running this command:
f() { local a=$(false); echo "Returned: $?"; }; f
Do you expect to see Returned: 1? You might but you won't see that.
What you will see is Returned: 0.
Now try either of these versions:
f() { a=$(false); echo "Returned: $?"; }; f
f() { local a; a=$(false); echo "Returned: $?"; }; f
Get the output you expected in the first place?
Right. local and export and declare and typeset are statements on their own. They have their own return values. They ignore (and replace) the return value of the commands that execute in their contexts.
The solution to your problem is to split the local path and path=$(some_command) statements.
http://www.shellcheck.net/ catches this (and many other common errors). You should make it your friend.
In addition to the above (if you've managed to follow along this far) even with the changes mentioned so far your exit 100 won't exit the main script since it will only exit the sub-shell spawned by the command substitution in the assignment.
If you want that exit 100 to exit your script then you either need to notice and re-exit with it (check for get_conf_dir failure after the conf_path assignment and exit with the previous exit code) or drop the get_conf_dir function itself and just do that inline in read_conf.