Shell script: how to read an optional argument. If the argument does not exist, then use a default value - shell

I want to write a shell script that can have an optional argument and if there is no optional argument, then uses a default value.
Example: if sys.argv[1].exists() ip = sys.argv[1] else ip = '10.9.2.16'

I'll assume that you've selected the correct tag and are submitting a question about Unix/Linux command line interpreters, and that the example code you wrote was a pseudocode example to explain the desired behavior.
From the manpage for bash, in the "Parameter Expansion" section, it clearly indicates how to do this with any variable, not just positional parameters:
${parameter:-word}
Use Default Values. If parameter is unset or null,
the expansion of word is substituted.
Otherwise, the value of parameter is substituted.
This will work for POSIX shell as well, and all dialects of shell script that I know of.
So for your example, you could use:
#!/bin/sh
ip=${1:-10.9.2.16}
echo "IP is $ip"
/path/to/script.sh would output: IP is 10.9.2.16.
/path/to/script.sh 8.8.8.8 would output: IP is 8.8.8.8.
If you mean that you want $1 to be optional, but that you want other arguments after it, you could explicitly omit the first argument and allow it to fallback to the default by calling the script with a pair of quotes (single or double) in place of the first argument.
For example, with the script:
#!/bin/sh
ip=${1:-10.9.2.16}
echo "IP is $ip"
echo "Your second argument is $2"
Calling it like /path/to/script.sh "" quux would output:
IP is 10.9.2.16
Your second argument is quux

As Wilson commented, In shell scripting you can use the ${parameter:-default} syntax to read an optional argument and use a default value if the argument does not exist. Here is an example:
optional_arg=${1:-default_value}
echo "The value of the optional argument is: $optional_arg"
This sets the variable optional_arg to the value of the first argument passed to the script ($1), or the default value "default_value" if the first argument is not provided.
You can also use if statement with [ -z "$variable" ] to check if variable is empty or not.
if [ -z "$optional_arg" ]; then
optional_arg="default_value"
fi

Related

Assignment of "$*"

I have come across an assignment like this which I have never seen before, ": ${var=$*}". The assignment can also be done like var=$*, but can anyone explain about the above what is being done. I tried to search for it but got nothing.
Explication:
For example:
A="value1"
B=${A="value2"}
echo $B -> value1
Now, when the variable A is not defined, it retrieves the value 'value2'
unset A
B=${A="value2"}
echo $B -> value2
Lets look at this line step by step:
: argument : This only executes the expansions of argument. The colon command is generally not useful, but can be used for parameter validation or initialisation via parameter expansion. It is also used to run infinite while loops.
What is the purpose of the : (colon) GNU Bash builtin?
[U&L] What purpose does the colon builtin serve
BashFAQ035
${var=word} The argument in the above expansion is of the form ${var=word}. This is identical to ${var:=word} with the only difference that the former tests if var is unset while the latter tests if var is unset or null. If the condition applies, var is assigned with the value of word
$* The value of word in the above is now the expansion of $*. It expands to a single string of the form $1c$2c$3c...$ where $n are the values of the command arguments and the value of c expands to the first character of the variable IFS.
Having this all said, this command is equivalent to the following line which uses classic programming jargon:
if [ -z ${var+x} ]; then var="$*"; fi
See How to check if a variable is set in Bash?

Bash shell parameter in bracket [duplicate]

What is the meaning of this style in bash?
${PUBLIC_INTERFACE:-eth0}
What is the purpose of :-?
If $PUBLIC_INTERFACE exists and isn't null, return its value, otherwise return "eth0".
There are actually a few of these documented in the bash man page:
${parameter:-word} Use Default Values. If parameter is unset or null, the expansion of word is substituted. Otherwise, the value of parameter is substituted.
${parameter:=word} Assign Default Values. If parameter is unset or null, the expansion of word is assigned to parameter. The value of parameter is then substituted. Positional parameters and special parameters may not be assigned to in this way.
${parameter:?word} Display Error if Null or Unset. If parameter is null or unset, the expansion of word (or a message to that effect if word is not present) is written to the standard error and the shell, if it is not interactive, exits. Otherwise, the value of parameter is substituted.
${parameter:+word} Use Alternate Value. If parameter is null or unset, nothing is substituted, otherwise the expansion of word is substituted.
:- is used in the ${parameter:-word} shell parameter expansion: if parameter is null or unset, it expands to the value of word, otherwise to the value of parameter.
Example:
$ str=
$ echo "${str:-default}"
default
This and the similar expansions using :=, :+ and :? all come in two flavours: with and without a colon. The difference is that the expansion with the colon kicks in for "unset or null", whereas without the colon, it's just "unset".
Observe:
$ str= # Null, but not unset
$ echo "${str-default}" # Expands to value of $str, the empty string
$ echo "${str:-default}" # Expands to "default"
default
Where is this useful? A few examples:
Default values
The editor invoked to edit the last command with fc is the result of the expansion ${FCEDIT:-${EDITOR:-vi}}: $FCEDIT if defined and not null, or else $EDITOR if defined and not null, or else vi.
A loop in a script that should read from a file if one is provided as an argument and from standard input otherwise could look like this:
while IFS= read -r line; do
# do something
done < "${1:-/dev/stdin}"
When using set -u
set -u is a handy way to force cleaner scripting by having the script die when encountering an unset variable, as promoted by for example this article (not that I endorse everything in there1). If we want to check if a certain variable has a value with [[ $var ]], the script now dies if var is unset, even though this might be legitimate.
The way around this is using [[ ${var:-} ]] instead, and set -u won't complain. (This is basically using a default value again, but the substituted value is the empty string in this case.2)
These expansions are not exclusive to Bash, by the way: the POSIX shell spec has them all, too.
1 See also BashFAQ/112, What are the advantages and disadvantages of using set -u (or set -o nounset)?
2 Just ${var-} instead of ${var:-} would actually be sufficient: set -u doesn't care about variables that have been set but contain the empty string; only unset ones trigger an error.

what does ${var-value} do in a shell script? [duplicate]

What is the meaning of this style in bash?
${PUBLIC_INTERFACE:-eth0}
What is the purpose of :-?
If $PUBLIC_INTERFACE exists and isn't null, return its value, otherwise return "eth0".
There are actually a few of these documented in the bash man page:
${parameter:-word} Use Default Values. If parameter is unset or null, the expansion of word is substituted. Otherwise, the value of parameter is substituted.
${parameter:=word} Assign Default Values. If parameter is unset or null, the expansion of word is assigned to parameter. The value of parameter is then substituted. Positional parameters and special parameters may not be assigned to in this way.
${parameter:?word} Display Error if Null or Unset. If parameter is null or unset, the expansion of word (or a message to that effect if word is not present) is written to the standard error and the shell, if it is not interactive, exits. Otherwise, the value of parameter is substituted.
${parameter:+word} Use Alternate Value. If parameter is null or unset, nothing is substituted, otherwise the expansion of word is substituted.
:- is used in the ${parameter:-word} shell parameter expansion: if parameter is null or unset, it expands to the value of word, otherwise to the value of parameter.
Example:
$ str=
$ echo "${str:-default}"
default
This and the similar expansions using :=, :+ and :? all come in two flavours: with and without a colon. The difference is that the expansion with the colon kicks in for "unset or null", whereas without the colon, it's just "unset".
Observe:
$ str= # Null, but not unset
$ echo "${str-default}" # Expands to value of $str, the empty string
$ echo "${str:-default}" # Expands to "default"
default
Where is this useful? A few examples:
Default values
The editor invoked to edit the last command with fc is the result of the expansion ${FCEDIT:-${EDITOR:-vi}}: $FCEDIT if defined and not null, or else $EDITOR if defined and not null, or else vi.
A loop in a script that should read from a file if one is provided as an argument and from standard input otherwise could look like this:
while IFS= read -r line; do
# do something
done < "${1:-/dev/stdin}"
When using set -u
set -u is a handy way to force cleaner scripting by having the script die when encountering an unset variable, as promoted by for example this article (not that I endorse everything in there1). If we want to check if a certain variable has a value with [[ $var ]], the script now dies if var is unset, even though this might be legitimate.
The way around this is using [[ ${var:-} ]] instead, and set -u won't complain. (This is basically using a default value again, but the substituted value is the empty string in this case.2)
These expansions are not exclusive to Bash, by the way: the POSIX shell spec has them all, too.
1 See also BashFAQ/112, What are the advantages and disadvantages of using set -u (or set -o nounset)?
2 Just ${var-} instead of ${var:-} would actually be sufficient: set -u doesn't care about variables that have been set but contain the empty string; only unset ones trigger an error.

How do I redirect output of echo command in bash script to script's directory?

Fairly simple, I have an echo statement in my shell (bash) script that I want to redirect to an external file as a means of logging. The script is run by the root user's crontab every hour (via sudo crontab -e).
I want this log file to reside in the same directory as the script.
My first attempt was,
echo "$(date) | blah blah" >> "$(pwd)/script.log"
However, this clearly does not work as the working directory of the root crontab (/root/) is not the same as the directory of the script. So following some advice on SO I did instead,
script_dir=$(dirname $0)
echo "$(date) | blah blah" >> "$(script_dir)/script.log"
This time, for reasons I do not yet understand, the log file is saved under /, as in /script.log.
Logically one would assume that the variable script_dir was evaluated to an empty string and so "$(script_dir)/script.log" was evaluated as "/script.log".
But as a test, I wrote a simple test script,
echo "$(dirname $0)"
and ran it from /. Sure enough, I get a proper non-empty output: /home/pi/scripts/testscripts/dirname.sh (where the test script I wrote resides).
So what's the deal, and how do I get this to work?
p.s. bash --version says I am currently running GNU bash, version 4.3.30(1)-release
You need the curly braces within double-quotes to expand variables in bash, something like,
echo "$(date) | blah blah" >> "${script_dir}/script.log"
Shell Parameter Expansion
The ‘$’ character introduces parameter expansion, command substitution, or arithmetic expansion.
The basic form of parameter expansion is ${parameter}. The value of parameter is substituted. The braces are required when parameter is a positional parameter with more than one digit, or when parameter is followed by a character that is not to be interpreted as part of its name.
More on Parameter expansion
${PARAMETER}
The easiest form is to just use a parameter's name within braces. This is identical to using $FOO like you see it everywhere, but has the advantage that it can be immediately followed by characters that would be interpreted as part of the parameter name otherwise. Compare these two expressions (WORD="car" for example), where we want to print a word with a trailing "s":
echo "The plural of $WORD is most likely $WORDs"
echo "The plural of $WORD is most likely ${WORD}s"
Why does the first one fail? It prints nothing, because a parameter (variable) named "WORDs" is undefined and thus printed as "" (nothing). Without using braces for parameter expansion, Bash will interpret the sequence of all valid characters from the introducing "$" up to the last valid character as name of the parameter. When using braces you just force Bash to only interpret the name inside your braces.
This line has an error :
echo "$(date) | blah blah" >> "$(script_dir)/script.log"
It should be :
echo "$(date) | blah blah" >> "$script_dir/script.log"
The "$(script_dir)" syntax tries to execute a command named script_dir and capture its output to use as a value inside the string. What you need is a simple variable expansion, $script_dir, which simply extracts the value of a variable named script_dir.

What's the difference between ${var} and ${var-}

I've seen some shell scripts that make use of this variable reference notation and I just can't find any info on it.
As far as my tests have gone it is just plainly the same.
Any clues?
$ uno=1
$ if [ -n "${uno}" ]; then echo yay\! ; fi
yay!
$ if [ -n "${uno-}" ]; then echo yay\! ; fi
yay!
${uno-} is an example of providing a default value in case the parameter uno is unset.
If uno is unset, we get the string that follows the -:
$ unset uno
$ echo ${uno-something}
something
If uno is merely the empty string, then the value of uno is returned:
$ uno=""
$ echo ${uno-something}
$
If uno has a non-empty value, of course, then that value is returned:
$ uno=Yes
$ echo ${uno-something}
Yes
Why use ${variable-}?
When proper operation of a script is important, it is common for the script writer to use set -u which generates an error message anytime an unset variable is used. For example:
$ set -u
$ unset uno
$ echo ${uno}
bash: uno: unbound variable
To handle the special cases where one may want to suppress this message, the trailing - can be used:
$ echo ${uno-}
$
[Credit for finding that the OP's full code used set -u and its importance to this question goes to Benjamin W.]
Documentation
From man bash
When not performing substring expansion, using the forms documented
below (e.g., :-), 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 Default Values. If parameter is unset or null, the expansion of word is substituted. Otherwise, the value
of parameter is substituted. [emphasis added]
From the Bash manual, about parameter expansion:
${parameter:-word}
If parameter is unset or null, the expansion of word is substituted. Otherwise, the value of parameter is substituted.
In your case, word is the empty string, and the colon is omitted. Without the colon, the check is just for unset, but not for null, so ${uno-} is equivalent to checking if uno is set (see How to check if a variable is set in Bash?), and if it is not, expand it to the empty string – guaranteeing that the expansion is not unset.
It depends on the context what this is good for; in your example, it really makes no difference.
Turns out the context is a script where set -u is used – see John 1024's answer for the details.

Resources