Please explain these symbols I found in a shell script - shell

I see the following line in a shell script.
CATALINA_BASE=${CATALINA_BASE:-${APP_HOME}/tomcat}
Is the :- like an if statement? That is, if the environment variable $CATALINA_BASE exists, use its value for the variable CATALINA_BASE?
I also see this line:
APP_USER=${APP_USER:?}
What does the ? mean? In this case, there is no -.

${foo:-bar} and ${foo:bar} are both parameter expansions with defaults. They vary in terms of how they expand an explicitly-set empty string (as opposed to a null, unset string).
See http://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html, http://mywiki.wooledge.org/BashFAQ/073, or http://mywiki.wooledge.org/BashSheet#Parameter_Operations for explanations of these and more.

Related

what is the meaning of : ${CONTAINER_CLI:="docker"} in shell scripting

I am learning shell scripting and came across this line
: ${CONTAINER_CLI:="docker"}
can someone please explain me what this line do? what is the meaning of : here?
man bash command said:
${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.
So, the variable CONTAINER_CLI is assigned with the value docker if CONTAINER_CLI does not exists or empty.
But... if you simply write :
${CONTAINER_CLI:="docker"}
You obtain an error if the result is not a command.
You just want make a assignation.
Put simply a : before.
It's like:
CONTAINER_CLI=${CONTAINER_CLI:="docker"}

What do double colons with parameter expansion do?

What does subjectAltName = ${ENV::subjectAltName} in a shell script mean? I've tried this locally on MacOS and it just returns null.
I've gone to various tutorials on bash scripting and I have found that something like
subjectAltName = ${ENV:-subjectAltName}
would mean if ENV is unset or null, the expansion of subjectAltName is substituted. Otherwise, the value of ENV is substituted but I have not seen double colons in the context of parameter expansion. See here for that tutorial. Also here.
I've also seen this usage:
$ENV::HOME/.rnd
Here, I expect a concatenation for whatever these variables are.
This is used for an openssl.conf file.
It's not shell syntax, it's a feature unique to openssl.conf and the OpenSSL CONF library. The config(5) man page says:
It is also possible to substitute a value from another section using the syntax $section::name or ${section::name}. By using the form $ENV::name environment variables can be substituted.

Unset is not useful? (Korn shell)

I'm reading this:
You can delete a variable with the command unset varname. Normally this is not useful, since all variables that don't exist are assumed to be null, i.e., equal to empty string "". But if you use the option nounset which causes the shell to indicate an error when it encounters an undefined variable, then you may be interested in unset.
My first question is: I cannot see why the use of unset be not useful; if I want to put my variable to null I can use it (or set variable="" or variable=). On the other hand, if I have a variable that doesn't exist, I don't know why I should have to use it..
My second question is: Why may I be interested in unset in that case?
There is a relevant difference between unset and empty variables.
When you can't tell in front which variables will be used, you can process the output of set (examples: https://stackoverflow.com/a/43419722/3220113 and https://stackoverflow.com/a/28104421/3220113 ).
You might have a situaton where you have sourced a read-only config file, but you do not want all lines set in your environment. In that case you might want to unset the settings you do not need.
When you write some utility that uses some variables, you do not want to leave garbage in the environment. Next to using local variables using unset is another possibility.
I think I have found the answer to my question.
1) If you need to remove the definition and the content of a variable you can use unset command. However, unless you turn on the nounset set option, Korn Shell will allow using variables which don't exist, and it will default the content of such a variable as an empty string. That's why you normally don't use unset: because you normally leave the nounset option off and test variables via conditional logic. Hence in these cases, i.e. the inhibition of the use of a variable, it is not useful. (Obviously, it remains useful for deleting variables - as noted by #Walter A, i.e. "" is not unset, the complete removal of the variable.)
2) That said, it follows that if you use the nounset, unset command makes sense. Indeed, if you unset a variable, the shell will disallow using it.

Bash - Why does $VAR1=FOO or 'VAR=FOO' (with quotes) return command not found?

For each of two examples below I'll try to explain what result I expected and what I got instead. I'm hoping for you to help me understand why I was wrong.
1)
VAR1=VAR2
$VAR1=FOO
result: -bash: VAR2=FOO: command not found
In the second line, $VAR1 gets expanded to VAR2, but why does Bash interpret the resulting VAR2=FOO as a command name rather than a variable assignment?
2)
'VAR=FOO'
result: -bash: VAR=FOO: command not found
Why do the quotes make Bash treat the variable assignment as a command name?
Could you please describe, step by step, how Bash processes my two examples?
How best to indirectly assign variables is adequately answered in other Q&A entries in this knowledgebase. Among those:
Indirect variable assignment in bash
Saving function output into a variable named in an argument
If that's what you actually intend to ask, then this question should be closed as a duplicate. I'm going to make a contrary assumption and focus on the literal question -- why your other approaches failed -- below.
What does the POSIX sh language specify as a valid assignment? Why does $var1=foo or 'var=foo' fail?
Background: On the POSIX sh specification
The POSIX shell command language specification is very specific about what constitutes an assignment, as quoted below:
4.21 Variable Assignment
In the shell command language, a word consisting of the following parts:
varname=value
When used in a context where assignment is defined to occur and at no other time, the value (representing a word or field) shall be assigned as the value of the variable denoted by varname.
The varname and value parts shall meet the requirements for a name and a word, respectively, except that they are delimited by the embedded unquoted equals-sign, in addition to other delimiters.
Also, from section 2.9.1, on Simple Commands, with emphasis added:
The words that are recognized as variable assignments or redirections according to Shell Grammar Rules are saved for processing in steps 3 and 4.
The words that are not variable assignments or redirections shall be expanded. If any fields remain following their expansion, the first field shall be considered the command name and remaining fields are the arguments for the command.
Redirections shall be performed as described in Redirection.
Each variable assignment shall be expanded for tilde expansion, parameter expansion, command substitution, arithmetic expansion, and quote removal prior to assigning the value.
Also, from the grammar:
If all the characters preceding '=' form a valid name (see the Base Definitions volume of IEEE Std 1003.1-2001, Section 3.230, Name), the token ASSIGNMENT_WORD shall be returned. (Quoted characters cannot participate in forming a valid name.)
Note from this:
The command must be recognized as an assignment at the very beginning of the parsing sequence, before any expansions (or quote removal!) have taken place.
The name must be a valid name. Literal quotes are not part of a valid variable name.
The equals sign must be unquoted. In your second example, the entire string was quoted.
Assignments are recognized before tilde expansion, parameter expansion, command substitution, etc.
Why $var1=foo fails to act as an assignment
As given in the grammar, all characters before the = in an assignment must be valid characters within a variable name for an assignment to be recognized. $ is not a valid character in a name. Because assignments are recognized in step 1 of simple command processing, before expansion takes place, the literal text $var1, not the value of that variable, is used for this matching.
Why 'var=foo' fails to act as an assignment
First, all characters before the = must be valid in variable names, and ' is not valid in a variable name.
Second, an assignment is only recognized if the = is not quoted.
1)
VAR1=VAR2
$VAR1=FOO
You want to use a variable name contained in a variable for the assignment. Bash syntax does not allow this. However, there is an easy workaround :
VAR1=VAR2
declare "$VAR1"=FOO
It works with local and export too.
2)
By using single quotes (double quotes would yield the same result), you are telling Bash that what is inside is a string and to treat it as a single entity. Since it is the first item on the line, Bash tries to find an alias, or shell builtin, or an executable file in its PATH, that would be named VAR=FOO. Not finding it, it tells you there is no such command.
An assignment is not a normal command. To perform an assignment contained in a quote, you would need to use eval, like so :
eval "$VAR1=FOO" # But please don't do that in real life
Most experienced bash programmers would probably tell you to avoid eval, as it has serious drawbacks, and I am giving it as an example just to recommend against its use : while in the example above it would not involve any security risk or error potential because the value of VAR1 is known and safe, there are many cases where an arbitrary (i.e. user-supplied) value could cause a crash or unexpected behavior. Quoting inside an eval statement is also more difficult and reduces readability.
You declare VAR2 earlier in the program, right?
If you are trying to assign the value of VAR2 to VAR1, then you need to make sure and use $ in front of VAR2, like so:
VAR1=$VAR2
That will set the value of VAR2 equal to VAR1, because when you utilize the $, you are saying that value that is stored in the variable. Otherwise it doesn't recognize it as a variable.
Basically, a variable that doesn't have a $ in front of it will be interpreted as a command. Any word will. That's why we have the $ to clarify "hey this is a variable".

Shell - use of the variable with dash

I was reading tcollector init.sh file here: https://github.com/OpenTSDB/tcollector/blob/master/rpm/initd.sh#L25
what does the dash mean in the line 25TCOLLECTOR=${TCOLLECTOR-/usr/local/tcollector/tcollector.py}?
(I originally thought it just assigns the path after the dash to TCOLLECTOR; however my tests show two different results:
if TCOLLECTOR has already been assigned a value, it will conserve that value
else TCOLLECTOR will have the value "/usr/local/tcollector/tcollector.py"
I also looked at the use of "-" but it's all about STDIN and STDOUT...I didn't get a clue of how they are related to my question.)
Thank you.
That's an example of parameter expansion; the general POSIX variety is documented here, and you can read about the Bash incarnation here.
Basically, the minus sign expansion does exactly what you described: ${anyVariable-anyExpression} expands to the value of $anyVariable if it is set, but if it's not set, then it expands to anyExpression.
The plus sign does exactly the opposite: ${anyVariable+anyExpression} expands to anyExpression if $anyVariable has a value, and to nothing (the empty string) if it is unset.
There are several other options as well.

Resources