shell script check if command eval result is a specific value - linux

I have a small snippet here that is part of a shell-script used to call an API and retreive a response. I wish to check for an empty API response by seeing if the returned string is an empty square-brackets string [] - and if it is, do a break and abort execution.
Currently, the code looks like this:
cmd = 'some curl api request being formatted here'
eval $cmd
and this is what I'm currently trying
cmd = 'some curl api request being formatted here'
if
eval $cmd == '[]' then echo 'Empty response from API' | break
else continue
fi
Any suggestions for achieving this?

# Call curl and save response
_response="$(curl ...)"
# Check response
if [ "$_response" = '[]' ]; then
# Empty response
printf 'Empty response from API\n'
break
fi
# Continue ...

Related

Expected value: CLOSED, actual value: "CLOSED"

I am trying to create automated test and I do not understand why this happens, the error is:
Test case: Verify that circuit breaker has status CLOSED -> Test FAILED, EXPECTED VALUE: CLOSED, ACTUAL VALUE: "CLOSED", WILL ABORT
Why does it not compare Strings even if they are? I am new to bash scripting so this is probably something pretty obvious.
Function which is calling Spring Boot Actuator
My code looks like:
function testCircuitBreaker() {
echo "Start Circuit Breaker Test"
EXEC="docker run --rm -it --network=my-network alpine"
#Verify that circuit breaker is closed via health endpoint
assertEqual "CLOSED" "$($EXEC wget movie-composite:8080/actuator/health -qO - | jq .components.movieCircuitBreaker.details.state)" "Verify that circuit breaker has status CLOSED"
#Three slow calls to get TimeoutException
for ((n = 0; n < 3; n++)); do
assertCurl 500 "curl -k https://$HOST:$PORT/movie-composite/MOV_ID_REVS_RECS?delay=3 $AUTH -s"
message=$(echo $RESPONSE | jq -r .message)
assertEqual "Did not observe any item or terminal signal within 2000ms" "${message:0:57}"
done
}
Assertion Function:
function assertEqual() {
local expected=$1
local actual=$2
local message=$3
printf "Test case: $message -> "
if [ "$actual" = "$expected" ]; then
echo "Test OK (actual value: $actual)"
return 0
else
echo "Test FAILED, EXPECTED VALUE: $expected, ACTUAL VALUE: $actual, WILL ABORT"
return 1
fi
}
jq outputs well-formed JSON by default, so strings will be quoted. As an example:
$ jq .foo <<<'{"foo":"bar"}'
"bar"
Therefore your assertEqual "CLOSED" "$(... | jq .components.movieCircuitBreaker.details.state)" ... command is comparing CLOSED with "CLOSED" (note the extra quotes). You could quote the expected string, as #rtx13 suggests, but I think it's clearer to have jq output just the contents of the field. You can do that with the --raw-output flag:
With this option, if the filter's result is a string then it will be written directly to standard output rather than being formatted as a JSON string with quotes. This can be useful for making jq filters talk to non-JSON-based systems.
$ jq -r .foo <<<'{"foo":"bar"}'
bar
You could try preserving the quotes around CLOSED by changing:
assertEqual "CLOSED" "$($EXEC wget movie-composite:8080/actuator/health -qO - | jq .components.movieCircuitBreaker.details.state)" "Verify that circuit breaker has status CLOSED"
to
assertEqual '"CLOSED"' "$($EXEC wget movie-composite:8080/actuator/health -qO - | jq .components.movieCircuitBreaker.details.state)" "Verify that circuit breaker has status CLOSED"
The single quotes around "CLOSED" preserve the quotation marks when passed to the assertEqual function.

how to wget continue previous loop if url return 404 ? bash

i am trying to write a script to auto download some pictures online.
How can I break inside loop when k returns 404 and continue the previous loop ?
Here is my code:
for i in {1..50};
do
for k in {1..100};
do
wget http://www.magga.com/files?cat=$i&id=$k
done
thanks
The wget's output is sent to stderr including 404 error.
You can check if the output string contains 404 Not Found and then exit from the innermost loop:
#!/bin/bash
for i in {1..50}; do
for k in {1..100}; do
ret=$(wget http://www.magga.com/files?cat=${i}&id=${k} 2>&1)
if [[ "$ret" =~ 404\ Not\ Found ]]; then
break # exit from current "k" loop and continues with next "i"
fi
done
done
Hope this helps.
Instead of having wget write to file, store the output in a variable. For example:
check=$(wget -O - http://www.magga.com/files?cat=$i&id=$k)
stores the output in $check. If the length of $check is zero -- you can get the length via ${#check}, break the loop. Otherwise write $check to a file.

How to pass a Map Data Structure as an argument to a method in a Bash Script

I want to create a data structure Person like a Map and want to pass it to a function in bash script. In the method I want to retrieve “Person” like Person[Name] ,Person[Age] , Person [Dept] as Mark , 10 and Finance respectively. etc. But I am not able to get and getting the output as mentioned in the comment. Need some guidance here how to that or what I am doing wrong.
Here is the script
#!/bin/bash -e
getValue(){
local Person=$1
echo Person[Name]
}
Person[Name]=”Mark”
Person [Age]=”10”
Person [Dept]=”Finance”
echo ${Person[Name]} # why is it printing Finance.I am expecting it to be printed as Mark
getValue Person # output is coming as Person
getValue ${Person} # output is coming as Finance
getValue ${Person[#]} # output is coming as Finance
You have to define Person as an associative array.
Here is the running code if you are using bash version 4 or above.
#!/bin/bash -e
function getValue(){
person=$(declare -p "$1")
declare -A person_arr=${person#*=}
echo ${person_arr[Name]}
echo ${person_arr[Age]}
echo ${person_arr[Dept]}
}
declare -A Person
Person[Name]="X"
Person[Age]=10
Person[Dept]="Finance"
echo ${Person[Name]}
echo ${Person[Age]}
echo ${Person[Dept]}
getValue "Person"

How do I get the output of a shell command executed using into a variable from Jenkinsfile (groovy)?

I have something like this on a Jenkinsfile (Groovy) and I want to record the stdout and the exit code in a variable in order to use the information later.
sh "ls -l"
How can I do this, especially as it seems that you cannot really run any kind of groovy code inside the Jenkinsfile?
The latest version of the pipeline sh step allows you to do the following;
// Git committer email
GIT_COMMIT_EMAIL = sh (
script: 'git --no-pager show -s --format=\'%ae\'',
returnStdout: true
).trim()
echo "Git committer email: ${GIT_COMMIT_EMAIL}"
Another feature is the returnStatus option.
// Test commit message for flags
BUILD_FULL = sh (
script: "git log -1 --pretty=%B | grep '\\[jenkins-full]'",
returnStatus: true
) == 0
echo "Build full flag: ${BUILD_FULL}"
These options where added based on this issue.
See official documentation for the sh command.
For declarative pipelines (see comments), you need to wrap code into script step:
script {
GIT_COMMIT_EMAIL = sh (
script: 'git --no-pager show -s --format=\'%ae\'',
returnStdout: true
).trim()
echo "Git committer email: ${GIT_COMMIT_EMAIL}"
}
Current Pipeline version natively supports returnStdout and returnStatus, which make it possible to get output or status from sh/bat steps.
An example:
def ret = sh(script: 'uname', returnStdout: true)
println ret
An official documentation.
quick answer is this:
sh "ls -l > commandResult"
result = readFile('commandResult').trim()
I think there exist a feature request to be able to get the result of sh step, but as far as I know, currently there is no other option.
EDIT: JENKINS-26133
EDIT2: Not quite sure since what version, but sh/bat steps now can return the std output, simply:
def output = sh returnStdout: true, script: 'ls -l'
If you want to get the stdout AND know whether the command succeeded or not, just use returnStdout and wrap it in an exception handler:
scripted pipeline
try {
// Fails with non-zero exit if dir1 does not exist
def dir1 = sh(script:'ls -la dir1', returnStdout:true).trim()
} catch (Exception ex) {
println("Unable to read dir1: ${ex}")
}
output:
[Pipeline] sh
[Test-Pipeline] Running shell script
+ ls -la dir1
ls: cannot access dir1: No such file or directory
[Pipeline] echo
unable to read dir1: hudson.AbortException: script returned exit code 2
Unfortunately hudson.AbortException is missing any useful method to obtain that exit status, so if the actual value is required you'd need to parse it out of the message (ugh!)
Contrary to the Javadoc https://javadoc.jenkins-ci.org/hudson/AbortException.html the build is not failed when this exception is caught. It fails when it's not caught!
Update:
If you also want the STDERR output from the shell command, Jenkins unfortunately fails to properly support that common use-case. A 2017 ticket JENKINS-44930 is stuck in a state of opinionated ping-pong whilst making no progress towards a solution - please consider adding your upvote to it.
As to a solution now, there could be a couple of possible approaches:
a) Redirect STDERR to STDOUT 2>&1
- but it's then up to you to parse that out of the main output though, and you won't get the output if the command failed - because you're in the exception handler.
b) redirect STDERR to a temporary file (the name of which you prepare earlier) 2>filename (but remember to clean up the file afterwards) - ie. main code becomes:
def stderrfile = 'stderr.out'
try {
def dir1 = sh(script:"ls -la dir1 2>${stderrfile}", returnStdout:true).trim()
} catch (Exception ex) {
def errmsg = readFile(stderrfile)
println("Unable to read dir1: ${ex} - ${errmsg}")
}
c) Go the other way, set returnStatus=true instead, dispense with the exception handler and always capture output to a file, ie:
def outfile = 'stdout.out'
def status = sh(script:"ls -la dir1 >${outfile} 2>&1", returnStatus:true)
def output = readFile(outfile).trim()
if (status == 0) {
// output is directory listing from stdout
} else {
// output is error message from stderr
}
Caveat: the above code is Unix/Linux-specific - Windows requires completely different shell commands.
this is a sample case, which will make sense I believe!
node('master'){
stage('stage1'){
def commit = sh (returnStdout: true, script: '''echo hi
echo bye | grep -o "e"
date
echo lol''').split()
echo "${commit[-1]} "
}
}
For those who need to use the output in subsequent shell commands, rather than groovy, something like this example could be done:
stage('Show Files') {
environment {
MY_FILES = sh(script: 'cd mydir && ls -l', returnStdout: true)
}
steps {
sh '''
echo "$MY_FILES"
'''
}
}
I found the examples on code maven to be quite useful.
All the above method will work. but to use the var as env variable inside your code you need to export the var first.
script{
sh " 'shell command here' > command"
command_var = readFile('command').trim()
sh "export command_var=$command_var"
}
replace the shell command with the command of your choice. Now if you are using python code you can just specify os.getenv("command_var") that will return the output of the shell command executed previously.
How to read the shell variable in groovy / how to assign shell return value to groovy variable.
Requirement : Open a text file read the lines using shell and store the value in groovy and get the parameter for each line .
Here , is delimiter
Ex: releaseModule.txt
./APP_TSBASE/app/team/i-home/deployments/ip-cc.war/cs_workflowReport.jar,configurable-wf-report,94,23crb1,artifact
./APP_TSBASE/app/team/i-home/deployments/ip.war/cs_workflowReport.jar,configurable-temppweb-report,394,rvu3crb1,artifact
========================
Here want to get module name 2nd Parameter (configurable-wf-report) , build no 3rd Parameter (94), commit id 4th (23crb1)
def module = sh(script: """awk -F',' '{ print \$2 "," \$3 "," \$4 }' releaseModules.txt | sort -u """, returnStdout: true).trim()
echo module
List lines = module.split( '\n' ).findAll { !it.startsWith( ',' ) }
def buildid
def Modname
lines.each {
List det1 = it.split(',')
buildid=det1[1].trim()
Modname = det1[0].trim()
tag= det1[2].trim()
echo Modname
echo buildid
echo tag
}
If you don't have a single sh command but a block of sh commands, returnstdout wont work then.
I had a similar issue where I applied something which is not a clean way of doing this but eventually it worked and served the purpose.
Solution -
In the shell block , echo the value and add it into some file.
Outside the shell block and inside the script block , read this file ,trim it and assign it to any local/params/environment variable.
example -
steps {
script {
sh '''
echo $PATH>path.txt
// I am using '>' because I want to create a new file every time to get the newest value of PATH
'''
path = readFile(file: 'path.txt')
path = path.trim() //local groovy variable assignment
//One can assign these values to env and params as below -
env.PATH = path //if you want to assign it to env var
params.PATH = path //if you want to assign it to params var
}
}
Easiest way is use this way
my_var=`echo 2`
echo $my_var
output
: 2
note that is not simple single quote is back quote ( ` ).

Bash output limited to echo only

I am writing a bash script to handle by backups. I have created a message function controller that uses functions to handle email, log and output.
So the structure is as:
message_call(i, "This is the output")
Message Function
-> Pass to email function
--> Build email file
-> Pass to log function
--> Build log file
-> Pass to echo function (custom)
--> Format and echo input dependent on $1 as a switch and $2 as the output message
When I echo I want nice clean output that only consists of messages passed to the echo function, I can point all output /dev/null but I am struggling to limit all output except for the echo command.
Current output sample:
craig#ubuntu:~/backup/functions$ sudo ./echo_function.sh i test
+ SWITCH=i
+ INPUT=test
+ echo_function
+ echo_main
+ echo_controller i test
+ '[' i == i ']'
+ echo_info test
+ echo -e '\e[32m\e[1m[INFO]\e[0m test'
[INFO] test
+ echo test
test
+ '[' i == w ']'
+ '[' i == e ']'
Above I ran the echo function alone and the output I want is on line 10, all other output in the sample I don't want.
If you have the line set -x in your script, comment it out. If not, try adding set +x at the top of your script.
If you want to hide all the output from everything except what you're explicitly doing in your echo function you could do something like this:
exec 7>&1 # save a copy of current stdout
exec >/dev/null # redirect everyone else's stdout to /dev/null
ls # output goes to /dev/null
echo My Message >&7 # output goes to "old" stdout

Resources