What does ${VARIABLE+set} mean? - bash

I'm looking through someone else's code and am not sure what this means. It is either a variable call VARIABLE+set which is a strange variable name since is has a +, or is is evaluated and is hard to Google because it has ${} in it ;)

It took be some time, but I found a link explaining what this does. It is a form of bash parameter-substitution that will evaluate to "set" if $VARIABLE has been set and null otherwise. This allows you to check if a variable is set by doing the following:
if [ -z "${VARIABLE+set}" ] ; then
echo "VARIABLE is not set"
fi
It is also interesting to note that ${VARIABLE+set} can just as easily be ${VARIABLE+anything}. The only reason for using +set is because it is slightly more self-documenting (although not enough to keep me from asking this question).

Related

How does $RANDOM work in Unix shells? Looks like a variable but it actually assumes different values each time it's called

I recently used the $RANDOM variable and I was truly curious about the under-the-hood implementation of it: the syntax says it's a variable but the behavior says it's like a function as it returns a different value each time it's called.
This is not "in Unix shells"; this is a Bash-specific feature.
It's not hard to guess what's going on under the hood; the shell special-cases this variable so that each attempt to read it instead fetches two bytes from a (pseudo-) random number generator.
To see the definition, look at get_random in variables.c (currently around line 1363).
about the under-the-hood implementation of it
There are some special "dynamic variables" with special semantics - $PWD $HOME $LINENO etc. When bash gets the value of the variable, it executes a special function.
RANDOM "variable" is setup here bash/variables.c and get_random() just sets the value of the variable, taking random from a simple generator implementation in bash/random.c.

Alternative for-loop construct

General comment: any new answer which gives a new and useful insight into this question will be rewarded with a bonus.
The Bash reference manual mentions that Bash supports the
following for-loop constructs:
for name [ [in [words ...] ] ; ] do commands; done
for (( expr1 ; expr2 ; expr3 )) ; do commands ; done
Surprisingly, the following for-loop constructs are also valid:
for i in 1 2 3; { echo $i; }
for ((i=1;i<=3;++i)); { echo $i; }
These unusual constructs are not documented at all. Neither the Bash
manual, the Bash man-pages nor The Linux Documentation
Project make any mention of these constructs.
When investigating the language grammar one can see that using
open-and-close braces ({ commands; }) as an alternative to do commands; done is a valid construct that is implemented for both
for-loops and select statements and dates back to Bash-1.14.7
[1].
The other two loop-constructs:
until test-commands; do consequent-commands; done
while test-commands; do consequent-commands; done
do not have this alternative form.
Since a lot of shell-languages are related, one can find that these
constructs are also defined there and mildly documented. The KSH manual mentions:
For historical reasons, open and close braces may be used instead of do and done e.g.
for i; { echo $i; }
while ZSH implements and documents similar alternatives for the other loop-constructs, but with limitations. It states:
For the if, while and until commands, in both these cases the
test part of the loop must also be suitably delimited, such as by
[[ ... ]] or (( ... )), else the end of the test will not be recognized.
Question: What is the origin of this construct and why is
this not propagated to the other loop-constructs?
Update 1: There are some very useful and educational comments below
this post pointing out that this is an undocumented Bourne Shell feature which seems to be the result of a C-vs-sh language battle in the early days.
Update 2: When asking the question: Why is this language feature not documented? to the Gnu Bash mailinglist, I received the following answer from Chet Ramey (current lead-developer of GNU bash):
It's never been documented. The reason bash supports it (undocumented) is
because it was an undocumented Bourne shell feature that we implemented
for compatibility. At the time, 30+ years ago, there were scripts that used
it. I hope those scripts have gone into the dustbin of history, but who
knows how many are using this construct now.
I'm going to leave it undocumented; people should not be using it anyway.
Related questions/answers:
A bash loop with braces?
Hidden features of Bash (this answer)
[U&L] What is the purpose of the “do” keyword in Bash for loops?
Footnotes: [1] I did not find earlier versions, I do believe it predates this
[W]hy is this not propagated to the other loop-constructs?
Braced forms of while and until commands would be syntactically ambiguous because you can't separate test-commands from consequent-commands without having a distinctive delimiter between them as they are both defined by POSIX to be compound lists.
For example, a shell that supports such constructs can choose either one of the brace groups in the command below as consequent-commands and either way it would be a reasonable choice.
while true; { false; }; { break; }
Because of its ambiguous form, this command can be translated to either of the below; neither is a more accurate translation than the other, and they do completely different things.
while true; do
false
done
break
while true; { false; }; do
break
done
The for command is immune to this ambiguity because its first part—a variable name optionally followed by in and a list of words, or a special form of the (( compound command—can easily be distinguished from the brace group that forms its second part.
Given that we already have a consistent syntax for while and until commands, I don't really see any point in propagating this alternate form to them.
Wrt its origin, see:
Characteristical common properties of the traditional Bourne shells,
Stephen Bourne's talk at BSDCon,
Unix v7 source code, sh/cmd.c.

How to test if a variable is set or not (empty strings count as set)

I searched this site with Google for an answer that explains how to test if a variable is set or not in a shell (I'm using bash, but I'd prefer the solution to be portable) in the strictest sense of the word, but I failed to find any results.
The problem is that the common answers do not actually check to determine whether a variable is set or not. Instead, they check to see if it is empty or not. If I type var="", then var is set to an empty string. Nonetheless, it has still been set. Running [ -z "${var}" ] will return true despite the variable existing.
How do I test if a variable is set or not across various shells as opposed to just empty or not?

Should I use "test" or "[" "]" in POSIX shell?

I believe both of the following code snippets are valid in POSIX compliant shell:
Option 1:
if [ "$var" = "dude" ]
then
echo "Dude, your var equals dude."
fi
Option 2:
if test "$var" = "dude"
then
echo "Dude, your var equals dude."
fi
Which syntax is preferred and why? Is there a reason to use one over the other in certain situations?
There is no functional difference, making this a purely stylistic choice with no widely accepted guidelines. The bash-hackers wiki has an extended section on classic (POSIX-compliant) test, with a great deal of attention to best practices and pitfalls, and takes no position on which to prefer.
Moreover, the POSIX specification for test -- while it does mark a great deal of functionality obsolescent1 -- specifies neither form as preferred over the other.
That said, one advantage to test is that it's less conducive to folks bringing in expectations from other languages which result in broken or buggy code. For instance, it's a common error to write [$foo=1] rather than the correct [ "$foo" = 1 ], but folks aren't widely seen to write test$foo=1: It's more visually obvious that test "$foo" = 1 is following the same parsing rules as other shell commands, and thus requires the same care regarding quoting and whitespace.
[1] Such as -a, -o, ( and ), and any usage with more than four arguments (excluding the trailing ] on an instance started under the name [).

How to call i in a for loop? [duplicate]

This question already has answers here:
What is the difference between $(command) and `command` in shell programming?
(6 answers)
Closed 7 years ago.
So, this question seems a-specific. It is, because I'm not a BASH-programmer, rather a Biologist-turned-writing-some-useful-scripts-for-my-daily-work-scripter. Anyway. Say, I have a for loop, like so:
for CHR $(seq 1 22); do
echo "Processing chromosome ${CHR}";
done
I used to write `seq 1 22` but now I've learned to write $(seq 1 22). Clearly there is a difference in terms of the way you write it. But what is the difference in terms in computer language and interpretation? Can someone explain that to me?
The other thing I learned by simply doing on the command line on our computer cluster, was to call "i" differently. I used to do: $CHR. But when I'd have a file name sometext_chr to which I'd like to add the number (sometext_chr$CHR) that wouldn't work. What does work is sometext_chr${CHR}. Why is that? Can someone help me explain the difference?
Again, I know the question is a bit a-specific - I simply didn't know how to otherwise frame it - but I hope someone can teach me the differences.
Thanks and best!
Sander
The $(...) can be nested easily, as the parentheses clearly indicate where an expression starts and ends. Using `, nesting is not so simple, as the start and end symbols are the same.
Your second example is probably from memory, because it's incorrect. sometext$chr and sometext${chr} would both work the same way. Perhaps what you really meant was a situation like this:
$chr_sometext
${chr}_sometext
The key point here is that _ is a valid character in a variable name. As a result, $chr_sometext is interpreter as the value of the variable chr_sometext. In ${chr}_sometext the variable is clearly chr, and the _sometext that follows it is a literal string value. Just like if you wrote $chrsometext you wouldn't assume that the chr is somehow special. This is the reason you have to add the clarifying braces.

Resources