"unbound variable" reading a boolean parameter from shell script command line - bash

My apology for not being able to find such a seemingly trivial thing myself.
I need to pass more than one boolean parameter to shell script (Bash) as follows:
./script --parameter1 --parameter2
and so on.
All are to be considered true if set.
In the beginning of the script, I use set -u.
Normal parameter with value passing I currently do as follows:
# this script accepts the following arguments:
# 1. mode
# 2. window
while [[ $# > 1 ]]
do
cmdline_argument="$1"
case $cmdline_argument in
-m|--mode)
mode="$2"
shift
;;
-w|--window)
window="$2"
shift
;;
esac
shift
done
I would like to add something like
-r|--repeat)
repeat=true
shift
;;
I do not understand why it does not work as expected.
It exits immediately with error:
./empire: line 450: repeat: unbound variable
Where the line 450 is:
if [ "$repeat" == true ];

When you use set -u, it's an error to dereference any variable that hasn't had a value explicitly assigned.
Thus, you need to set repeat=0 (or repeat=false) at the top of your script, or to use a dereference method that has an explicit default behavior when the value is unset; see BashFAQ #112.

Related

Bash two commands in curly braces? [duplicate]

I've got a variable let's call it: ENV that can be set or not, and if set it's in lowercase. According to my ENV I would like to get some other variables (ex: URL_DEV or URL_PROD).
I know I can get my env in upper case with: ENV=${ENV^^} and set default value with ENV=${ENV:-DEFAULT} but is it possible to do it in one line ?
And generally, how can I combine bash operators on variables ?
I tried something like: ENV=${ENV^^:-DEFAULT} but does not work as expected.
My solution is:
ENV=${ENV:-dev}
ENV=${ENV^^}
You cannot achieve nested parameter expansion in bash shell, though its possible in zsh, so ENV=${ENV^^:-DEFAULT} operation cannot be executed by default.
You could use a ternary operator in the form of case construct in bash shell as there is no built-in operator for it (? :)
case "$ENV" in
"") ENV="default" ;;
*) ENV=${ENV^^} ;;
esac
But you shouldn't use upper case variable names for user defined shell variables. They are only meant for variables maintained by the system.
Nested parameter expansion is not possible in bash, alternatively you can check if the variable is set using [ ... ] operator:
[ -z "$ENV" ] && echo "DEFAULT" || echo ${ENV^^}

Loading a while true loop into a variable

I'm having a bit of trouble getting this to work/ knowing if its possible. I'm creating a game using little other than bash, this requires a lot of repeated case statements. I am trying to load all the repeated case statements into a variable, then repeat them when necessary to limit the amount of work it will take to update the shared case statements between different scripts.
Here is what I have:
#!/bin/bash
moo="[m][o][o]) echo 'thank you for following instructions' ;;"
test=$(echo "while true ; do
read -p 'type moo: ' case
case $case in
$moo
*) echo 'type moo please'
esac
done")
"$test"
The problem I run into is:
./case.sh: line 13: $'while true ; do\nread -p \'type moo: \' case\ncase in\n[m][o][o]) echo \'thank you for following instructions\' ;;\n*) echo \'type moo please\' ;;\nesac\ndone': command not found
The information in the moo variable will eventually be in a separate script and will be set by invoking it as a function within that script when I finally get a working model.
It looks like this is a workable idea, I've just reached a loss on how to invoke the variable without it acting up. If anyone has any ideas, I would greatly appreciate it.
Thank you in advance!
It doesn't work because the quotes make the variable expansion be treated as a single word.
But it wouldn't work without quotes, either, because the shell doesn't parse the output of variables for syntax like semicolon and newline. Variable expansion is done after that stage of command parsing. The only processing that's done on expanded variables is word-splitting and wildcard matching.
You need to use eval to perform all command parsing:
eval "$test"
Another problem is that the variable $case is being expanded when you assign the variable test, it's not getting the value being read by read. Since the variable doesn't have a value yet, it's being executed as:
case in ...
and this is invalid syntax. You need to escape the $ so it will be passed through literally.
There's also no need for echo, you can simply assign the string directly.
test="while true ; do
read -p 'type moo: ' case
case \$case in
$moo
*) echo 'type moo please'
esac
done"

I found this code in an autoconf configure script what is the following code trying to do?

I found this code in an autoconf configure script. What is the following code trying to do?
if ${am_cv_autoconf_installed+:} false; then :
$as_echo_n "(cached) " >&6
else
Lots of stuff going on here. Let's break it down.
First of all, the syntax ${var+foo} is a common idiom for checking whether the variable var has been defined. If var is defined, then ${var+foo} will expand to the string foo. Otherwise, it will expand to an empty string.
Most commonly (in bash, anyway), this syntax is used as follows:
if [ -n "${var+foo}" ]; then
echo "var is defined"
else
echo "var is not defined"
fi
Note that foo is just any arbitrary text. You could just as well use x or abc or ilovetacos.
However, in your example, there are no brackets. So whatever ${am_cv_autoconf_installed+:} expands to (if anything) will be evaluated as a command. As it turns out, : is actually a shell command. Namely, it's the "null command". It has no effect, other than to set the command exit status to 0 (success). Likewise, false is a shell command that does nothing, but sets the exit status to 1 (failure).
So depending on whether the variable am_cv_autoconf_installed is defined, the script will then execute one of the following commands:
: false
-OR-
false
In the first case, it calls the null command with the string "false" as an argument, which is simply ignored, causing the if statement to evaluate to true. In the second case, it calls the false command, causing the if statement to evaluate to false.
So all this is really doing is checking whether am_cv_autoconf_installed is defined. If this were just an ordinary bash script and didn't require any particular level of portability, it would have been a lot simpler to just do:
if [ -n "${am_cv_autoconf_installed+x}" ]; then
However, since this is a configure script, it was no doubt written this way for maximum portability. Not all shells will have the -n test. Some may not even have the [ ] syntax.
The rest should be fairly self-explanatory. If the variable is defined, the if statement evaluates to true (or more accurately, it sets the exit status to 0), causing the $as_echo_n "(cached) " >&6 line to execute. Otherwise, it does whatever is in the else clause.
I'm guessing $as_echo_n is just the environment-specific version of echo -n, which means it will print "(cached) " with no trailing newline. The >&6 means the output will be redirected to file descriptor 6 which presumably is set up elsewhere in the script (probably a log file or some such).

Bash assignment value to variable as command substitution and print value output

I would like to achieve this in Bash: echo $(a=1)and print the value of variable a
I test eval, $$a,{}, $() but none of them work as most of the times either I got literally a=1 or in one case (I don't remember which) it tried to execute the value.
I known that I can do: a=1;echo $a but because I'm little fun one command per line (even if sometimes is getting little complicated) I was wondering if is possible to do this either with echo or with printf
If you know that $a is previously unset, you can do this using the following syntax:
echo ${a:=1}
This, and other types of parameter expansion, are defined in the POSIX shell command language specification.
If you want to assign a numeric value, another option, which doesn't depend on the value previously being unset, would be to use an arithmetic expansion:
echo $(( a = 1 ))
This assigns the value and echoes the number that has been assigned.
It's worth mentioning that what you're trying to do cannot be done in a subshell by design because a child process cannot modify the environment of its parent.

Parsing input options containing whitespaces in a bash script

I have a bash script parsing input option with a block of code like the following
for WORD in "$#" ; do
case $WORD in
--*) true ;
case $WORD in
--opt1=*)
OPT1=${WORD/--opt1=/}
shift ;;
--opt2=*)
OPT2=${WORD/--opt2=/}
shift ;;
*) echo "Unrecognized argument $WORD"
;;
esac ;;
*) echo "Option $WORD not starting with double dash."
;;
esac
done
The script is invoked by another parent program which creates the entire command line.
The output created by this parent program looks like
./childscript.sh "--opt1=value1 --opt2=value2"
The problems appear when the generated line looks like
./childscript.sh "--opt1='value11 value12' --opt2=value2"
The scripts complains saying
Option value12 not starting with double dash.
How can I modify the child bash code to make it understand white spaces inside the input options?
I don't think the generated line is what you think it is.
Your code works completely fine for me if I simply invoke it directly. With added echoes to check that the values are being stored in the right place:
$ ./child.sh --opt1='v1 v2' --opt2='v3 v4'
OPT1='v1 v2'
OPT2='v3 v4'
You should be able to confirm this. Your problem isn't in making the child script accept arguments like these, it's in having the parent script invoke it correctly.
And by the way, you don't actually want to run something like this:
./childscript.sh "--opt1=value1 --opt2=value2"
That will cause that entire string (--opt1=value1 --opt2=value2) to be read as a single argument. I suspect that you haven't told us the full story on the way the parent script is calling this. If you show us those details, we can probably help out more - or maybe this is enough of a hint.

Resources