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 allbreak
. - 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 ofpattern
against its value is replaced withstring
. ...
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-export
ed 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:
- the first command is exactly equivalent to:
STR "=" "foo"
, - the second is the same as
STR "=foo"
, - 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
When Installing Rust Toolchain in Docker, Bash 'Source' Command Doesn't Work
Install Binaries into /Bin, /Sbin, /Usr/Bin and /Usr/Sbin, Interactions with --Prefix and Destdir
Why Doesn't Set -E Cause a Failure with 'False || False && True'
Extracting Variable in Yaml from a Shell Script
How to Get the CPU Time for a Perl System Call
Docker in Wsl2 Alpine Without Docker Desktop
Idle Time of a Process in Linux
How to Read from User in Rpm Install Script
How to Escape the Bang (!) Character in Linux Bash Shell
Using $Origin to Specify the Interpreter in Elf Binaries Isn't Working
Hash ("#") Symbol in /Etc/Environment Causes String to Be Split
Escaping Single Quotes in Shell for Postgresql
Linking to Modules Folder Gives Undefined Reference
Docker-Compose Stop Working After Docker Desktop Installation on Debian 11