What does +x mean in bash scripting [duplicate] - bash

This question already has answers here:
What does "plus colon" ("+:") mean in shell script expressions?
(4 answers)
Closed 4 years ago.
What does +x mean in the below statement.
if[ -z ${FSV_ROOT+x} ]

Read up on Use Alternative Value. http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02
In parameter expansion if parameter is unset or null, null shall be substituted; otherwise, the expansion of word shall be substituted. Use of the colon in the format shall result in a test for a parameter that is unset or null; omission of the colon shall result in a test for a parameter that is only unset.
So in your case:
If FSV_ROOT is set and not null, substitute x
If FSV_ROOT set but null, substitute x
If FSV_ROOT is unset, substitute null

${parameter+alt_value}: if parameter is set (to any value including null), return "alt_value" instead.
[ -z ${parameter+x} ] will return true if parameter has not been set at all. The "x" has no special meaning and could be replaced with any non-null string. It is there primarily because just [ -z $parameter ] would also return true if parameter were set to null - but it also helps to avoid a syntax error if $parameter were set to expand to more than one word, which would require quoting of the variable otherwise.
See also:
https://tldp.org/LDP/abs/html/parameter-substitution.html#PARAMALTV
http://tldp.org/LDP/abs/html/refcards.html
Do not confuse with the common use of +x with the chmod command, where it means to set the execute bit on a file.

Related

Conditional on non-instantiated variable

I am new to Bash scripting, having a lot more experience with C-type languages. I have written a few scripts with a conditional that checks the value of a non-instantiated variable and if it doesn't exist or match a value sets the variable. On top of that the whole thing is in a for loop. Something like this:
for i in ${!my_array[#]}; do
if [ "${my_array[i]}" = true ]
then
#do something
else
my_array[i]=true;
fi
done
This would fail through a null pointer in Java since my_array[i] is not instantiated until after it is checked. Is this good practice in Bash? My script is working the way I designed, but I have learned that just because a kluge works now doesn't mean it will work in the future.
Thanks!
You will find this page on parameter expansion helpful, as well as this one on conditionals.
An easy way to test a variable is to check it for nonzero length.
if [[ -n "$var" ]]
then : do stuff ...
I also like to make it fatal to access a nonexisting variable; this means extra work, but better safety.
set -u # unset vars are fatal to access without exception handling
if [[ -n "${var:-}" ]] # handles unset during check
then : do stuff ...
By default, referencing undefined (or "unset") variable names in shell scripts just gives the empty string. But is an exception: if the shell is run with the -u option or set -u has been run in it, expansions of unset variables are treated as errors and (if the shell is not interactive) cause the shell to exit. Bash applies this principle to array elements as well:
$ array=(zero one two)
$ echo "${array[3]}"
$ echo "array[3] = '${array[3]}'"
array[3] = ''
$ set -u
$ echo "array[3] = '${array[3]}'"
-bash: array[3]: unbound variable
There are also modifiers you can use to control what expansions do if a variable (or array element) is undefined and/or empty (defined as the empty string):
$ array=(zero one '')
$ echo "array[2] is ${array[2]-unset}, array[3] is ${array[3]-unset}"
array[2] is , array[3] is unset
$ echo "array[2] is ${array[2]:-unset or empty}, array[3] is ${array[3]:-unset or empty}"
array[2] is unset or empty, array[3] is unset or empty
There are a bunch of other variants, see the POSIX shell syntax standard, section 2.6.2 (Parameter Expansion).
BTW, you do need to use curly braces (as I did above) around anything other than a plain variable reference. $name[2] is a reference to the plain variable name (or element 0 if it's an array), followed by the string "[2]"; ${name[2]}, on the other hand, is a reference to element 2 of the array name. Also, you pretty much always want to wrap variable references in double-quotes (or include them in double-quoted strings), to prevent the shell from "helpfully" splitting them into words and/or expanding them into lists of matching files. For example, this test:
if [ $my_array[i] = true ]
is (mostly) equivalent to:
if [ ${my_array[0]}[i] = true ]
...which isn't what you want at all. But this one:
if [ ${my_array[i]} = true ]
still doesn't work, because if my_array[i] is unset (or empty) it'll expand to the equivalent of:
if [ = true ]
...which is bad test expression syntax. You want this:
if [ "${my_array[i]}" = true ]

Variable inside a square bracket in Bash [duplicate]

This question already has answers here:
Purpose of square brackets in shell scripts
(3 answers)
How to use double or single brackets, parentheses, curly braces
(9 answers)
Closed 4 years ago.
What does it mean to have a variable inside square brackets? Boolean?
For example:
[ $FILES ] || { print "File not found." }
[ $VERIFY ] && { print "file verification fail" }
[] invokes the test command which allows you to run conditional tests based on what arguments you supply inside them.
For the examples you have supplied:
$FILES is a single argument, so the test will return True if the argument is not null. Piped with || (Logical OR), whenever $FILES is 'null', test will return a non-zero exit status (1) thus printing "File not found."
Ditto for $VERIFY except being piped with && means that print "file verification" will execute as long as $VERIFY is not null (as test will return a status of 0 in this case).
I will also add that there also exists [[]] which is the 'newer' test and is more commonly used
EDIT: I would also recommend enclosing variable names in double quotes ("") as they are technically arguments to a command.

How can I write if/else with Boolean in Bash? [duplicate]

This question already has answers here:
How can I declare and use Boolean variables in a shell script?
(25 answers)
Closed 5 years ago.
How can I write an 'if then' statement to switch between these to variables as in the following?
if(switch){
server_var_shortname=$server_shared_shortname
server_var=$server_shared
server_var_bare=$server_shared_bare
} else {
server_var_shortname=$server_vps_shortname
server_var=$server_vps
server_var_bare=$server_vps_bare
}
I'm not familiar with Bash syntax and basically just need an 'if/else' statement on a Boolean. Also, can I use true / false values as such? Also how do I do the 'else' statement?
$switch=true;
if $switch
then
server_var_shortname=$server_shared_shortname
server_var=$server_shared
server_var_bare=$server_shared_bare
fi
First, shells (including Bash) don't have Booleans; they don't even have integers (although they can sort of fake it). Mostly, they have strings.
Bash also has arrays... of strings. There are a number of ways of faking Booleans; my favorite is to use the strings "true" and "false". These also happen to be the names of commands that always succeed and fail respectively, which comes in handy, because the if statement actually takes a command, and runs the then clause if it succeeds and the else clause if it fails. Thus, you can "run" the Boolean, and it'll succeed if set to "true" and fail if set to "false". Like this:
switch=true # This doesn't have quotes around it, but it's a string anyway.
# ...
if $switch; then
server_var_shortname=$server_shared_shortname
server_var=$server_shared
server_var_bare=$server_shared_bare
else
server_var_shortname=$server_vps_shortname
server_var=$server_vps
server_var_bare=$server_vps_bare
fi
Note that the more usual format you'll see for if has square-brackets, like if [ something ]; then. In this case, [ is actually a command (not some funny sort of grouping operator) that evaluates its argument as an expression; thus [ "some string" = "some other string" ] is a command that will fail because the strings aren't equal. You could use if [ "$switch" = true ]; then, but I prefer to cheat and use the fake Boolean directly.
Caveat: if you do use the cheat I'm suggesting, make sure your "Boolean" variable is set to either "true" or "false" -- not unset, not set to something else. If it's set to anything else, I take no responsibility for the results.
Some other syntax notes:
Use $ on variables when fetching their values, not when assigning to them. You have $switch=true; up there, which will get you an error.
Also, you have a semicolon at the end of that line. This is unnecessary; semicolons are used to separate multiple commands on the same line (and a few other places), but they aren't needed to end the last (/only) command on a line.
The [ command (which is also known as test) has a kind of weird syntax. Mostly because it's a command, so it goes through the usual command parsing, so e.g. [ 5 > 19 ] is parsed as [ 5 ] with output sent to a file named "19" (and is then true, because "5" is nonblank). [ 5 ">" 19 ] is better, but still evaluates to true because > does string (alphabetical) comparisons, and "5" is alphabetically after "19". [ 5 -gt 19 ] does the expected thing.
There's also [[ ]] (similar, but cleaner syntax and not available in all shells) and (( )) (for math, not strings; also not in all shells). See Bash FAQ #31.
Putting commands in variables is generally a bad idea. See Bash FAQ #50.
shellcheck.net is your friend.
Bash doesn't have any concept of Boolean - there are no true / false values. The construct
[ $switch ]
will be true except when switch variable is not set or is set to an empty string.
[ ] && echo yes # Nothing is echoed
[ "" ] && echo yes # Nothing is echoed
unset switch && [ $switch ] && echo yes # Nothing is echoed
switch=1 && [ $switch ] && echo yes # 'yes' is echoed
switch=0 && [ $switch ] && echo yes # 'yes' is echoed - the shell makes no distinction of contents - it is true as long it is not empty
See also:
How can I declare and use Boolean variables in a shell script?
Here is a good guide for If else. But I want to show a different approach (which you will find also in the link on page 3).
Your coding looks like JavaScript, so I think with Switch you could also mean the case command instead of if. Switch in JavaScript is similar to case within a shell, but there isn't any method to check for Booleans. You can check string values for like true and false, and you can check for numbers.
Example...
#!/bin/bash
case "$Variable" in
false|0|"")
echo "Boolean is set to false."
;;
*)
echo "Boolean is set to true."
;;
esac
Addition
Keep in mind, there are many programs and tools that uses Boolean values in different forms.
Two examples...
SQL in general uses numbers as Boolean.
JavaScript uses true and false values.
Meaning: Your Bash script has to know the format of Booleans, before processing them!
You need something like this:
if
CONDITION_SEE_BELOW
then
server_var_shortname=$server_shared_shortname
server_var=$server_shared
server_var_bare=$server_shared_bare
else
server_var_shortname=$server_vps_shortname
server_var=$server_vps
server_var_bare=$server_vps_bare
fi
In Bash (and other shells), the CONDITION_SEE_BELOW part has to be a command. A command returns a numerical value, and by convention 0 means "true" and any non-zero value means "false". The then clause will execute if the command returns 0, or the else clause in all other cases. The return value is not the text output by the command. In shells, you can access it with the special variable expansion $? right after executing a command.
You can test that with commands true and false, which do one thing: generate a zero (true) and non-zero (false) return value. Try this at the command line:
true ; echo "true returns $?"
false ; echo "false returns $?"
You can use any command you want in a condition. There are two commands in particular that have been created with the idea of defining conditions: the classic test command [ ] (the actual command only being the opening bracket, which is also available as the test command), and the double-bracketed, Bash-specific [[ ]] (which is not technically a command, but rather special shell syntax).
For instance, say your switch variable contains either nothing (null string), or something (string with at least one character), and assume in your case you mean a null string to be "false" and any other string to be "true". Then you could use as a condition:
[ "$switch" ]
If you are OK with a string containing only spaces and tabs to be considered empty (which will happen as a result of standard shell expansion and word splitting of arguments to a command), then you may remove the double quotes.
The double-bracket test command is mostly similar, but has some nice things about it, including double-quoting not being needed most of the time, supporting Boolean logic with && and || inside the expression, and having regular expression matching as a feature. It is, however a Bashism.
You can use this as a reference to various tests that can be performed with both test commands:
6.4 Bash Conditional Expressions
If at all interested in shell programming, be sure to find out about the various tests you can use, as you are likely to be using many of them frequently.
As addition to Gordon's excellent answer, in Bash you can also use the double-parentheses construct. It works for integers, and it is the closest form to other languages. Demo:
for i in {-2..2}; do
printf "for %s " "$i"
if (( i )) # You can omit the `$`
then
echo is nonzero
else
echo is zero
fi
done
Output:
for -2 is nonzero
for -1 is nonzero
for 0 is zero
for 1 is nonzero
for 2 is nonzero
You can use any arithmetic operations inside, e.g.:
for i in {1..6}; do
printf "for %s " "$i"
if (( i % 2 )) #modulo
then
echo odd
else
echo even
fi
done
Output
for 1 odd
for 2 even
for 3 odd
for 4 even
for 5 odd
for 6 even

What does ":-" do in a variable declaration?

I have been tasked with assuming control over some bash scripts, and looking through them I've come across the following notation:
INITPATH=${INITPATH:-"include"}
As far as I can tell this does something similar to a = a || b and allows the setting of a default value if the environment variable is not set?
I guess I'm just looking for some clarification on this, and whether the ":-" can be broken down or used in other contexts. I've as yet failed to come across it flicking through various Bash documentation.
From the manual:
${parameter:-word}
If parameter is unset or null, the expansion of word is substituted.
Otherwise, the value of parameter is substituted.
In your example, if INITPATH is unset/null, it's set to include.
This has most of how you can substitute
echo "$\{var}"
echo "Substitute the value of var."
echo "1 - $\{var:-word}"
echo "If var is null or unset, word is substituted for var. The value of var does not change."
echo "2- $\{var:=word}"
echo "If var is null or unset, var is set to the value of word."
echo "5-$\{var:?message}"
echo "If var is null or unset, message is printed to standard error. This checks that variables are set correctly."
echo "3 - $\{var:+word}"
echo "If var is set, word is substituted for var. The value of var does not change."

how to understand - and := in bash?

JOBS=${JOBS:="-j2"}
dir=${1-/usr/src}
What does := and - mean here?
I can guess they serve as some kind of default , what's the difference then?
For := (and the related =), you can use the built-in ':' command to just evaluate the parameter without having to assign it to itself:
# Set JOBS=-j2 if JOBS is not set or equal to ""
: ${JOBS:='-j2'}
# Set JOBS=-j2 if JOBS is not set. Don't change if JOBS is already set to ""
: ${JOBS='-j2'}
For :- and -, don't change the value of the variable; just use the second value in its place:
# Set dir to /usr/src if $1 is not set, but don't set $1 itself
dir=${1-/usr/src}
# Set dir to /usr/src if $1 is not set or set to "", but don't set or change $1
dir=${1:-/usr/src}
man bash (the Parameter Expansion section) will answer these and related questions.
Excerpt:
${parameter:-word}
Use Default Values. If parameter is unset or null, the expansion of word is substituted. Otherwise, the value of parame‐
ter 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 substi‐
tuted.

Resources