As of 2016-02-26, there will be no more posts for this blog. s/blog/pba/
Showing posts with label arithmetic. Show all posts

A couple of years back I wrote about the builtin vs keyword conditional expressions, but I didnt think about another conditional use with arithmetic, that is with tests like [ $a -gt $b] or [[ $a -ne 0 ]].

Use similar code to benchmark 1 > 0 as shown below, not the real code, but you get the idea:

time for ((i = 0; i < 10000; i++)); do <test> 1 >/-gt 0 ; done

The result is:

<test> with time % slower
[ 6.647s 47.9%
[[ 4.692s 04.4%
(( 4.493s fastest
test (builtin) 6.538s 45.5%

(( is just marginally faster than [[ by my definition. Of course, they both are faster than builtin [ and test, as for /usr/bin/test, its an external command, there is no point to test, because its slow for sure.

With this result, there really isnt much difference between two, however, 1 > 0 is more readable than 1 -gt 0, literally and mathematically.

Note

Yes, [[ 1 > 0 ]] is a valid syntax, but it doesnt do what you think, its not arithmetically but lexicographically. In short, its for strings, see bash(1), and there is no such this as [[ 1 >= 0 ]].

if in Bash is similar to if conditional statements in other programming languages. The statement checks the values, such as equality or empty value. The one difference is the syntax of how if statement looks.

1   Syntax

The syntax in Bash is, from bash(1):

if list; then list; [ elif list; then list; ] ... [ else list; ] fi

Where list is, from bash(1)

a sequence of one or more pipelines separated by one of the operators ;, &, &&, or , and optionally terminated by one of ;, &, or <newline>.

and A pipeline is, from bash(1)

a sequence of one or more commands separated by one of the control operators | or |&. The format for a pipeline is:

[time [-p]] [ ! ] command [ [||&] command2 \... ]

If you understand above, you may ask where does that put [ ] and [[ ]] or even (( ))? Almost all of the code you will read would look like:

if [[ -z "$foobar" ]]; then
  do_something
fi

Those three are actually commands, first one is the shell builtin (= test), last two are compound commands (and keywords). What does that mean? It means that [[ ]] and other two are not part of if syntax.

I believe many of you are like me used to think [[ ]] is part of if syntax, therefore you may code like this:

command
ret=$?
if (( $? == 0 )); then :; fi

You can see the better way to code it in next section.

2   Checking exit status

if command; then :; fi

if relies on the last command in the list whose exit status (return value) will be used to decide which branch it should go next. When command returns 0, it runs the code; or runs code in else when command returns other than 0.

Sometimes, you may want to want to run code when the command fails, i.e. returns non-zero exit status:

if ! command; then :; fi

Again, ! is not part of if syntax, but is part of the pipeline, from bash(1):

If the reserved word ! precedes a pipeline, the exit status of that pipeline is the logical negation of the exit status as described above.

If you are crazy enough, you can

if
  command1
  command2
  command3
  ...
  command99
then
  :
fi

Only the exit status of command99 will be checked. I know nobody code like this, but you can do like that. To be more practical, it maybe more practical for while:

while prepare_something; command_to_check; do :; done

3   Checking values

There are plenty of pages about checking values, I am not going to write about it. Read bash(1), it should be enough, actually. One thing I want to mention is how to check multiple conditions:

if [[ "$foo" == "foo" ]] && [[ "$bar" == "bar" ]]; then command; fi

There is a shortcut for this, this can be rewritten as

[[ "$foo" == "foo" ]] && [[ "$bar" == "bar" ]] && command

3.1   test and [ ] vs. [[ ]]

One may ask the difference, to be perfectly honest, I dont know completely. The former is shell builtin command, the latter is shell reserved keyword. Shell built-in command is faster than external command and I believe reserved keyword may be faster, because from what I know about shell built-in command is loadable extension. (I have written one of my own for prompt) So reversed keyword may be faster because it may not need to access data through another layer. But I dont know what exact the implementation detail is, so dont quote me on this.

What I am certain is that [ ] will give you more compatibility. But the thumb of rule is to code with the one you are comfortable with.

3.2   (( )) vs. [[ ]]

[[ 1 -gt 0 ]] and (( 1 > 0 )) test exactly the same thing, however, there is slight performance difference, the latter, Arithmetic Evaluation, is a bit of faster, but hardly noticeable unless you do at million times scale.