9

I'm writing a bash script with the AWS CLI and shellcheck is giving an error that I think is incorrect. I'd like to try to figure out why its carping.

Here's the code, and error message:

for server in $(${aws} ec2 describe-instances --query 'Reservations[].Instances[][].{Name: Tags[?Key==`Name`].Value[] | [0]}' --filters "Name=tag:Name,Values=${server_name}*" --output text);
                                                                                                                                                                            ^-- SC2016: Expressions don't expand in single quotes, use double quotes for that.

I can't get the code to line up correctly in the SO editor but the ^-- is pointing at the * in the code. This part:

"Name=tag:Name,Values=${server_name}*"

The error provides a link to ShellCheck documentation for reference but when I double check everything, it looks like I'm in compliance. :D

I am guessing that the * is throwing things off and I know that I can get around this by doing shellcheck -e SC2016 but I'm really wondering what might be causing shellcheck to carp.

Any ideas?

2
  • As Joseph said I think it's more likely that your output is line wrapping and it's pointing to * but above it is `Name`
    – jesse_b
    Mar 18 '20 at 20:30
  • @jesse_b yeah! when i shrunk the text of my terminal down way small, it lined up with the first quote on the 'Reservations... part of the code. thanks.
    – Michael J
    Mar 18 '20 at 20:37
12

It is a false positive, but it's not the one you think it is. It has nothing to do with the *, and didn't point there for me. It's upset about `Name` being inside of single quotes. For example, echo '`Name`' produces the same warning, because it thinks that you want the backticks to be evaluated, so it's warning you that they won't be.

1
  • 1
    ahhh indeed. if i quote that part of the command in double quotes, shellcheck complains that i should use $() notation for the Name command, which is really a part of a parameter and not a command.
    – Michael J
    Mar 18 '20 at 20:35
3

Not an answer, but a formatted comment:

Pedantically, you shouldn't be using a for loop, but a while read loop:

while IFS= read -r server; do
    : do stuff here
done < <(
    "$aws" ec2 describe-instances \
        --query 'Reservations[].Instances[][].{Name: Tags[?Key==`Name`].Value[] | [0]}' \
        --filters "Name=tag:Name,Values=${server_name}*" \
        --output text
)

for loops read whitespace-separated words, while loops read lines -- see http://mywiki.wooledge.org/BashFAQ/001

Alternately, use readarray to capture the output

readaray -t servers < <(
    "$aws" ec2 describe-instances \
        --query 'Reservations[].Instances[][].{Name: Tags[?Key==`Name`].Value[] | [0]}' \
        --filters "Name=tag:Name,Values=${server_name}*" \
        --output text
)

for server in "${servers}"; do ...; done

Lastly, for long and unreadable commands, storing the options in an array can improve readability:

opts=(
    --query 'Reservations[].Instances[][].{Name: Tags[?Key==`Name`].Value[] | [0]}' 
    --filters "Name=tag:Name,Values=${server_name}*"
    --output text
)

readarray -t servers < <("$aws" ec2 describe-instances "${opts[@]}")

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.