How to best handle command that shows error in console but is returning exit 0 - bash

I'm running into an issue with a Jenkins Pipeline. As part of our deploy process there is bash script that runs to validate the deployment files and deploy to an environment. There is a specific command at the end that uses a vendor's cli tool to deploy to our environment. If there is an error in this command, it appears to still be returning exit 0 and the build does not deploy but it is showing the job completed successfully in Jenkins. I thought about making an if statement as something like this to make the job fail if there is an error:
if $myCommand | grep -q '*** ERROR ***' &> /dev/null
then
exit 1
fi
I do want the command to finish and deploy if an error is not found in this command. My question is would this work and/or is there a better way to do this?

That's a fine way to do it, but your example is not grepping stderr, it is only grepping stdout. You'll want:
if $myCommand 2>&1 | grep ...

Or you could capture the output using Command substitution and print that message, otherwise, yeah just grep -q
output=$("$myCommand")
if [[ $output = *'*** ERROR ***'* ]]; then
printf 'Uh oh, something went wrong!\n' >&2
printf '%s\n' "$output" >&2
exit 1
fi
Although this might work or any other answer on this post, still this is not a solution but just a band-aid, the proper solution is to fix the program/utility/command that does not properly give an exit status that you can act upon.

Related

Trying to exit main command from a piped grep condition

I'm struggling to find a good solution for what I'm trying to do.
So I have a CreateReactApp instance that is booted through a yarn run start:e2e. As soon as the output from that command has "Compiled successfully", I want to be able to run next command in the bash script.
Different things I tried:
if yarn run start:e2e | grep "Compiled successfully"; then
exit 0
fi
echo "THIS NEEDS TO RUN"
This does appear to stop the logs, but it does not run the next command.
yarn run start:e2e | while read -r line;
do
echo "$line"
if [[ "$line" == *"Compiled successfully!"* ]]; then
exit 0
fi
done
echo "THIS NEEDS TO RUN"
yarn run start:e2e | grep -q "Compiled successfully";
echo $?
echo "THIS NEEDS TO RUN"
I've read about the differences between pipes / process substitions, but don't see a practical implementation regarding my use case..
Can someone enlighten me on what I'm doing wrong?
Thanks in advance!
EDIT: Because I got multiple proposed solutions and none of those worked I'll maybe redefine my main problem a bit.
So the yarn run start:e2e boots op a react app, that has a sort of "watch" mode. So it keeps spewing out logs after the "Compiled successfully" part, when changes occur to the source code, typechecks, ....
After the React part is booted (so if the log Compiled succesfully is outputted) the logs do not matter anymore but the localhost:3000 (that the yarn compiles to) must remain active.
Then I run other commands after the yarn run to do some testing on the localhost:3000
So basically what I want to achieve in pseudo (the pipe stuff in command A is very abstract and may not even look like the correct solution but trying to explain thoroughly):
# command A
yarn run dev | cmd_to_watch_the_output "Compiled succesfully" | exit 0 -> localhost:3000 active but the shell is back in 'this' window
-> keep watching the output until Compiled succesfully occurs
-> If it occurs, then the logs does not matter anymore and I want to run command B
# command B
echo "I WANT TO SEE THIS LOG"
... do other stuff ...
I hope this clears it up a bit more :D
Thanks already for the propositions!
If you want yarn run to keep running even after Compiled successfully, you can't just pipe its stdout to another program that exits after that line: that stdout needs to have somewhere to go so yarn's future attempts to write logs don't fail or block.
#!/usr/bin/env bash
case $BASH_VERSION in
''|[0-3].*|4.[012].*) echo "Error: bash 4.3+ required" >&2; exit 1;;
esac
exec {yarn_fd}< <(yarn run); yarn_pid=$!
while IFS= read -r line <&$yarn_fd; do
printf '%s\n' "$line"
if [[ $line = *"Compiled successfully!"* ]]; then
break
fi
done
# start a background process that reads future stdout from `yarn run`
cat <&$yarn_fd >/dev/null & cat_pid=$!
# close the FD from that background process so `cat` has the only copy
exec {yarn_fd}<&-
echo "Doing other things here!"
echo "When ready to shut down yarn, kill $yarn_pid and $cat_pid"

Output redirection to console in shell script , not reflecting realtime

I have encountered a weird problem with console output when calling a subscript from inside another script.
Below is the Main Script which is calling a TestScript.
The TestScript is an installation script written in perl which takes some time to execute and prints messages as the installation progresses.
My problem here is that the output from the called perl script is only shown on the console once the installation is completed and the script returns.
Oddly i have used this kind of syntax successfully before for calling shell scripts and it works fine for them and output is shown simultaneously without waiting for the subscript to return.
I need to capture the output of the script so that i can grep if the installation was successful.
I do not control the perl script and cannot modify it in any way.
Any help would be greatly appreciated.
Thanks in advance.
#!/bin/sh
echo " Main script"
output=`/var/tmp/Packages/TestScript.pl | tee /dev/tty`
exitCode=$?
echo $output | grep -q "Installation completed successfully"
if [ $? -eq 0 ]; then
echo "Installation was successful"
fi
echo $exitCode

How can I prevent bash from reporting an error when attempting to call a non-existing script?

I am writing a simple script in bash to check whether or not a bunch of dependencies are installed on the current system. My script attempts to run a sample script with the -h flag, greps the output for a keyword i would expected to be returned by the sample scripts, and therefore knows whether or not the sample script is installed on the system.
I then pass this through a conditional statement that basically says sample scripts = OK or sample scripts = FAIL. However, in the case in which the sample script isn't installed on the system, bash throws the warning -bash: sample_script: command not found. How can I prevent this from displaying? I tried using the 1>&2 error redirection, but the warning still appears on the screen (I want the OK/FAIL output text to be displayed on the user's screen upon running my script).
Thanks for any suggestions!
If you just want to suppress errors (stderr) and let the "OK" or "FAIL" you are echoing (stdout) pass through, you would do:
./yourscript.sh 2> /dev/null
Although the better approach would be to test whether sample_script is executable before trying to execute it. For instance:
if [ -x "$script" ]; then
*do whatever generates FAIL or OK*
fi
#devnull dixit
command -h 2>/dev/null
I use this function to be independent of which, whence, type -p and whatnot:
pathto () {
DIRLIST=$(echo "$PATH"|tr : ' ')
for e in "$#"; do
for d in $DIRLIST; do
test -f "$d/$e" -a -x "$d/$e" && echo "$d/$e"
done
done
}
pathto script will echo the full path if it can be found (and is executable). Returning 0 or 1 instead left as an exercise :-)
for bash:
if ! type -P sample_script &> /dev/null; then
echo Error: sample_script is not installed. Come back later. >&2
exit 1
fi
sample_script "$#"

Not able to force exit on Jenkins Build

I've been having a lot of trouble with this so here goes.
I have a Jenkins build that executes the following shell script:
#!/bin/sh -x
if [ 'grep -c "It misses" log' -gt 0 ];
then exit 1;
fi
I know that the grep returns 1 when it finds something and technically Jenkins should mark the build as failed on a non-zero exit, but the jenkins still marks it as a success.
The console output for the jenkins build when running the script is:
Started by user bla
[project_name] $ /bin/sh -x /var/tmp/hudson41276.sh
+ [ grep -c "It misses" log -gt 0 ]
Finished: SUCCESS
Could anybody give me a hand and point out what I'm missing here?
Thanks,
CJ
If I understand right, you want the job to fail if "It misses" is not found in file "log". You can do this by not using the -c option of grep, just redirect the output like this:
grep "It misses" log > /dev/null
Grep will return 0 if it finds the phrase, and the job will succeed. If it does not find the phrase, grep will return 1, and the job will fail. If you want it the other way around (fail if it does find the phrase) just use grep -v. $? is your friend when you want to be sure of the exit status of a shell command.
Try this:
#!/bin/sh
set -e
grep -c "It misses" log
set -e: Exit at the first error.
grep -c 'arg': Exit 1 if nothing was grepped.
The problem is with your script, not Jenkins. The part of your script where you attempt to compare the exit code of grep looks like this:
if [ 'grep -c "It misses" log' -gt 0 ] ...
This will not even execute grep. In fact, it is simply comparing a string to a number.
You were probably attempting to do:
if [ `grep -c "It misses" log` -gt 0 ] ...
Note the use of backticks (`). Shell will execute grep and replace the backticks with the output of grep.
Bonus item: the condition in the if statement is actually a command that gets executed and its exit code determines where the execution will continue. So... why not use the grep command and it's useful exit code as the condition? (grep will exit with code 0 when it finds matches.)
if grep "It misses" log; then
exit 1
fi
It's shorter, much more readable and ever performs better because it does not need to execute so many commands.
Such a short if statement could even be replaced with a one-liner:
grep "It misses" log && exit 1
By default jenkins start shell with -e, so it exists at first error.
You could turn it off by
set +e
do failing task..

Catching errors in Bash with glassfish commands [return code in pipes]

I am writing a bash script to manage deployments to a GF server for several environments. What I would like to know is how can I get the result of a GF command and then determine whether to continue or exit.
For example
Say I want to redeploy, I have this script
$GF_ASADMIN --port $GF_PORT redeploy --name $EAR_FILE_NAME --keepstate=true $EAR_FILE | tee -a $LOG
The variables are already defined. So GF will start to redeploy and either suceed or fail. I want to check if it does and act accordingly. I have this right after it.
RC=$?
if [[ $RC -eq 0 ]];
then echoInfo "Application Successfully redeployed!" | tee -a $LOG;
else
echoError "Failed to redeploy application!"
exit 1
fi;
However, it doesnt really seem to work .
The problem is the pipe
$GF_ASADMIN ... | tee -a $LOG
$? reflects the return code of tee.
Your are looking for PIPESTATUS. See man bash:
PIPESTATUS
An array variable (see Arrays below) containing a list of exit
status values from the processes in the most-recently-executed
foreground pipeline (which may contain only a single command).
See also this example to clarify the PIPESTATUS
false | true
echo ${PIPESTATUS[#]}
Output is: 1 0
The corrected code is:
RC=${PIPESTATUS[0]}
Or try using a code block redirect, for example:
{
if "$GF_ASADMIN" --port $GF_PORT redeploy --name "$EAR_FILE_NAME" --keepstate=true "$EAR_FILE"
then
echo Info "Application Successfully redeployed!"
else
echo Error "Failed to redeploy application!" >&2
exit 1
fi
} | tee -a "$LOG"

Resources