bash scription if conditional "${1:-}" [duplicate] - bash

This question already has answers here:
Usage of :- (colon dash) in bash
(2 answers)
Closed 1 year ago.
I try to understant the if condition
log_daemon_msg () {
if [ -z "${1:-}" ]; then
return 1
fi
log_daemon_msg_pre "$#"
if [ -z "${2:-}" ]; then
echo -n "$1:" || true
return
fi
echo -n "$1: $2" || true
log_daemon_msg_post "$#"
}
what is mean "${1:-}" and "${2:-}"

See Shell Parameter Expansion, {parameter:-word}.
${parameter:-word}
If parameter is unset or null, the expansion of word is substituted. Otherwise, the value of parameter is substituted.
So ${1:-} is the first argument of the function log_daemon_msg or the empty string if the function was called without arguments or with an empty first argument.
Normally, that doesn't really make sense, as just writing $1 would have the same effect. However, if your script runs with set -u (exit when using an undefined variable) ${1:-} can be used to get the standard-behavior ($1 turns into the empty string if unset). But the echo -n "$1: $2" at the end would still fail in the case of missing arguments.

Related

setting variables with variable names in bash (para$i=... giving "command not found") [duplicate]

This question already has answers here:
Assign variables inside for loops
(2 answers)
Closed 6 years ago.
I want to pass three arguments to a script,the first two numbers and third one any character,Buut when i run the script it says command not found ,even though the value is getting assigned.i have attached the code and image below.enter image description here
This is my peice of code,
#!/bin/bash
if [ $# -lt 3 ]
then
echo "insufficient argument"
for((i=$#+1;i<4;i=$i+1))
do
read -p "enter $i parameter: " x
para$i=x
done
fi
This is not a valid assignment:
para$i=x
Since your shell is bash, you can do the following instead:
# bash 3.1 or higher
printf -v "para$i" %s "$x"
...or...
# bash 4.3 or higher; works with arrays and other tricky cases too.
declare -n para="para$i"
para=$x
unset -n para
...or...
# any POSIX shell
# be very careful about the quoting; only safe if $x is quoted and $i is a controlled value
eval "para$i=\$x"
See the BashFAQ #6 section on indirect assignment for more details.
See an example of processing a parameter called process_date
The script this will accept the parameter as so:
some_sh_script.sh -process_date=01/01/2016
Script:
process_date=""
while test "$1" != "" ; do
# Test argument syntax e.g. -someName=someValue or help operators
if [[ $1 != -*=* && $1 != -h && $1 != -help ]]
then
echo "Error in $0 - $1 - Argument syntax invalid."
usage
exit 1
fi
# END Test argument syntax
# Split argument name & value by `=` delimiter
paramName=`echo $1 | cut -d '=' -f1`
paramVal=`echo $1 | cut -d '=' -f2`
case $paramName in
-process_date)
process_date=$paramVal
;;
#User help parameter
-help|-h)
usage
exit 0
;;
-*)
echo "No such option $1"
usage
exit 1
;;
esac
#parse next argument
shift
done

How to concatenate the arguments and store it in a variable in unix? [duplicate]

This question already has answers here:
Concatenate all arguments and wrap them with double quotes
(6 answers)
Closed 5 years ago.
I would like to concatenate all the arguments passed to my bash script except the flag.
So for example, If the script takes inputs as follows:
./myBashScript.sh -flag1 exampleString1 exampleString2
I want the result to be "exampleString1_exampleString2"
I can do this for a predefined number of inputs (i.e. 2), but how can i do it for an arbitrary number of inputs?
function concatenate_args
{
string=""
for a in "$#" # Loop over arguments
do
if [[ "${a:0:1}" != "-" ]] # Ignore flags (first character is -)
then
if [[ "$string" != "" ]]
then
string+="_" # Delimeter
fi
string+="$a"
fi
done
echo "$string"
}
# Usage:
args="$(concatenate_args "$#")"
This is an ugly but simple solution:
echo $* | sed -e "s/ /_/g;s/[^_]*_//"
You can also use formatted strings to concatenate args.
# assuming flag is first arg and optional
flag=$1
[[ $1 = ${1#-} ]] && unset $flag || shift
concat=$(printf '%s_' ${#})
echo ${concat%_} # to remove the trailing _
nJoy!
Here's a piece of code that I'm actually proud of (it is very shell-style I think)
#!/bin/sh
firsttime=yes
for i in "$#"
do
test "$firsttime" && set -- && unset firsttime
test "${i%%-*}" && set -- "$#" "$i"
done
IFS=_ ; echo "$*"
I've interpreted your question so as to remove all arguments beginning with -
If you only want to remove the beginning sequence of arguments beginnnig with -:
#!/bin/sh
while ! test "${1%%-*}"
do
shift
done
IFS=_ ; echo "$*"
If you simply want to remove the first argument:
#!/bin/sh
shift
IFS=_ ; printf %s\\n "$*"
flag="$1"
shift
oldIFS="$IFS"
IFS="_"
the_rest="$*"
IFS="$oldIFS"
In this context, "$*" is exactly what you're looking for, it seems. It is seldom the correct choice, but here's a case where it really is the correct choice.
Alternatively, simply loop and concatenate:
flag="$1"
shift
the_rest=""
pad=""
for arg in "$#"
do
the_rest="${the_rest}${pad}${arg}"
pad="_"
done
The $pad variable ensures that you don't end up with a stray underscore at the start of $the_rest.
#!/bin/bash
paramCat () {
for s in "$#"
do
case $s in
-*)
;;
*)
echo -n _${s}
;;
esac
done
}
catted="$(paramCat "$#")"
echo ${catted/_/}

Use string as bash variable name in alternative value expansion [duplicate]

This question already has answers here:
How can I look up a variable by name with #!/bin/sh (POSIX sh)?
(4 answers)
Closed 7 years ago.
How can I use the value of one variable as the name of another variable in an alternative value expansion (${var+alt}) in bash?
I would think that
#!/bin/bash
cat='dog'
varname='cat'
if [ -z ${`echo "${varname}"`+x} ]; then
echo 'is null'
fi
should be roughly equivalent to
#!/bin/bash
if [ -z ${dog+x} ]; then
echo 'is null'
fi
but when I try to do this, I get
${`echo "${cat}"`+x}: bad substitution
I guess part of the problem is that the subshell doing the command substitution doesn't know about $varname anymore? Do I need to export that variable?
My reason for doing this is that I learned from this answer how to check if a variable is null, and I'm trying to encapsulate that check in a function called is_null, like this:
function is_null {
if [ $# != 1 ]; then
echo "Error: is_null takes one argument"
exit
fi
# note: ${1+x} will be null if $1 is null, but "x" if $1 is not null
if [ -z ${`echo "${1}"`+x} ]; then
return 0
else
return 1
fi
}
if is_null 'some_flag'; then
echo 'Missing some_flag'
echo $usage
exit
fi
I'm not sure if I understand your problem.
If I got, what you need is eval command.
$ cat='dog'
$ varname='cat'
$ echo ${varname}
cat
$ eval echo \$${varname}
dog

Idiomatic bash: set default value for variable [duplicate]

This question already has answers here:
Assigning default values to shell variables with a single command in bash
(11 answers)
Closed 8 years ago.
How can I set a default value for a bash variable in a concise, idiomatic way? This just looks ugly:
if [[ ! -z "$1" ]]; then
option="$1"
else
option="default"
fi
default value : ${parameter:-word} \
assign default value : ${parameter:=word} |_ / if unset or null -
error if empty/unset : ${parameter:?mesg} | \ use no ":" for unset only
use word unless empty/unset : ${parameter:+word} /
You can use:
option=${1:-default}
This sets option to the first command line parameter if a first command line parameter was given and not null. Otherwise, it sets option to default. See Bash reference manual for the details on parameter expansion and some useful variants of this form.
For uses other than assigning a default value, which Toxaris already covered, it's worth mentioning that there is a reverse test for -z, so instead of
if [[ ! -z "$1" ]]; then
do_something
fi
you can simply write:
if [ -n "$1" ]; then
do_something
fi
and if there is no else branch, you can shorten it to:
[ -n "$1" ] && do_something

Test for a Bash variable being unset, using a function

A simple Bash variable test goes:
${varName:? "${varName} is not defined"}
I'd like to reuse this, by putting it in a function. How can I do it?
The following fails
#
# Test a variable exists
tvar(){
val=${1:? "${1} must be defined, preferably in $basedir"}
if [ -z ${val} ]
then
echo Zero length value
else
echo ${1} exists, value ${1}
fi
}
I.e., I need to exit if the test fails.
Thanks to lhunath's answer, I was led to a part of the Bash man page that I've overlooked hundreds of times:
When not performing substring expansion, bash tests for a parameter that is unset or null; omitting the colon results in a test only for a parameter that is unset.
This prompted me to create the following truth table:
Unset
Set, but null
Set and not null
Meaning
${var-_}
T
F
T
Not null or not set
${var:-_}
T
T
T
Always true, use for subst.
$var
F
F
T
'var' is set and not null
${!var[#]}
F
T
T
'var' is set
This table introduces the specification in the last row. The Bash man page says "If name is not an array, expands to 0 if name is set and null otherwise." For purposes of this truth table, it behaves the same even if it's an array.
You're looking for indirection.
assertNotEmpty() {
: "${!1:? "$1 is empty, aborting."}"
}
That causes the script to abort with an error message if you do something like this:
$ foo=""
$ assertNotEmpty foo
bash: !1: foo is empty, aborting.
If you just want to test whether foo is empty, instead of aborting the script, use this instead of a function:
[[ $foo ]]
For example:
until read -p "What is your name? " name && [[ $name ]]; do
echo "You didn't enter your name. Please, try again." >&2
done
Also, note that there is a very important difference between an empty and an unset parameter. You should take care not to confuse these terms! An empty parameter is one that is set, but just set to an empty string. An unset parameter is one that doesn't exist at all.
The previous examples all test for empty parameters. If you want to test for unset parameters and consider all set parameters OK, whether they're empty or not, use this:
[[ ! $foo && ${foo-_} ]]
Use it in a function like this:
assertIsSet() {
[[ ! ${!1} && ${!1-_} ]] && {
echo "$1 is not set, aborting." >&2
exit 1
}
}
Which only aborts the script when the parameter name you pass denotes a parameter that isn't set:
$ ( foo="blah"; assertIsSet foo; echo "Still running." )
Still running.
$ ( foo=""; assertIsSet foo; echo "Still running." )
Still running.
$ ( unset foo; assertIsSet foo; echo "Still running." )
foo is not set, aborting.
You want to use [ -z ${parameter+word} ]
Some part of man bash:
Parameter Expansion
...
In each of the cases below, word is subject to tilde expansion, parameter expansion, command substitution, and
arithmetic expansion. When not performing substring expansion, bash tests for a parameter that is unset or null;
omitting the colon results in a test only for a parameter that is unset.
...
${parameter:+word}
Use Alternate Value. If parameter is null or unset, nothing is substituted, otherwise the expansion of
word is substituted.
...
in other words:
${parameter+word}
Use Alternate Value. If parameter is unset, nothing is substituted, otherwise the expansion of
word is substituted.
some examples:
$ set | grep FOOBAR
$ if [ -z "${FOOBAR+something}" ]; then echo "it is unset"; fi
it is unset
$ declare FOOBAR
$ if [ -z "${FOOBAR+something}" ]; then echo "it is unset"; fi
$ FOOBAR=
$ if [ -z "${FOOBAR+something}" ]; then echo "it is unset"; fi
$ FOOBAR=1
$ if [ -z "${FOOBAR+something}" ]; then echo "it is unset"; fi
$ unset FOOBAR
$ if [ -z "${FOOBAR+something}" ]; then echo "it is unset"; fi
it is unset
$
This function tests for variables that are currently set. The variable may even be an array. Note that in Bash: 0 == TRUE, 1 == FALSE.
function var.defined {
eval '[[ ${!'$1'[#]} ]]'
}
# Typical usage of var.defined {}
declare you="Your Name Here" ref='you';
read -p "What's your name: " you;
if var.defined you; then # Simple demo using literal text
echo "BASH recognizes $you";
echo "BASH also knows a reference to $ref as ${!ref}, by indirection.";
fi
unset you # Have just been killed by a master :D
if ! var.defined $ref; then # Standard demo using an expanded literal value
echo "BASH doesn't know $ref any longer";
fi
read -s -N 1 -p "Press any key to continue...";
echo "";
So to be clear here, the function tests literal text. Every time a command is called in Bash, variables are generally 'swapped-out' or 'substituted' with the underlying value unless:
$varRef ($) is escaped: $varRef
$varRef is single quoted '$varRef'
I.e., I need to exit if the test fails.
The code:
${varName:? "${varName} is not defined"}
will return a nonzero exit code when there is not a variable named "varName". The exit code of the last command is saved in $?.
About your code:
val=${1:? "${1} must be defined, preferably in $basedir"}
Maybe it is not doing what you need. In the case that $1 is not defined, the "${1}" will be substituted with nothing. Probably you want use the single quotes that literally writes ${1} without substitution.
val=${1:? '${1} must be defined, preferably in $basedir'
I am unsure if this is exactly what you want, but a handy trick I use when writing a new and complex script is to use "set -o":
set -o # Will make the script bomb out when it finds an unset variable
For example,
$ grep '$1' chex.sh
case "$1" in
$ ./chex.sh
./chex.sh: line 111: $1: unbound variable
$ ./chex.sh foo
incorrect/no options passed.. exiting
if set | grep -q '^VARIABLE='
then
echo VARIABLE is set
fi

Resources