Unable to Set Variable in Case Statement Bash

unable to set variable in case statement bash

You need to remove the $ sign in the assignments - INSTANCE_SIZE="m1.small". With the dollar sign, $INSTANCE_SIZE gets substituted with its value and no assignment takes place - bash rather tries to execute the command that resulted from the interpolation.

Can't assign a value to a variable in a case statement in bash

I think you were aiming at something like this:

#!/bin/bash
# Print usage message:
usage() {
echo "Usage: $0 [-n N] [-t|-p] INPUT OUTPUT" >> /dev/stdout
}
# Set default values
n_threads=1
use_threads=1
while getopts "n:pth" opt; do
case $opt in
n) n_threads=$OPTARG;;
t) use_threads=1;;
p) use_threads=0;;
h) usage; exit 0;;
*) usage; exit 1;;
esac
done
# Get rid of scanned options
shift $((OPTIND-1))
if (($# != 2)); then usage; exit 1; fi
if ((use_threads)); then
echo "Using $n_threads threads. IF: $1; OF: $2"
# ...
else
echo "Using $n_threads processes. IF: $1; OF: $2"
# ...
fi

Here's some example invocations, including a couple of errors:

$ ./ff -p foo bar
Using 1 processes. IF: foo; OF: bar
$ ./ff foo bar
Using 1 threads. IF: foo; OF: bar
$ ./ff -n 7 foo bar
Using 7 threads. IF: foo; OF: bar
$ ./ff -n 7 -p foo bar
Using 7 processes. IF: foo; OF: bar
$ ./ff -p -n7 foo bar
Using 7 processes. IF: foo; OF: bar
$ ./ff -q -n7 foo bar
./ff: illegal option -- q
Usage: ./ff [-n N] [-t|-p] INPUT OUTPUT
# Note: The error message here could be more informative.
# Exercise left for the reader
$ ./ff -n 7 foo
Usage: ./ff [-n N] [-t|-p] INPUT OUTPUT

Variable assignment inside a case statement (bash)

Remove the spaces around the = operators:

case "$flag" in
H) host="$OPTARG" ;;
w) warning="$OPTARG" ;;
c) critical="$OPTARG" ;;
esac

Case statement not working in bash, conditions not apply

The function is in fact written correctly. It's how it's called that's the problem.

host=$(get_virtual_host)

When you capture a command's output the command runs in a subshell. Exiting the subshell doesn't directly cause the parent shell to exit; the parent shell needs to check the subshell's exit status.

host=$(get_virtual_host) || exit

This will exit the parent if get_virtual_host fails. A bare exit without an explicit exit code forwards the existing value of $?.

How to update variable within case statement based on user input

  • Insert ; before all break.
  • Replace $hash with ${hash:=2} to use a default value if $hash is empty.
  • "") hash="sha256" break 2;; can be removed.

Why does my variable work in a test statement, but not in a case statement until I use echo to read it into itself?

Quoting from man bash:

${parameter/pattern/string}

Pattern substitution. The pattern is expanded to produce a pattern just as in pathname expansion, Parameter is expanded and the longest match of pattern against its value is replaced with string. ...

That means, these lines:

source=${source//^[[:space:]]*}
source=${source//*[[:space:]]$}

do nothing at all, ^ and $ doesn't work in pathname expansion; the pattern is not a regex. source=$(echo $source) makes it work because since $source is not in double-quotes, its value undergoes word splitting and the space at the end gets lost.

The proper way of doing this using parameter expansions is:

source=${line%%#*}
source=${source#${source%%[^[:space:]]*}}
source=${source%${source##*[^[:space:]]}}

Case statement not catching empty string

The only way your case statement isn't going to match with no $1 given is if it isn't entered in the first place.

Consider the following:

#!/usr/bin/env bash
set -e

command=$1
shift
case $command in
*) echo "Default case was entered";;
esac

This emits no output when $1 is unset -- but not because anything wrong with the case statement.

Rather, the issue is that shift exits with a nonzero exit status when there's nothing available to shift, and the set -e causes the script as a whole to exit on that failure.


First Moral Of This Story: Don't Use set -e (or #!/bin/bash -e)

See BashFAQ #105 for an extended discussion -- or the exercises included therein if in a hurry. set -e is wildly incompatible between different "POSIX-compliant" shells, and thus makes behavior hard to predict. Manual error handling may not be fun, but it's much more reliable.


Second: Consider A Usage Function

This gives you a terse way to have your usage message in one place, and re-use it where necessary (for example, if you don't have a $1 to shift):

#!/usr/bin/env bash

usage() { echo "Usage: $0 {loop|...}" >&2; exit 1; }

command=$1
shift || usage
case $command in
*) usage ;;
esac

Because of the || usage, the exit status of shift is considered "checked", so even if you do run your script with set -e, it will no longer constitute a fatal error.


Alternately, Mark The shift As Checked Explicitly

Similarly:

shift ||:

...will run shift, but then fall back to running : (a synonym for true, which historically/conventionally implies placeholder use) should shift fail, similarly preventing set -e from triggering.


Aside: Use Lower-Case Names For Your Own Variables

POSIX specifies that the shell (and other tools to which the standards applies) have their behavior modified only by environment variables with all-caps names:

Environment variable names used by the utilities in the Shell and Utilities volume of POSIX.1-2017 consist solely of uppercase letters, digits, and the ( '_' ) from the characters defined in Portable Character Set and do not begin with a digit. Other characters may be permitted by an implementation; applications shall tolerate the presence of such names. Uppercase and lowercase letters shall retain their unique identities and shall not be folded together. The name space of environment variable names containing lowercase letters is reserved for applications. Applications can define any environment variables with names from this name space without modifying the behavior of the standard utilities.

This applies even to regular, non-exported shell variables because specifying a shell variable with the same name as an environment variable overwrites the latter.

BASH_COMMAND, for example, has a distinct meaning in bash -- and thus can be set to a non-empty value at the front of your script. There's nothing stopping COMMAND from similarly being meaningful to, and already used by, a POSIX-compliant shell interpreter.

If you want to avoid side effects from cases where your shell has set a built-in variable with a name your script uses, or where your script accidentally overwrites a variable meaningful to the shell, stick to lowercase or mixed-case names when writing scripts for POSIX-compliant shells.

Command not found error in Bash variable assignment

You cannot have spaces around the = sign.

When you write:

STR = "foo"

bash tries to run a command named STR with 2 arguments (the strings = and foo)

When you write:

STR =foo

bash tries to run a command named STR with 1 argument (the string =foo)

When you write:

STR= foo

bash tries to run the command foo with STR set to the empty string in its environment.

I'm not sure if this helps to clarify or if it is mere obfuscation, but note that:

  1. the first command is exactly equivalent to: STR "=" "foo",
  2. the second is the same as STR "=foo",
  3. and the last is equivalent to STR="" foo.

The relevant section of the sh language spec, section 2.9.1 states:

A "simple command" is a sequence of optional variable assignments and redirections, in any sequence, optionally followed by words and redirections, terminated by a control operator.

In that context, a word is the command that bash is going to run. Any string containing = (in any position other than at the beginning of the string) which is not a redirection and in which the portion of the string before the = is a valid variable name is a variable assignment, while any string that is not a redirection or a variable assignment is a command. In STR = "foo", STR is not a variable assignment.

How can I use a case statement in a make shell command?

You don't need to run a subshell just to concatenate a few variables:

foobar := $(foo)$(if $(bar),-,)$(bar)

default:
echo '$(foobar)'

should work just fine.

Live demo



Related Topics



Leave a reply



Submit