This question already has answers here:
-z option inside if condition in shell script
(3 answers)
Closed 2 years ago.
I am trying to create a Bash script to create an AWS EC2 instance.
Goal: improve my Bash scripting skills
In order to improve my bash scripting skills, I wanted to practice creating if statements in my script.
As shown above, I created the if statement to check if the variable new_ami is empty then to echo "Did not find it" but if it is not empty, it will echo "Found AMI".
Here is my script
ami=$(aws ec2 describe-images --owners self amazon --filters "Name=name, Values=amzn2-*.0-x86_64-gp2" "Name=state, Values=available" --profile XXXXXX --output json | jq '.Images | sort_by(.CreationDate) | last(.[]).ImageId')
new_ami=$(echo "${ami}" | sed 's/"//g')
echo $new_ami
if test -z "$new_ami"
then
echo "Found AMI"
else
echo "Did not find it"
fi
When I run my script, this is the result, I got
ami-0ce1e3f77cd41957e
Did not find it
I have a problem:
The script echoes the variable new_ami which shows that he variable is not empty but the if statement fails to echo "Found AMI" instead it echoes "Did not find it" which means that the variable new_ami is empty.
How can this happen?
Why is my if statement behaving like this?
how do I fix it?
Thank you for all the help
#davidonstack if test -z "$new_ami" returns false, as it check for if variable has length.
So when its -z returns true, then expression will be executed, if false else expression will be executed.
Check more information in man test
-z string True if the length of string is zero.
-n string True if the length of string is nonzero.
Try to use -n as below
ami=$(aws ec2 describe-images --owners self amazon --filters "Name=name, Values=amzn2-*.0-x86_64-gp2" "Name=state, Values=available" --profile XXXXXX --output json | jq '.Images | sort_by(.CreationDate) | last(.[]).ImageId')
new_ami=$(echo "${ami}" | sed 's/"//g')
echo $new_ami
if test -n "$new_ami"
then
echo "Found AMI"
else
echo "Did not find it"
fi
Related
I am trying to run a command using the AWS CLI and then set the output of that command to a variable, which was easy enough. I parsed the initial command with jq and the output was an array of the GroupNames which is exactly what I wanted. Now I am trying to run an if statement that says if $1 is in the list_groups: print the users in that group. My issue is two fold. I do not know if The output from the first command is readable because it is not in a specific format.
#!/bin/bash
list_groups=$(aws iam list-groups --output json | jq -r .'Groups[] | .GroupName' --output json)
echo $list_groups
if ${1} == $list_groups;
then
echo aws iam get-group --group-name $1 --output json
else
echo 'sorry'
fi
So I am trying to take the list_groups variable that I stored the output of that CLI command in and then with user input in $1 see if what I input is in that GroupName and if it is use that $1 to then print out the users inside that group show in the command below.
echo aws iam get-group --group-name $1 --output json
I am sorry as I cannot print my output here for the GroupNames I am getting back from the first command as it is sensitive but, here is an example.
Admin Users assets.dev assets.prod
As you can see there are no separating values and I am not sure if that is contributing to the issues.
If I wanted to run this command here is what I would enter
bash accounts_parse.sh Admin
The error I get is
command not found
Thank you all for your help and I will clarify if my explanation is sub par or more information is needed.
if ${1} == $list_groups;
This isn't how if statements in bash (and other POSIX shells) work.
The word that comes after if is a command to execute, and its exit status is evaluated, and if it's 0 the if-branch is taken.
That explains your "command not found" - it's trying to execute ${1} as a command, and you don't have an "Admin" command.
Use either
if [ "${1}" = "$list_groups" ]
or
if [[ "${1}" = "$list_groups" ]]
the latter is a bashism (with some improvements) and won't work in other otherwise compatible shells. "==" is also available as a bash extension but entirely equivalent to "=", so I'd advise to just use that.
What's important is that the [ is a command (typically a builtin, also accessible as "test"). It's not a special bit of syntax but a thing that is executed with arguments and returns a status. ([[ has some special handling and is more syntax-y, but conceptually it's still "a thing to execute")
You can use read builtin in bash:
read user _ < <(aws iam list-groups --output json | jq -r .'Groups[] | .GroupName' --output json)
if [[ $user == $1 ]]; then
echo aws iam get-group --group-name $1 --output json
else
echo 'sorry'
fi
read user _ will read first word from input into variable user and ignore remaining.
This question already has answers here:
"echo -n" prints "-n"
(11 answers)
Closed 3 years ago.
I have the following bash script and seems the -n flag is always interpreted as string in echo
echo Hello, who am I talking to?
read login_name
login_name=$(echo -n $login_name | base64)
echo $login_name | base64 --decode
How do i correct that, or is there any other better syntax for my script?
Better use more portable printf instead of echo -n and use read -p with correct quoting;
read -p 'Hello, who am I talking to? ' login_name
login_name=$(printf '%s' "$login_name" | base64)
printf '%s' "$login_name" | base64 --decode
echo
PS: Just to clarify that there is nothing special about using any flag inside $(...) so one can use it like this:
dt=$(date -u '+%Y/%m/%d')
echo "$dt"
This question already has answers here:
Why does shell ignore quoting characters in arguments passed to it through variables? [duplicate]
(3 answers)
Closed 4 years ago.
I am trying to use a variable to store grep's options:
#!/bin/sh
set -xe
GREP_OPTS="-e \"test this\""
echo "I want to test this." | grep $GREP_OPTS
Output:
+ GREP_OPTS=-e "test this"
+ echo I want to test this.
+ grep -e "test this"
grep: this": No such file or directory
how can I make this work?
Word-Splitting is occurring on your GREP_OPTS="-e \"test this\"" resulting in the command being
grep -e '"test' 'this"'
Resulting in the exact error:
grep: this": No such file or directory
(of course there is no file named "this\"")
See BashFAQ-50 - I'm trying to put a command in a variable, but the complex cases always fail.
In order to prevent word splitting use an array for options instead of trying to use a single variable, e.g.
#!/bin/sh
set -xe
GREP_OPTS=(-e "test this")
echo "I want to test this." | grep "${GREP_OPTS[#]}"
Example Use/Output
$ bash grepopts.sh
+ GREP_OPTS=(-e "test this")
+ echo 'I want to test this.'
+ grep -e 'test this'
I want to test this.
Let me know if you have further questions.
I'm running this command do get a value from the json;
addr=$(./xuez-cli getnetworkinfo | jq -r '.localaddresses[0].address')
and it works just fine.
BUT if this .localaddresses[0].address part empty or doesn't even exist, jq sets the addr variable as null like this; addr=null
and I want to check if the json is empty/null and run some other command instead of parsing it as null string.
I couldn't find a way to work this around. How can I do this?
Something useful I found for shell scripts was:
jq '.foo // empty'
Which returns the match if successful, and the empty string if unsuccessful. So in bash I use:
addr=$(./xuez-cli getnetworkinfo | jq -r '.localaddresses[0].address // empty')
if [[ ! -z "$addr" ]]; then
# do something
fi
Ref: https://github.com/stedolan/jq/issues/354#issuecomment-43147898
https://unix.stackexchange.com/questions/451479/jq-print-for-null-values
First, a note: There's nothing inherently wrong with addr=null; you can just test for it:
if [[ $addr = null ]]; then ...code here...; fi
The rest of this answer pretends the above were untrue. :)
There are two practices that are notable as improving ease of error handling for this case:
Using set -o pipefail will detect whether any part -- not just the last component -- of a shell pipeline fails.
Using jq -e will cause jq's exit status to reflect whether it returned content that was either false or null.
Thus:
set -o pipefail
if addr=$(./xuez-cli getnetworkinfo | jq -er '.localaddresses[0].address'); then
: "address retrieved successfully; this message is not logged unless set -x is active"
else
echo "Running other logic here"
fi
...goes to Running other logic here if either jq fails (and -e specifies that false and null shall be treated as failures), or if xuez-cli reports an unsuccessful exit status.
This question already has answers here:
Bash script prints "Command Not Found" on empty lines
(17 answers)
Closed 6 years ago.
I am trying to learn more shell scripting. The available shells on this machine are /bin/sh, /bin/csh, and /bin/tcsh with sh being default and used here. OS is FreeBSD 9.1-RELEASE.
My current project needs to check whether a process updated the database yesterday. The first two echoes are just there for the moment verifying the variables have what I think they do.
#!/bin/sh
lastcheck=$(mysql -h dbserver.mysite.com -u myuser -pmypass mydb -se "SELECT MAX(DATE_FORMAT(datetime_sent_to_fulfiller,'%Y%m%d')) FROM print_mailing_request;"|cut -f1)
yesterday=$(echo -e "$(TZ=GMT+30 date +%Y%m%d)\n$(TZ=GMT+20 date +%Y%m%d)" | grep -v $(date +%Y-%m-%d) | tail -1)
echo "previous day was $yesterday"
echo "we last checked on $lastcheck"
if [ "$lastcheck" -eq "$yesterday" ]; then
echo "cool"
else
echo "uncool"
fi;
One question is why the : not found: output is showing up and how do I prevent it?
Another question is why both 'cool' and 'uncool' are being echoed?
Last question is why 'else' is being echoed?
$ /bin/sh pmr.cron.sh
: not found:
previous day was 20160602
we last checked on 20160602
: not found:
: not found:
cool
: not found: else
uncool
: not found:
You have carriage returns in your script; that generates the "not found" messages and is probably why both branches of your if are getting generated.
Your dates are comparable as strings, no need to use -eq to compare them as numbers.