Boolean in Shell Scripting - shell

I have a problem with boolean for while loop. As such, I switch to for loop instead.
But still, I cannot change the value of a boolean after the condition is met.
doFirst= true
for (( j=1; j<=7; j++))
do
letter="A"
seatChoses=$letter$j
flagRand=$(echo $flightSeatBooked | awk -v flseatRand=$flightSeatBooked -v orseatRand=$seatChoses '{print match(flseatRand, orseatRand)}')
if $doFirst ; then
**$doFirst= false** // Here is the error!
if [ $flagRand -eq 0 ]; then
echo "System generated a slot, "$seatChoses" for you. [Y or N]"
fi
fi
done

There is no such thing as a boolean value in a shell script (that is, something you can store in a variable, and treat as a boolean). true and false are commands; true exits with value 0, and false exits with a nonzero value. An if statement in bash taks a command; if that command returns 0, then the then clause is executed, otherwise the else clause is.
doFirst= true
This line doesn't do what you expect at all. In a shell script, you cannot have any spaces after the equals sign. The space means you're done with the assignment, and now writing a command. This is equivalent to:
doFirst="" true
Furthermore, if you have an assignment before a command (like this), that doesn't actually perform the assignment in the shell. That sets that environment variable in the environment for that command alone; the assignment has no effect on anything outside of that command.
if $doFirst ; then
This expands the $doFirst variable, and tries to interpret the result as a command. Oddly, if $doFirst is undefined (which it is, as I explain above), this takes the then branch. At that point, you make your first mistake again, trying to set a variable to be false, and again, nothing happens; $doFirst is left undefined. You make the further mistake of trying to assign $doFirst; you use $ to get the value of a variable, when setting, you use the bare name.
My recommendation would be to not try to use booleans in Bash; just use strings instead, and check the value of the string. Note that I remove the space, so now I'm setting it to that exact string; and there is no command, so this sets the variable within the shell, not in the environment for a single command:
doFirst=true
# ...
if [ $doFirst = true ]; then
doFirst=false
# ...

Are you actually putting a space between the = and the "true"/"false" or is that a formatting error? That's one of your problems.
Another, as mentioned by Anders Lindahl in the comment section, is that when you set a variable in shell scripting, you cannot use the $ in the front. You must say
doFirst=false
Again, note that there are no spaces around the equals sign.

Related

How does "FOO= myprogram" in bash make "if(getent("FOO"))" return true in C?

I recently ran into a C program that makes use of an environmental variable as a flag to change the behavior of a certain part of the program:
if (getenv("FOO")) do_this_if_foo();
You'd then request the program by prepending the environment variable, but without actually setting it to anything:
FOO= mycommand myargs
Note that the intention of this was to trigger the flag - if you didn't want the added operation, you just wouldn't include the FOO=. However, I've never seen an environment variable set like this before. Every example I can find of prepended variables sets a value, FOO=bar mycommand myargs, rather than leaving it empty like that.
What exactly is happening here, that allows this flag to work without being set? And are there potential issues with implementing environmental variables like this?
The bash manual says:
A variable may be assigned to by a statement of the form
name=[value]
If value is not given, the variable is assigned the null string.
Note that "null" (in the sense of e.g. JavaScript null) is not a thing in the shell. When the bash manual says "null string", it means an empty string (i.e. a string whose length is zero).
Also:
When a simple command is executed, the shell performs the following expansions, assignments, and redirections, from left to right.
[...]
If no command name results, the variable assignments affect the current shell environment. Otherwise, the variables are added to the environment of the executed command and do not affect the current shell environment.
So all FOO= mycommand does is set the environment variable FOO to the empty string while executing mycommand. This satisfies if (getenv("FOO")) because it only checks for the presence of the variable, not whether it has a (non-empty) value.
Of course, any other value would work as well: FOO=1 mycommand, FOO=asdf mycommand, etc.
FOO= is just setting the variable to null (to be precise it's setting the variable to a zero-byte string, which thus returns a pointer to a NUL terminator - thanks #CharlesDuffy). Given the code you posted it could be FOO='bananas'and produce the same behavior. It's very odd to write code that way though. The common reason to set a variable on the command line is to pass a value for that variable into the script, e.g. to set debugging or logging level flags is extremely common, e.g. (pseudocode):
debug=1 logLevel=3 myscript
myscript() {
if (debug == 1) {
if (loglevel > 0) {
printf "Entering myscript()\n" >> log
if (logLevel > 1) {
printf "Arguments: %s\n" "$*" >> log
}
}
}
do_stuff()
}
Having just a "variable exists" test is a bit harder to work with because then you have to specifically unset the variable to clear the flag instead of just setting FOO=1 when you want to do something and otherwise your script doesn't care when FOO is null or 0 or unset or anything else.

I found this code in an autoconf configure script what is the following code trying to do?

I found this code in an autoconf configure script. What is the following code trying to do?
if ${am_cv_autoconf_installed+:} false; then :
$as_echo_n "(cached) " >&6
else
Lots of stuff going on here. Let's break it down.
First of all, the syntax ${var+foo} is a common idiom for checking whether the variable var has been defined. If var is defined, then ${var+foo} will expand to the string foo. Otherwise, it will expand to an empty string.
Most commonly (in bash, anyway), this syntax is used as follows:
if [ -n "${var+foo}" ]; then
echo "var is defined"
else
echo "var is not defined"
fi
Note that foo is just any arbitrary text. You could just as well use x or abc or ilovetacos.
However, in your example, there are no brackets. So whatever ${am_cv_autoconf_installed+:} expands to (if anything) will be evaluated as a command. As it turns out, : is actually a shell command. Namely, it's the "null command". It has no effect, other than to set the command exit status to 0 (success). Likewise, false is a shell command that does nothing, but sets the exit status to 1 (failure).
So depending on whether the variable am_cv_autoconf_installed is defined, the script will then execute one of the following commands:
: false
-OR-
false
In the first case, it calls the null command with the string "false" as an argument, which is simply ignored, causing the if statement to evaluate to true. In the second case, it calls the false command, causing the if statement to evaluate to false.
So all this is really doing is checking whether am_cv_autoconf_installed is defined. If this were just an ordinary bash script and didn't require any particular level of portability, it would have been a lot simpler to just do:
if [ -n "${am_cv_autoconf_installed+x}" ]; then
However, since this is a configure script, it was no doubt written this way for maximum portability. Not all shells will have the -n test. Some may not even have the [ ] syntax.
The rest should be fairly self-explanatory. If the variable is defined, the if statement evaluates to true (or more accurately, it sets the exit status to 0), causing the $as_echo_n "(cached) " >&6 line to execute. Otherwise, it does whatever is in the else clause.
I'm guessing $as_echo_n is just the environment-specific version of echo -n, which means it will print "(cached) " with no trailing newline. The >&6 means the output will be redirected to file descriptor 6 which presumably is set up elsewhere in the script (probably a log file or some such).

Bash: usage of `true`

In many scripts I've inherited from a former employee I keep seeing this pattern:
if (true $SOME_VAR)&>/dev/null; then
...
fi
or this one
(true $SOME_VAR)&>/dev/null || SOME_VAR="..."
The man page for true says it always returns true, hence I keep wondering, what is the point of these checks? In the first case the then part is always executed, in the second case the right hand part is never executed.
If set -u (a.k.a. set -o nounset) is in effect, true $SOME_VAR will fail when $SOME_VAR is not defined. This is therefore a way to test whether the variable is defined.
To complement jwodder's helpful answer and Fred's helpful answer:
In Bash v4.2+
, the less obscure and more efficient -v operator can be used to test if a variable is defined[1] (note that no $ must be used):
[[ -v SOME_VAR ]]
In older Bash versions and in POSIX-compliant scripts, use Fred's parameter-expansion-based approach, which is also more efficient than the (true ...) approach.
If the intent is to simply provide a default value, as in the (true $SOME_VAR)&>/dev/null || SOME_VAR="..." idiom, use the (POSIX-compliant) technique suggested by kojiro, also based on a parameter expansion:
SOME_VAR=${SOME_VAR-...} # keep $SOME_VAR value or default to '...'
Toby Speight suggests another POSIX-compliant variant, ${SOME_VAR=...}, which directly updates the variable with the default value, if it is undefined; however, it has the side effect of expanding to the (resulting) value - which may or may not be desired. A concise, but also slightly obscure way to suppress the expansion is to pass the expansion to the colon (null) utility (:), which expands, but otherwise ignores its arguments (compared to using true for the same purpose, it is perhaps slightly less confusing):
: ${SOME_VAR=...} # set $SOMEVAR to '...' only if not defined
Note that all parameter expansions shown/mentioned above have a variant that places : before the operator, which then acts not only when the variable is undefined, but also when it is defined but empty (contains the null string):
${SOME_VAR:+...}, ${SOME_VAR:-...}, ${SOME_VAR:=...}
Arguably, this variant behavior is the generally more robust technique, especially given that when set -u (set -o nunset) is not turned on, undefined variables expand to the null (empty) string.
To add to jwodder's explanation:
The use of (...) around true $SOME_VAR to create a subshell is crucial for this somewhat obscure test for variable existence to work as intended.
Without a subshell, the entire script would abort.
The need for a subshell makes the technique not just obscure, but also inefficient (although that won't really be noticeable with occasional use).
Additionally, if set -u (set -o nounset) happens not to be in effect, the technique treats all variables as defined.
With the subshell, only the subshell aborts, which is reflected in its exit code to the current shell: 1, if the subshell aborted (the variable doesn't exist), 0 otherwise.
Therefore, the (true ...) command only evaluates to (conceptually) true if the variable exists.
&>/dev/null suppresses the error message from the subshell that is emitted if the variable doesn't exist.
As an aside: true never produces no output, so it is sufficient to use (true $SOME_VAR)2>/dev/null (suppress stderr only) - this change makes the technique POSIX-compliant (though still not advisable).
It isn't just set -u (set -o nounset) statements inside a script that turn on aborting in case of access to an undefined variable - invoking bash explicitly with command-line option -u has the same effect.
[1] Since Bash v4.3, you can also test whether an array variable has an element with the specified index; e.g.:
a=( one two ); [[ -v a[0] ]] succeeds, because an array element with index 0 exists; works analogously with associative arrays.
The following is probably equivalent, and more straightforward :
if [ "${SOME_VAR+x}" ] then
...
fi
Or, in the assignment case :
[ "${SOME_VAR+x}" ] || SOME_VAR="..."
The + expansion operator expands to a null string if the variable is unset, and to x if it is assigned (assigned a null string still means assigned). In this case, you could replace x by whatever you want (except a null string).
There is also a ${SOME_VAR:+x} variant. The difference is with null strings : :+ expands to a null string if the variable is assigned a null string (while + expands to x if the value is assigned, even if it is a null string).
While not strictly the same,
if [ x"$SOME_VAR" = x ]; then
...
fi
tends to do what you want; that is the if is true if $SOME_VAR is undefined or (difference:) defined to be the zero-length string.
This code does not work if SOME_VAR is unset and -u is set. I believe the following bashism works though: "${SOME_VAR-}" = "".

Bash assignment value to variable as command substitution and print value output

I would like to achieve this in Bash: echo $(a=1)and print the value of variable a
I test eval, $$a,{}, $() but none of them work as most of the times either I got literally a=1 or in one case (I don't remember which) it tried to execute the value.
I known that I can do: a=1;echo $a but because I'm little fun one command per line (even if sometimes is getting little complicated) I was wondering if is possible to do this either with echo or with printf
If you know that $a is previously unset, you can do this using the following syntax:
echo ${a:=1}
This, and other types of parameter expansion, are defined in the POSIX shell command language specification.
If you want to assign a numeric value, another option, which doesn't depend on the value previously being unset, would be to use an arithmetic expansion:
echo $(( a = 1 ))
This assigns the value and echoes the number that has been assigned.
It's worth mentioning that what you're trying to do cannot be done in a subshell by design because a child process cannot modify the environment of its parent.

Bash Script Understanding

I'm trying to figure what exactly is the bash code mentioned below trying to do, specially the [-z $M ] part. here M is a variable with a value
if [ -z $M ] ; then
can not find module directory
exit 1
man test Enter
press /-zEnter
you see:
-z STRING
the length of STRING is zero
so your script does, if $M length==0, then exit with status code 1
As others have said, it's using the test command (aka [) to check whether a string is blank. At least, that's what it's trying to do; because the string ($M) isn't double-quoted, it's actually doing something slightly different. Without double-quotes, the value of $M undergoes word splitting and wildcard expansion after it's replaced, so it might not be treated as a simple string (which the -z operator works on) with ... potentially unexpected consequences. Let me run through some of the possibilities:
If the value of $M is a single word (non-blank) without wildcards (* and ?), everything works as expected.
If the value of $M is zero-length (blank), the test command only sees a single argument (-z); when test is only given a single argument, it simply tests whether it's blank -- it's not, so it evaluates to true.
This happens to be the expected result in this case, but it's purely by coincidence, and with many other operators it wouldn't be the right result. For instance, [ -n $M ] (which looks like it should test whether $M is *non*blank), [ -e $M ] (which looks like it should test whether $M is the name of a file/directory) etc will all evaluate to true if $M is blank.
If the value of $M consists entirely of whitespace (but isn't empty), it gets eliminated before test sees it, and test evaluates to true (see previous case). This might or might not be what the scripter had in mind.
If the value of $M has multiple words, test will attempt to evaluate it as (part of) an expression. It will probably not be a valid expression, in which case test will print an error and return false (which is right ... sort of).
On the other hand, if it is a valid expression... Suppose for example you had, M='= -z; test would evaluate the expression -z = -z which would be true, not at all what the scripter had in mind.
If the value of $M has any wildcards, the shell will try to match them against files and pass test the list of matches; it'll try to evaluate them as an expression (see previous case), probably giving an error and returning false (again, sort of right).
Mind you, if you happen to have set the nullglob shell option and the wildcard doesn't match any files, the shell will replace it with null, and the script will act as though "u*n*m*a*t*c*h*e*d" was the empty string.
The lesson here: if you don't want your scripts to behave in weird and unexpected ways, double-quote your variable references!
The [ is actually a standard Unix command (probably implemented internally in Bash, but available whatever shell you are using). It is an alias for the command test, so its manual entry can be found by typing man test. Here's an online copy of that manual page.
When invoked as [, test will generally expect its last argument to be a ], just for good looks, so [ -z $M ] is equivalent to test -z $M.
In this case, the -z argument causes test to return true if the following argument is a string of length zero. The variable $M, defined further up the script, can thus be tested for a valid value.
It checks whether the content of variable M is an empty string.
Check this link

Resources