Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bash Variable Maths Not Working

I have a simple bash script, which forms part of an in house web app that I've developed.

It's purpose is to automate deletion of thumbnails of images when the original image has been deleted by the user.

The script logs some basic status info to a file /var/log/images.log

#!/bin/bash
cd $thumbpath
filecount=0
# Purge extraneous thumbs
find . -type f | while read file
do
  if [ ! -f "$imagepath/$file" ]
  then
    filecount=$[$filecount+1]
    rm -f "$file"
  fi
done
echo `date`: $filecount extraneous thumbs removed>>/var/log/images.log

Whilst the script correctly deletes thumbs, it doesn't correctly output the number of thumbs that are being purged, it always shows 0.

For example, having just manually created some orphaned thumbnails, and then running my script, the manually generated orphaned thumbs are deleted, but the log shows:

Thu Jun 9 23:30:12 BST 2011: 0 extraneous thumbs removed

What am I doing wrong that is stopping $filecounter from showing a number other than zero, when files are being deleted.

I've created the following bash script to test this, and this works perfectly, outputting 0 then 1:

#!/bin/bash
count=0
echo $count
count=$[$count+1]
echo $count

Edit:

Thanks for the answers, but why does the following work

$ x=3
$ x=$[$x+1]
$ echo $x
4

...and also the second example works, yet it doesn't work in the first script?

Second Edit:

This works

count=0
echo Initial Value $count
for i in `seq 1 5`
do
  count=$[$count+1]
  echo $count
done
echo Final Value $count


Initial Value 0
1
2
3
4
5
Final Value 5

as does replacing count=$[$count+1] with count=$((count+1)), but not in my initial script.


1 Answers

You're using the wrong operator. Try using $(( ... )) instead, e.g.:

$ x=4
$ y=$((x + 1))
$ echo $y
5
$

EDIT
The other problem you're bumping into is down to the pipe. Bumped into this one before (with ksh, but wouldn't suprise me to find that other shells have the same problem). The pipe is forking another bash process, so when you do the increment, filcount is getting incremented in the subshell that's been forked after the pipe. This value isn't passed back to the calling shell as the subshell has it's own independent environment (environment variables are inherited in called processes, but called process cannot modify the environment of the calling process).

As an example, this demonstrates that filecount gets incremented okay:

#!/bin/bash

filecount=0
ls /bin | while read x
do
        filecount=$((filecount + 1))
        echo $filecount
done
echo $filecount

...so you should see filecount increase in the loop, but the final filecount will be zero because this echo belongs to the main shell, but the forked subshell (which consists purely of the while loop).

One way you can get the value back is like this...

#!/bin/bash

filecount=0
filecount=`ls /bin | while read x
do
        filecount=$((filecount + 1))
        echo $filecount
done | tail -1`
echo $filecount

This will only work if you don't care about any other stdout output in the loop as this throws it all away apart from the last line we output (the final value of filecount). This works because we're using stdout and stdin to feed the data back to the parent shell.

Depending on your viewpoint this is either a nasty hack or a nifty bit of shell jiggery-pokery. I'll leave you to decide what you think it is :-)

like image 118
Chris J Avatar answered Aug 16 '26 00:08

Chris J



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!