curl works, but won't execute in BASH script - bash

The following curl command works from the command line. I get a valid response from the server.
curl -X POST https://somebaseurl/api/v1/auth/login -H "Content-Type:application/json" -d '{"email": "foo#bar,com", "password": "foo"}'
However I am trying to write a BASH script like this
baseUrl=https://somebaseurl
contentTypeJson="\"Content-Type:application/json\""
credentials="'{\"email\": \"foo#bar.com",\"password\": \"foo\"}'"
login="curl -X POST $baseUrl/api/v1/auth/login -H $contentTypeJson -d $credentials"
echo ${login}
response=`${login}`
echo ${response}
I get a bad request response from the server. However if I copy the echoed curl command directly into my terminal it works. What am I doing wrong?
edit:
As requested I get
Bad Request For request 'POST api/v1/auth/login' [Expected application/json]

Bash and cURL can be quite particular how quotes are used within a script. If the escaping gets thrown off then everything else can easily fail. Running the script through shellcheck.net is often very helpful in identifying such issues. Below is a revised version of the script after fixing based upon the suggestions:
#!/bin/bash
baseUrl="https://somebaseurl/api/v1/auth/login"
contentTypeJson="Content-Type:application/json"
credentials="{\"email\": \"foo#bar.com\", \"password\": \"foo\"}"
login="$(curl -X POST "$baseUrl" -H "$contentTypeJson" -d "$credentials")"
echo "${login}"
response="${login}"
echo "${response}"

Executing with backticks interprets the command only as a sequence of words, and doesn't treat quotes specially. To have the shell interpret quotes as if they were interactively typed, use eval ${login} instead.
As an aside, bash has a -x option which will show you commands as they are being executed (run your script with bash -x script.sh instead of bash script.sh or ./script.sh). This will show you the commands correctly quoted, and is more helpful than printing them out using echo.

Related

Why does curl not return a value in bash script?

My task is very simple - nevertheless I have been sitting already for hours and have no idea why it doesn't work.
In linux bash script I want to get the result of a webservice call with curl. I'm not interested in the content, only the status code:
#!/bin/bash
set -euo pipefail # put bash in "strict mode"
echo "Before"
response2=$(curl -o /dev/null -s -w '%{http_code}' -u username:password-X POST https://xxxxx.yyyyy.at:8081/MyPath/service/rest/crypto/encrypt -H 'Content-Type: application/json' -d '{"deviceId": "ABCD","payload": "ABCD"}')
echo "after"
It works when there is a valid request
Before...
200
Also, when the path of the service is wrong, it gives http error code
Before...
500
But when the host is wrong (not existent hostname) I get
Before...
and the script terminates (although the call is from a looping menue).
Why is this the case?
The manual call of curl with same parameters gives
000
as output, so why this output is not displayed in my script?
A reproducable example is (server name not existing):
#!/bin/bash
set -euo pipefail
#- Check kms
f_check_kms(){
echo "Before..."
response2=$(curl -o /dev/null -s -w '%{http_code}' -u user:xxxx -X POST https://xxxx.xxx.intra.graz.at:8081/ATM-KeyManagement-REST-Service/service/rest/crypto/encryptUCast -H 'Content-Type: application/json' -d '{"deviceId": "SAG0530000016261", "encryptionSuite": "DLMS_SUITE_0", "securityMode": "AUTHENT_AND_ENCRYPT", "roleId": "001","initialVector": "4D4D4D0000BC614E01234567","payload": "ABCD","usedGuek":"NO","usedGak":"NO"}')
echo "$response2"
}
f_check_kms
You're running your script with set -e to make the shell interpreter exit when any¹ unchecked² command exits with a nonzero status, and when you provide an invalid hostname, curl exits with a nonzero exit status.
Because you're passing -s for silent mode, it doesn't print any error messages about this (you asked it not to!). It does still print the http_code you asked for, but because the script exits, the echo "after" is never reached, and whatever other code you're relying on to print the contents of the response2 variable is likewise never reached.
Suppressing this is as simple as adding a conditional to the end, like the || : sequence below:
response2=$(curl -o /dev/null -s -w '%{http_code}' -u username:password \
-X POST https://xxxxx.yyyyy.at:8081/MyPath/service/rest/crypto/encrypt \
-H 'Content-Type: application/json' \
-d '{"deviceId": "ABCD","payload": "ABCD"}' \
) || : "Ignoring exit status of $?"
You'll be able to see that message when running your script in trace mode (set -x / bash -x yourscript), but it'll be otherwise invisible, and because || is branching on curl's exit status, this marks curl as "checked" so set -e won't decide to exit based on its exit status.
¹ Not really true: set -e has a bunch of exceptions it doesn't exit over, and those exceptions change between individual shell releases.
² This is a very unintuitively-defined word: For example, when you check the exit status of a function, everything that function calls can become "checked", so set -e's behavior is extremely context-sensitive and hard to predict; what's checked when a function is called one time might not be checked when it's called again later.

bash script from docker does not work as expected if statement [duplicate]

This question already has answers here:
Difference between sh and Bash
(11 answers)
Pattern matching in UNIX Case statement
(1 answer)
Closed 1 year ago.
I am using this image which has bash v4.3.48 and curl v7.56.1:
https://hub.docker.com/r/bizongroup/alpine-curl-bash/tags?page=1&ordering=last_updated
Inside the docker I write the following script:
email_dest="iz#gmail.com}}"
suffix="#gmail.com"
dm_to=${email_dest%"$suffix"}
if [[ $email_dest == *"#users.noreply.github.com"* ]]
then
echo "Email address is no reply. Please fix your email preferences in Github"
elif [[ $email_dest == *$suffix* ]]
then
curl -X POST -H 'Content-type: application/json' --data '{"text":"Hello <#'"$dm_to"'>. '{{inputs.parameters.workflow_name}}' "}' https://hooks.slack.com/services/T01JNE5DXA7/B0246T84N75/hHDk7RUg2BWl2bYbPoN9r
else
echo "Email address is not of digibank domain!"
fi
If I run this script with bash command <script_name> it will work as expected (Run the curl command). But if I run it with sh command <script_name> it will not run the curl command:
/ # bash send-message.sh
ok/ #
/ # sh send-message.sh
Email address is not of digibank domain!
Any suggestion of what it could be? and what should be changed so it will work with sh?
That lies within the differences between bash and sh:
sh is POSIX compliant, whereas bash isn't (fully).
As a best practice you should always include a shebang:
#!/usr/bin/env bash
echo "this is going to run within bash"
With this you can now omit calling the script via bash myscript and just call it with ./myscript and it is always going to use bash (even if you are in a zsh, sh or whatever else).
However, if you truly want to have a script that runs with both sh and bash then you should rewrite your script to be plain sh compliant (i.e. POSIX).
TL;DR
Any suggestion of what it could be? and what should be changed so it will work with sh?
In your script you are using bash extensions such as [[ which is why it does not work with sh.
Checkout the links I posted above for more differences and how you can "convert" your bash script into a sh script.
The following site has a great summary on what to change in order to get your bash script working for dash which is an implementation of sh: http://mywiki.wooledge.org/Bashism
Furthermore, you can also check if any issues exist by using the following site: https://www.shellcheck.net/

shell command line to run a script N times

I'm trying to run a curl command for the following numbers but it doesn't apply
for i in {2,51,52,53,54}; do curl -v -X PUT http://localhost:8198/v3/progress/i/?status=Open; done
anything going wrong ?
In bash you can use variables with $. So i should be $i
This should make it work:
for i in {2,51,52,53,54}; do curl -v -X PUT http://localhost:8198/v3/progress/$i/?status=Open; done

bash read is being skipped when run from curl pipe

I'm building a bootstrap for a github project and would like it to be a simple one-liner. The script requires a password input.
This works and stops the script to wait for an input:
curl -s https://raw.github.com/willfarrell/.vhosts/master/setup.sh -o setup.sh
bash setup.sh
This does not, and just skips over the input request:
curl -s https://raw.github.com/willfarrell/.vhosts/master/setup.sh | bash
setup.sh contains code is something like:
# code before
read -p "Password:" -s password
# code after
Is it possible to have a clean one-liner? If so, how might one do it?
Workaround:
Use three commands instead of piping output.
curl -s https://raw.github.com/willfarrell/.vhosts/master/setup.sh -o vhosts.sh && bash vhosts.sh && rm vhosts.sh
I had the same exact problem as the OP and was looking for an answer. This question was one of the first hits on Google for me and since it doesn't have a real answer yet, here's the command that I eventually stumbled upon which solved my need of using read in a remote script.
bash <(curl -s https://example.com/my-bash-script.sh)
With the pipe, the read reads from standard input (the pipe), but the shell already read all the standard input so there isn't anything for the read to read.

How do I use a variable in the --data section of a curl command?

I am writing a bash script to call curl from the command line. I sadly cannot figure out how to substitute a simple variable into the -d section of the curl request.
Why doesn't this work?
#!/bin/sh
name=$1
test -z $name && echo "Repo name required." 1>&2 && exit 1
curl -u 'metaraine' https://api.github.com/user/repos -d '{"name":"$name"}'
It doesn't actually substitute the value of $name into the data.
What about this?
curl -u 'metaraine' https://api.github.com/user/repos -d "{\"name\":\"$name\"}"
That is, escape the quotes and use double quotes around the {} instead of simple ones.

Resources