Number Base in Bash - bash

To force numbers to be interpreted in base10, you can prefix with 10#. Specifically 10#08 and 10#09 will be interpreted as valid decimal numbers, and not invalid octal numbers. (I'm taking the output of date +%S)
However, it seems I then can't use the variable in comparisons:
x=10#08
y=10#20
echo $((x+y)) // (returns 28, as expected)
while [ $x -lt $y ]
do
x=$((x++))
done
gives me the error
-bash: [: 10#08: integer expression expected
Is this a bug in bash?

bash's [ builtin mostly emulates the old standard [ command (aka test, and yes it's really a command), which doesn't know about these newfangled base marks. But bash's arithmetic expressions ((( ))) and conditional expressions ([[ ]]) do:
$ x=10#08
$ y=10#20
$ echo $((x+y))
28
$ [ $x -lt $y ] && echo yes
-bash: [: 10#08: integer expression expected
$ /bin/[ $x -lt $y ] && echo yes # This uses external test cmd instead of builtin
[: 10#08: bad number
$ [[ $x -lt $y ]] && echo yes
yes
$ ((x<y)) && echo yes
yes
For purely arithmetic tests, (( )) is generally easiest to use. But both are bash extensions (i.e. not available in the brand-X shell), so be sure to start your script with #!/bin/bash, not #!/bin/sh.

How about [ $(($x)) -lt $(($y)) ] or [[ $x -lt $y ]] ?

Not a bug. The shell doesn't do arithmetic evaluation in conditional expressions. See the sections Arithmetic Expansion and ARITHMETIC EVALUATION in man bash for details about when arithmetic evaluation is done.

Related

octal expansion difference between [ and [[?

As far as I had known, [[ and [ can be expected to behave mostly the same, taking into account a few extra features [[ has. But recently I noticed a discrepancy in how bash treats octal expansions:
$ b=010; echo $((b))
8
$ [[ $b -eq 8 ]]; echo $?
0
but
$ [ $b -eq 8 ]; echo $?
1
$ test $b -eq 8; echo $?
1
$ [ $b -eq 10 ]; echo $?
0
Why does the latter expression drop the auto octal conversion? Expressions like -eq are "Arithmetic" according to help test in Bash and the Bash Reference Manual, and further according to the next section of the reference manual constants with a leading zero can be treated as octal.
POSIX sh is a little less clear on the subject: Even though POSIX arithmetic expressions still expand leading-zero integers to their octal value, it refers to -eq expressions in test as algebraic, not arithmetic.
Is there any documentation or evidence to suggest that bash makes a distinction between [[ and [ for octal expansion on purpose, or is it just an accidental feature?
[[ is known as the extended test command and behaves as ksh88, you can find an elaboration here:
http://tldp.org/LDP/abs/html/testconstructs.html#DBLBRACKETS
Also if you need to be sure of the base in bash you can use the # base operator like this:
b=010; echo $((10#$b))
10
b=10; echo $((8#$b)
8
b=013; echo $((8#$b))
11

sh shell double if statement

Can anyone see what I did wrong here? I keep getting the following error message: [[: not found
read INPUT
if [[ "$INPUT" -ge 1 ]] && [[ "$INPUT" -le 10 ]]; then
Do something
else
printf "Please enter a value between 1 and 10"
fi
[[ is not available in scripts which start with #!/bin/sh, or which are started with sh yourscript. Start your script with #!/bin/bash if you want to use it.
See also http://mywiki.wooledge.org/BashGuide/Practices#Choose_Your_Shell
If you are going to use bash, by the way, there's a better syntax for numeric comparisons:
if (( input >= 1 && input <= 10 )); then ...
Note that lower-case variable names are preferred for local use -- all-upper-case names are reserved for environment variables and shell builtins.
If you're not going to use bash, use the POSIX test operator:
if [ "$input" -ge 1 ] && [ "$input" -le 10 ]; then ...
Note that when using [ ] correct quoting is essential, whereas with [[ ]] it is often superfluous; also, [ ] is missing some extensions such as pattern-matching and regular-expression operators.
It's complicated:
First, there are three separate ways of constructing your if statement. Each way has its own unique syntax on how to join two booleans. (Actually, there are four ways since one way allows you to use list operators).
A little background...
The if command is a compound command built into the shell. The if command executes the commands following the if. If that command returns a zero value, the if statement is considered true and the then clause executes. Otherwise, if it exists, the else clause will execute. Remember, the if is just a command. You can do things like this:
if ! mv "$foo" "$bar"
then
echo "I can't move $foo to $bar"
exit 2
fi
What we need is a command to do some testing for us. If the test succeeds, that test command returns an exit code of zero. If not, it returns a non-zero exit code. Then, it could be used with the if command!
The test command (Yes, there's really one!).
The [ is an alias for the test command which was created to allow you to test files, strings, and numbers for the if statement. (This is now a built in command in Bash, but its roots are actually part of /bin/test and /bin/[). These are the same:
if test "$foo" -eq "$bar"
then
...
fi
and
if [ "$foo" -eq "$bar" ]
then
...
fi
The test command (if you read the manpage has a -a And test and a -o Or test. You could have done:
if [ "$INPUT" -ge 1 -a "$INPUT" -le 10 ]
then
....
fi
This is a single test statement with three test parameters (-ge, -a, and -le).
Using List Operators
This isn't the only way to do a compound boolean test. The Bash shell has two list operators: && and ||. The list operators go in between two commands. If you use && and the left hand command returns a non-zero exit code, the right hand command is not executed, and the entire list returns the exit value of the left-hand command. If you use ||, and the left hand command succeeds, the right hand command is not executed, and the entire list returns a zero exit value. If the first command returns a non-zero exit value, the right-hand command is executed, and the entire list returns the exit value of the right-hand command.
That's why you can do things like this:
[ $bar -eq 0 ] || echo "Bar doesn't have a zero value"!
Since [ ... ] is just a command that returns a zero or non-zero value, we can use these list operators as part of our test:
if [ "$INPUT" -ge 1 ] && [ "$INPUT" -le 10 ]
then
...
fi
Note that this is two separate tests and are separated by a && list operator.
Bash's Special [[ compound command
In Kornshell, Zsh, and Bash, there are special compound commands for testing. These are the double square brackets. They appear to be just like the single square brackets command, but because they're compound commands, parsing is affected.
For example:
foo="This has white space"
bar="" #No value
if [ ! $foo = $bar ] # Doesn't work!
then
The shell expands $foo and $bar and the test will become:
if [ This has white space = ]
which just doesn't work. However,
if [[ $foo != $bar ]]
works fine because of special parsing rules. The double brackets allow you to use parentheses for grouping and && and || as boolean operators. Thus:
if [[ $INPUT -ge 1 && $INPUT -le 10 ]]
then
...
fi
Note that the && appears inside a single set of double square brackets. (Note there's no need for quotation marks)
Mathematical Boolean Expression
Bash has built in mathematical processing including mathematical boolean expressions. If you put something between double parentheses, Bash will evaluate it mathematically:
if (( $INPUT >= 1 && $INPUT <= 10 ))
then
...
fi
In this case, (( $INPUT >= 1 && $INPUT <= 10 )) is evaluated. If $INPUT is between 1 and 10 inclusively, the mathematical expression will evaluate as true (zero exit code), and thus the then clause will be executed.
So, you can:
Use the original test (single square brackets) command and use the -a to string together two boolean statements in a single test.
Use list operators to string together two separate test commands (single square brackets).
Use the newer compound test command (double square brackets) that now include && and || as boolean operators, so you have a single compound test.
Forget about test command and just use mathematical evaluation (double parentheses) to evaluate boolean expressions.
Test Constructs Can Vary by Shell
As has been mentioned in other posts, [[ is a Bash shell keyword that isn't present in the Bourne shell. You can see this from a Bash prompt with:
type '[['
[[ is a shell keyword
In a Bourne shell, you will instead get "command not found."
Be More Portable: Use the -a Test Operator
A more portable construct is to use the -a test operator to join conditions (see man test for details). For example:
if [ "$INPUT" -ge 1 -a "$INPUT" -le 10 ]; then
: # do something when both conditions are true
else
: # do something when either condition is false
fi
This will work in every Bourne-compatible shell I've ever used, and on any system that has a /bin/\[ executable.

Two while loops behaving strangely, Bash script

I'm new to Bash scripting. I have written a script to help me get some info using ssh from bunch of servers. the IP address of first set of devices are from 101 to 148, and the other set are from 201 to 210.
#!/bin/bash
BASE=192.168.11
SD_START=101
SD_END=148
HD_START=201
HD_END=210
SD_counter=$SD_START
HD_counter=$HD_START
while [[ $SD_counter -le $SD_END ]]
do
ip=$BASE.$SD_counter
ssh $ip command1
SD_counter=$(($SD_counter +1))
if [ "$SD_counter"==148 ]
then
while [[ $HD_counter -le $HD_END ]]
do
ip=$BASE.$HD_counter
ssh $ip command2
HD_counter=$(($HD_counter +1))
done
fi
done > log_SD_HD
echo "Done!"
But for some reason command1 is executed on 192.168.11.101 first, then command2 is executed on ip range 192.168.11.201-192.168.11.210 which is the second while loop.
After that the first while loop continues till the end.
Why is this happening? I want the first while loop to be done before the second while loop. Could someone please point out what I'm doing wrong?
#0x1cf's answer provides the right pointer:
[ "$SD_counter"==148 ] doesn't work as expected.
Specifically: "$SD_counter"==148, based on bash's string synthesizing rules, is expanded to a single string literal: the value of $SD_counter is concatenated with literal ==148, and the resulting string literal is treated as a Boolean.
Since a non-empty string in a Boolean context always evaluates to true, [ "$SD_counter"==148 ] always evaluates to true due to lack of spaces around the ==.
Aside from that: in bash you should use [[ ... ]] rather than [ ... ] - it is more robust and provides more features.
Also note (as #0x1cf notes too) that - if using [ ... ] or [[ ... ]] - using the arithmetic operators is the right choice when dealing with numbers: -eq, -ne, -lt, -le, -gt, or -ge.
Generally, though, using (( ... )) expressions - arithmetic evaluation - provides more flexibility with numbers - see below.
That said, your code can be greatly simplified by using arithmetic evaluation - (( ... )) (see section ARITHMETIC EVALUATION in man bash):
It allows you to use C-style arithmetic and Boolean expressions.
If we combine this with bash's array variables, your code can be simplified to:
#!/usr/bin/env bash
BASE=192.168.11
START_INDICES=( 101 201 )
END_INDICES=( 148 210 )
COMMANDS=( command1 command2 )
numRanges=${#START_INDICES[#]}
for (( range = 0; range < numRanges; ++range )); do
cmd=${COMMANDS[range]}
for (( i=${START_INDICES[range]}; i<=${END_INDICES[range]}; ++i )); do
ip=$BASE.$i
ssh $ip $cmd
done
done > log_SD_HD
Note:
(( ... )) expressions DIFFER from normal bash assignments and conditionals in that you:
need NOT reference variables with $
need NOT double-quote variable references
you MAY have spaces around the assignment operator (=)
you MAY omit spaces around relational operators: (( SD_counter==148 )) DOES work.
( string1 ... ) creates an array with elements string1, ...; ${#arrayVar[#]} returns the count of elements of array variable arrayVar; ${arrayVar[ndx]} returns the element with (0-based) index ndx.
It's better to avoid ALL-UPPERCASE variable names such as BASE, as they may conflict with environment variables, which are by convention typically all-uppercase.
UPDATE
Hint: You can always use #!/bin/bash -x to trace and debug your scripts.
Maybe using two while loop is a good idea, just as V_Maenolis showed. However, to answer your question about what's wrong with your script, try this
Replace
if [ "$SD_counter"==148 ]
with
if [ "$SD_counter" -gt 148 ]
which works for me.
So there are two errors
There should be a space before and after == operator, that is to say, using A == B NOT A==B
The logic of comparing SD_counter == 148 is incorrect. Because when SD_counter hits 148, your script will run into the second while loop, and you'll get 147, 201, ..., 210, 148. Using -gt instead avoids the problem.
There is no reason to nest the loops from what you showed:
#!/bin/bash
BASE=192.168.11
SD_START=101
SD_END=148
HD_START=201
HD_END=210
SD_counter=$SD_START
HD_counter=$HD_START
while [[ $SD_counter -le $SD_END ]]
do
ip=$BASE.$SD_counter
ssh $ip command1
SD_counter=$(($SD_counter +1))
done> log_SD_HD
while [[ $HD_counter -le $HD_END ]]
do
ip=$BASE.$HD_counter
ssh $ip command2
HD_counter=$(($HD_counter +1))
done>> log_SD_HD
echo "Done!"

unary operator expected in shell script when comparing null value with string

I have two variables
var=""
var1=abcd
Here is my shell script code
if [ $var == $var1 ]; then
do something
else
do something
fi
If I run this code it will prompt a warning
[: ==: unary operator expected
How can I solve this?
Since the value of $var is the empty string, this:
if [ $var == $var1 ]; then
expands to this:
if [ == abcd ]; then
which is a syntax error.
You need to quote the arguments:
if [ "$var" == "$var1" ]; then
You can also use = rather than ==; that's the original syntax, and it's a bit more portable.
If you're using bash, you can use the [[ syntax, which doesn't require the quotes:
if [[ $var = $var1 ]]; then
Even then, it doesn't hurt to quote the variable reference, and adding quotes:
if [[ "$var" = "$var1" ]]; then
might save a future reader a moment trying to remember whether [[ ... ]] requires them.
Why all people want to use '==' instead of simple '=' ? It is bad habit! It used only in [[ ]] expression. And in (( )) too. But you may use just = too! It work well in any case. If you use numbers, not strings use not parcing to strings and then compare like strings but compare numbers. like that
let -i i=5 # garantee that i is nubmber
test $i -eq 5 && echo "$i is equal 5" || echo "$i not equal 5"
It's match better and quicker. I'm expert in C/C++, Java, JavaScript. But if I use bash i never use '==' instead '='. Why you do so?

Are double square brackets [[ ]] preferable over single square brackets [ ] in Bash?

A coworker claimed recently in a code review that the [[ ]] construct is to be preferred over [ ] in constructs like
if [ "`id -nu`" = "$someuser" ] ; then
echo "I love you madly, $someuser"
fi
He couldn't provide a rationale. Is there one?
[[ has fewer surprises and is generally safer to use. But it is not portable - POSIX doesn't specify what it does and only some shells support it (beside bash, I heard ksh supports it too). For example, you can do
[[ -e $b ]]
to test whether a file exists. But with [, you have to quote $b, because it splits the argument and expands things like "a*" (where [[ takes it literally). That has also to do with how [ can be an external program and receives its argument just normally like every other program (although it can also be a builtin, but then it still has not this special handling).
[[ also has some other nice features, like regular expression matching with =~ along with operators like they are known in C-like languages. Here is a good page about it: What is the difference between test, [ and [[ ? and Bash Tests
Behavior differences
Some differences on Bash 4.3.11:
POSIX vs Bash extension:
[ is POSIX
[[ is a Bash extension inspired from KornShell
regular command vs magic
[ is just a regular command with a weird name.
] is just the last argument of [.
Ubuntu 16.04 actually has an executable for it at /usr/bin/[ provided by coreutils, but the Bash built-in version takes precedence.
Nothing is altered in the way that Bash parses the command.
In particular, < is redirection, && and || concatenate multiple commands, ( ) generates subshells unless escaped by \, and word expansion happens as usual.
[[ X ]] is a single construct that makes X be parsed magically. <, &&, || and () are treated specially, and word splitting rules are different.
There are also further differences like = and =~.
In Bashese: [ is a built-in command, and [[ is a keyword: What's the difference between shell builtin and shell keyword?
<
[[ a < b ]]: lexicographical comparison
[ a \< b ]: Same as above. \ required or else does redirection like for any other command. Bash extension.
expr x"$x" \< x"$y" > /dev/null or [ "$(expr x"$x" \< x"$y")" = 1 ]: POSIX equivalents, see: How to test strings for lexicographic less than or equal in Bash?
&& and ||
[[ a = a && b = b ]]: true, logical and
[ a = a && b = b ]: syntax error, && parsed as an AND command separator cmd1 && cmd2
[ a = a ] && [ b = b ]: POSIX reliable equivalent
[ a = a -a b = b ]: almost equivalent, but deprecated by POSIX because it is insane and fails for some values of a or b like ! or ( which would be interpreted as logical operations
(
[[ (a = a || a = b) && a = b ]]: false. Without ( ) it would be true, because [[ && ]] has greater precedence than [[ || ]]
[ ( a = a ) ]: syntax error, () is interpreted as a subshell
[ \( a = a -o a = b \) -a a = b ]: equivalent, but (), -a, and -o are deprecated by POSIX. Without \( \) it would be true, because -a has greater precedence than -o
{ [ a = a ] || [ a = b ]; } && [ a = b ] non-deprecated POSIX equivalent. In this particular case however, we could have written just: [ a = a ] || [ a = b ] && [ a = b ], because the || and && shell operators have equal precedence, unlike [[ || ]] and [[ && ]] and -o, -a and [
word splitting and filename generation upon expansions (split+glob)
x='a b'; [[ $x = 'a b' ]]: true. Quotes are not needed
x='a b'; [ $x = 'a b' ]: syntax error. It expands to [ a b = 'a b' ]
x='*'; [ $x = 'a b' ]: syntax error if there's more than one file in the current directory.
x='a b'; [ "$x" = 'a b' ]: POSIX equivalent
=
[[ ab = a? ]]: true, because it does pattern matching (* ? [ are magic). Does not glob expand to files in the current directory.
[ ab = a? ]: a? glob expands. So it may be true or false depending on the files in the current directory.
[ ab = a\? ]: false, not glob expansion
= and == are the same in both [ and [[, but == is a Bash extension.
case ab in (a?) echo match; esac: POSIX equivalent
[[ ab =~ 'ab?' ]]: false, loses magic with '' in Bash 3.2 and above and provided compatibility to Bash 3.1 is not enabled (like with BASH_COMPAT=3.1)
[[ ab? =~ 'ab?' ]]: true
=~
[[ ab =~ ab? ]]: true. POSIX extended regular expression match and ? does not glob expand
[ a =~ a ]: syntax error. No Bash equivalent.
printf 'ab\n' | grep -Eq 'ab?': POSIX equivalent (single-line data only)
awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?': POSIX equivalent.
Recommendation: always use []
There are POSIX equivalents for every [[ ]] construct I've seen.
If you use [[ ]] you:
lose portability
force the reader to learn the intricacies of another Bash extension. [ is just a regular command with a weird name, and no special semantics are involved.
Thanks to Stéphane Chazelas for important corrections and additions.
[[ ]] has more features - I suggest you take a look at the Advanced Bash Scripting Guide for more information, specifically the extended test command section in Chapter 7. Tests.
Incidentally, as the guide notes, [[ ]] was introduced in ksh88 (the 1988 version of KornShell).
From Which comparator, test, bracket, or double bracket, is fastest?:
The double bracket is a “compound
command” where as test and the single
bracket are shell built-ins (and in
actuality are the same command). Thus,
the single bracket and double bracket
execute different code.
The test and single bracket are the
most portable as they exist as
separate and external commands.
However, if your using any remotely
modern version of BASH, the double
bracket is supported.
If you are into following Google's style guide:
Test, [ … ], and [[ … ]]
[[ … ]] is preferred over [ … ], test and /usr/bin/[.
[[ … ]] reduces errors as no pathname expansion or word splitting takes place between [[ and ]]. In addition, [[ … ]] allows for regular expression matching, while [ … ] does not.
# This ensures the string on the left is made up of characters in
# the alnum character class followed by the string name.
# Note that the RHS should not be quoted here.
if [[ "filename" =~ ^[[:alnum:]]+name ]]; then
echo "Match"
fi
# This matches the exact pattern "f*" (Does not match in this case)
if [[ "filename" == "f*" ]]; then
echo "Match"
fi
# This gives a "too many arguments" error as f* is expanded to the
# contents of the current directory
if [ "filename" == f* ]; then
echo "Match"
fi
For the gory details, see E14 at http://tiswww.case.edu/php/chet/bash/FAQ
In a question tagged 'bash' that explicitly has "in Bash" in the title, I'm a little surprised by all of the replies saying you should avoid [[...]] because it only works in bash!
It's true that portability is the primary objection: if you want to write a shell script which works in Bourne-compatible shells even if they aren't bash, you should avoid [[...]]. (And if you want to test your shell scripts in a more strictly POSIX shell, I recommend dash; though it is an incomplete POSIX implementation since it lacks the internationalization support required by the standard, it also lacks support for most, but not all, of the many non-POSIX constructs found in bash, ksh, zsh, etc.)
The other objection I see is at least applicable within the assumption of bash: that [[...]] has its own special rules which you have to learn, while [...] acts like just another command. That is again true (and Mr. Santilli brought the receipts showing all the differences), but it's rather subjective whether the differences are good or bad. I personally find it freeing that the double-bracket construct lets me use (...) for grouping, && and || for Boolean logic, < and > for comparison, and unquoted parameter expansions. It's like its own little closed-off world where expressions work more like they do in traditional, non-command-shell programming languages.
A point I haven't seen raised is that this behavior of [[...]] is entirely consistent with that of the arithmetic expansion construct $((...)), which is specified by POSIX, and also allows unquoted parentheses and Boolean and inequality operators (which here perform numeric instead of lexical comparisons). Essentially, any time you see the doubled bracket characters you get the same quote-shielding effect.
(Bash and its modern relatives also use ((...)) – without the leading $ – as either a C-style for loop header or an environment for performing arithmetic operations; neither syntax is part of POSIX.)
So there are some good reasons to prefer [[...]]; there are also reasons to avoid it, which may or may not be applicable in your environment. As to your coworker, "our style guide says so" is a valid justification, as far as it goes, but I'd also seek out backstory from someone who understands why the style guide recommends what it does.
A typical situation where you cannot use [[ is in an autotools configure.ac script. There brackets have a special and different meaning, so you will have to use test instead of [ or [[ -- Note that test and [ are the same program.
I am surprised that I haven't seen this issue raised sooner, but consider:
$ n=5
$ [[ $n -gt 0 ]] && echo $n is positive
5 is positive
$ n=foo
$ [[ $n -gt 0 ]] && echo $n is positive
$ [ "$n" -gt 0 ] && echo $n is positive
bash: [: foo: integer expression expected
The lack of error message from the line [[ $n -gt 0 ]] renders [[ completely unusable. Perhaps I'm being too harsh and the correct response is to simply avoid integer comparisons in [[, but I strongly advise against [[ for this reason.
In a nutshell, [[ is better because it doesn't fork another process. No brackets or a single bracket is slower than a double bracket because it forks another process.
[[ ]] double brackets are unsupported under certain versions of SunOS and totally unsupported inside function declarations by:
GNU Bash, version 2.02.0(1)-release (sparc-sun-solaris2.6)

Resources