0

I am trying to set a shell variable and pass it into a command as part of a Shell script with the following code:

SLS_DEBUG_TEXT=""
if [ "$ENABLE_DEBUG_LOGGING" = "true" ]; then
  SLS_DEBUG_TEXT="SLS_DEBUG=* "
fi

$SLS_DEBUG_TEXT sls create_domain -v $AWS_PROFILE_FLAG --stage $STAGE // gives error
#SLS_DEBUG=* sls create_domain -v $AWS_PROFILE_FLAG --stage $STAGE // works correctly

The error I'm getting is:

./sls-deploy.sh: line 110: SLS_DEBUG_TEXT: command not found

The if statement is mostly irrelevant but I want to show that this variable needs to be set conditionally (i.e. I don't always want the debug logging enabled). The last line in the code snippet (which is commented out) is what the command should look like if debug logging is enabled and this works correctly.

I have found a few previous posts on here and Stack Overflow where the issue was due to spaces between the equals sign when setting a variable (example) but I don't think I've fallen into that trap here.

Thanks for your help.

5

$SLS_DEBUG_TEXT is expanded too late, after the stage where the shell would otherwise treat its value as an assignment. The variable's value is therefore instead treated as a command.

What you could do instead is to use env:

SLS_DEBUG_TEXT='SLS_DEBUG=*'
env "$SLS_DEBUG_TEXT" sls create_domain -v "$AWS_PROFILE_FLAG" --stage "$STAGE"

Note the quoting around every single variable expansion.

If SLS_DEBUG_TEXT may be empty or unset, the above would generate an error since env would try to execute a command with no name.

To work around that, you may instead use

SLS_DEBUG_TEXT='SLS_DEBUG=*'
env ${SLS_DEBUG_TEXT:+"$SLS_DEBUG_TEXT"} sls create_domain -v "$AWS_PROFILE_FLAG" --stage "$STAGE"

The ${SLS_DEBUG_TEXT:+"$SLS_DEBUG_TEXT"} would expand to "$SLS_DEBUG_TEXT" if that variable is set and non-empty. Otherwise it would expand to nothing (not even an empty string, which would be the case with "$SLS_DEBUG_TEXT").

Note too that if you have SLS_DEBUG_TEXT='SLS_DEBUG=* ' (as in your code), the space after the * would become part of the value of SLS_DEBUG in the sls process' environment. I don't know if this is intended or not.


I'm also noticing that the error message that you quote says

./sls-deploy.sh: line 110: SLS_DEBUG_TEXT: command not found

To me, this indicates that you are using the variable's name without $ in front of it at some point in the script. This may be totally unrelated to the main issue that you had. The code that you show would instead cause the error

./sls-deploy.sh: line NNN: SLS_DEBUG=*: command not found
  • 1
    What is the impact of using env? Does that set it just for the command I'm running or globally? This script calls Serverless a few times and I don't want to enable debug logging on them all. – Stuart Leyland-Cole Sep 2 '19 at 10:46
  • @StuartLeyland-Cole The env utility sets the value in its own environment, and then invokes the given command (sls) with the modified environment. The utility can not set the environment variable for the current shell. I.e., the variable will not be set "globally". – Kusalananda Sep 2 '19 at 10:54
  • env works great if SLS_DEBUG_TEXT is set to SLS_DEBUG=* but when it isn't I get env: : No such file or directory. Note that SLS_DEBUG_TEXT is only set if debug logging is enabled. Is there a way to make env work in this situation? – Stuart Leyland-Cole Sep 2 '19 at 11:26
  • @StuartLeyland-Cole Yes, use env ${SLS_DEBUG_TEXT:+"$SLS_DEBUG_TEXT"} sls ... I will add this to the answer with an explanation. – Kusalananda Sep 2 '19 at 11:27
  • 1
    There is a lot of helpful information in this answer so I'm marking it as the accepted answer even though I didn't exactly use your answer! In fact I created a function that checks the ENABLE_DEBUG_LOGGING variable as suggested in Michael Homer's answer as I repeat this logic several times. The text expansion, `${a:+"$a"}, in particular was very useful. Thanks for all your clarifications and edits too. – Stuart Leyland-Cole Sep 2 '19 at 16:00
4

The shell grammar recognises assignment words at the lexing stage, long before parameter expansion occurs. Although you can have a parameter expand to a command, you can't have one expand to an assignment — it's just treated as a command at that point, even though it's textually the same (it's like you wrote "SLS_DEBUG=*" sls ...).

This is an oddity of the shell command language parsing and evaluation. Certain kinds of behaviour are determined lexically when the code is first read in, and those behaviours can never be recovered later on even though things generally appear to work by textual substitution.

You can use SLS_DEBUG_TEXT="eval SLS_DEBUG='*' " and it will work, albeit with potential problems if the remainder of the command can ever have any shell metacharacters in it. Another option is to conditionally define a function instead:

sls_debug() { :; }
if [ "$ENABLE_DEBUG_LOGGING" = "true" ]; then
    sls_debug() {
        SLS_DEBUG='*' "$@"
    }
fi
sls_debug sls create_domain ...

Alternatively (and perhaps clearer) a function that just checks $ENABLE_DEBUG_LOGGING internally would work as well, or env "$SLS_DEBUG_TEXT" ... or eval "$SLS_DEBUG_TEXT" ... will also be explicit.

  • Thank you for your suggestion of creating a function which checks the ENABLE_DEBUG_LOGGING variable and adds the SLS_DEBUG=* text if required. This was the better solution specifically for me as I am making multiple calls to Serverless. I will add my function as a separate answer just for completeness. – Stuart Leyland-Cole Sep 2 '19 at 16:03
2

For completeness this is how I ended up solving my issue which was a combination of Kusalananda's answer and Michael Homer's answer.

function executeServerlessCommand() {
  if [ "$ENABLE_SLS_DEBUG_LOGGING" = "true" ]; then
    SLS_DEBUG='*' "$@"
  else
    "$@"
  fi
}

executeServerlessCommand sls create_domain -v ${AWS_PROFILE_FLAG:+"$AWS_PROFILE_FLAG"} --stage "$STAGE"

Some points of note:

  1. I created a function which takes the command to run, checks the ENABLE_SLS_DEBUG_LOGGING variable and prepends SLS_DEBUG=* to the command if true, otherwise executes the provided command
  2. I used the ${a:+"$a"} syntax for the AWS_PROFILE_FLAG variable which only adds the variable value if set. Previously this variable was not quoted properly.

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.