Friday, January 30, 2009

Evaluate expression inside another evaluation - bash script


As we know, $( ) is used to to evaluate sub-expressions in bash shell (same as of back-tick in bash shell)

i.e.

$ var=`/sbin/ifconfig | grep eth0`

is same as

$ var=$(/sbin/ifconfig | grep eth0)

Lets discuss an example to see the good use of $( ) in bash shell.

#Suppose 'offsetno' variable is set to 9
$ offsetno=9

#Number of *.sh files under my directory (/tmp/mydir) is 29
$ ls /tmp/mydir/*.sh | wc -l
29

#Now to add the values of 'offsetno' with 'total number of .sh files' in my directory
$ expr $offsetno + `ls /tmp/mydir/*.sh | wc -l`
38

#Now if you need to store this output value in a variable called 'eff_num_file'
$ eff_num_file=`expr $offsetno + `ls /tmp/mydir/*.sh | wc -l``

Output:
expr: syntax error
64

#Oh what happened!! Lets echo 'eff_num_file' variable content; alas!! nothing got evaluated and stored.

$ echo $eff_num_file

#Now try this way:
$ eff_num_file=$(expr $offsetno + $(ls /tmp/mydir/*.sh | wc -l))

#See whats stored in eff_num_file variable, correct!!
$ echo $eff_num_file
38

What we noticed here is that back-tick within back-tick does not work, whereas $( ) inside $( ) work.

** The above example is just an example to explain the trick.

10 comments:

Werner said...

Is the back tick depreciated? I write scripts date insert the date into the filename like so:

mv file.tar.gz "`date`".file.tar.gz

Is the $() a better way? Interesting article.

Unknown said...

@Werner, thanks for commenting. I dont feel back-tick is depreciated. $( ) is just another way of evaluation of expression in bash. For your example, the following two does the same

$ mv file.log "`date`".file.log
$ mv file.log "$(date)".file.log

** we are using "" as date output contains spaces.

The only advantage I saw is when we need to evaluate an expression inside another expression (As I explained above)

i.e.

var=$(expr $(date '+%m') + 1)
works
but
var=`expr `date '+%m'` + 1`
does not work.

http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html explains this in details.

Unknown said...

One of my reader Peace,
notHerbert has pointed this

Nested backticks must be escaped to work.

var=`expr \`date '+%m'\` + 1`

Thanks Peace, its useful.

Unknown said...

I have a script with
function myfunc {
echo in myfunc
}

# i want to evaluate another script and pass in "myfunc"
# e.g.
x=`. otherscript.sh myfunc`

if I don't use the backticks myfunc is found and called. Is there a way to assign x above?

Unknown said...

#I have a script with:

function myfunc {
echo in myfunc
}

x=`. otherscript.sh myfunc`
#above assignment of x fails

#but this below works
. otherscript.sh myfunc

is there a way to get the x assignment to work? At runtime I get "myfunc: command not found"

Unknown said...

@Paul,

do you mean this ?

$ cat p.sh
#!/bin/sh

_myfunc () {
echo $1 + 100 | bc
}

_myfunc $(sh q.sh 9)

$ cat q.sh
#!/bin/sh

val=$1
echo $((s=val+5))

$ sh p.sh
114

Please let me know if you have any other queries. Thanks for your comment.

Edu said...

Hi,

Im trying to do something like this:

#!/bin/bash

test="{33,351}";
ls -laht /tmp/_structure/interface*_${test}_*.xml


The problem is that the script recognizes the {33,351} like a string and not as a command. if i replace the var test for the static value it results well. But i need that it comes from a variable.

I've tried as an evaluation expression but it not works. Can you help me?
Thanks

Unknown said...

@Edu,

Thanks for the comment.

Assuming you are trying to have test var to be from 33,34,35,....,351

you can use:
test=$(seq 33 351)

for i in $test
do
...
done

or

test=`seq 33 351`

And if you are just looking for test var to be one of two values viz. 33 and 351

you can loop using for:
for test in 33 351
do
ls -laht /tmp/_structure/interface*_${test}_*.xml
done

Please let me know if I misunderstood your query. Thanks.

Anonymous said...

Hi, I have a related question. Is it possible using $(), `` or similar mechanism to have an alias whose value is evaluated when called, not when assigned? Here's a simple example.
alias d = "echo `date`"
d will now print out the date at the time d was assigned
what I want to do is have d print out the current date when called. This is a simplistic example I know, but it is representative of what I need.

Unknown said...

@Subhan,

Putting on .bash_profile

d() {
date
}

should print the current time.

© Jadu Saikia www.UNIXCL.com