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
    Commented Mar 18, 2020 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
    Commented Mar 18, 2020 at 20:37

2 Answers 2

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
    Commented Mar 18, 2020 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[@]}")

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .