Customised search for networking technology geeks.

Simple TCL script

Video explanation in Armenian.

The problem

Say you have a web developers team, and staging system, your testers are building test versions of your websites and that eats your HDD's free space, but in fact you don't need old versions of website, so to free some disk space you can remove old versions. You can do that manually, but if you are reading this probably you've decided to automate that process. Now I am going to show the TCL script that helps me do that. In my situation there are many websites sitting in the /data/www directory, but few of them starting with "dev" string are not website directories so I need to skip them. What I need to do with websites it to keep last n versions and some folders and remove the older versions. Here is the output of ls -al command for example.com website.
root@stage-apache:/data/www/example.com# ls -al
total 32
drwxr-xr-x  8 staginguser www-data 4096 2012-08-30 16:03 .
drwxr-xr-x 26 root        root     4096 2012-08-27 15:11 ..
lrwxrwxrwx  1 staginguser www-data   11 2012-08-30 15:38 htdocs -> htdocs-0522
drwxr-xr-x 16 staginguser www-data 4096 2012-08-24 12:31 htdocs-0483
drwxr-xr-x 16 staginguser www-data 4096 2012-08-30 15:38 htdocs-0522
drwxr-xr-x 16 staginguser www-data 4096 2012-08-24 12:31 htdocs-0683
drwxr-xr-x  5 staginguser www-data 4096 2012-08-24 11:59 htdocs-base
drwxr-xr-x  2 root        root     4096 2012-06-06 14:03 logs
drwxr-xr-x 12 staginguser www-data 4096 2012-08-24 12:30 example.com-0483
drwxr-xr-x 12 staginguser www-data 4096 2012-08-24 12:30 example.com-0522
drwxr-xr-x 12 staginguser www-data 4096 2012-08-30 15:37 example.com-0683
I need to keep logs, htdocs-base and last two htdocs-nnnn and example.com-nnnn folders, in fact I need to remove two folders the htdocs-0483 and the respective example.com-0483 folders. This is the script that will do that.

The TCL script

1. #!/usr/bin/tclsh
2. cd /data/www
3. set skins [exec ls | grep -v dev]
4. puts $skins
5. foreach {skin} $skins {
6.         puts "The current skin is $skin"
7.        cd $skin;
8.        puts "Now I am in $skin's directory"
9.        set ver [exec ls -r | grep "htdocs-0"]
10.        set vers [exec ls -r | grep $skin]
11.        set last [llength $ver]
12.        puts "there are $last skin versions for $skin"
13.        for {set i 2} {$i<$last} {incr i} { exec rm -r [set folder [lindex $ver $i]];  exec rm -r [set folders [lindex $vers $i]]; puts "I've removed the $folder and $folders"}
14.        puts "I am done with $skin skin, going up to the next skin\n\n"
15.        cd ../
16. }
The 1st line determines to what interpreter the commands in the script should be sent, as you can see that's tclsh(TCL). The 2nd line takes us to the folder where all sites aka skins are located. The 3rd line executes ls | grep -v dev command, -v dev is to negate grep matching rule, I am doing it to skip folders starting with "dev". The 4th line prints out all the skins (hmm, why I left it here). The 5th line starts the foreach cycle for all skins, it takes all skins in skins list one by one and assigns them to a variable named skin, which takes the current skin name as value on every iteration. The 6th line defines itself. The 7th line takes us to current skin's directory, we put $ sign before variable's name to pull out its value not the name. The 8th line defines itself. The 9th line is a very important line, it takes all the folder names starting with htdocs-0 and pushes them into the ver list. The 10th line does almost the same it takes all the folder names starting with the current skin name and pushes them int vers list. 11th line takes the length of the list named ver, which is by the way is equal to (or at least should be) ver list's length and assigns it to the variable named last. 12th line prints the quantity of the version, and the name of the current skin, actually it defines itself, too. 13th line is the most important and the easiest line to understand, it is simply a for cycle, which has structure "for {index}{condition}{increment}{body}". The set i 2 means that we are starting to count from 2, that's done to skip first two folder because they are newest two folders, the last two versions of the skin, and they should be kept. The $i<$last means that index aka i should increase all the way to the length of list named ver (remember the line 11).The incr i adds 1 to i. The The exec rm -r [set folder [lindex $ver $i]]; assigns the current ith element of list named ver to the variable name folder, the the current ith element of the list named ver is the name of skin version's folder.The exec rm -r takes that name and removes that folder.The exec rm -r [set folders [lindex $vers $i]]; means exactly the same for list named vers. The last step of the for cycle the command puts "I've removed the $folder and $folders" defines itself. The 14th line defines itself. the 15th line goes up to start all over again inside the foreach cycle. The 16th line is simply closes the foreach cycle.

Two words about nested cycles

Nested cycle means cycle inside another cycle, cycle a can contain cycle b, cycle b itself can contain cycle c or cycle a can contain cycle b an c. If cycle a rolls 10 times and cycle b rolls 10 times, then cycle b's body is being executed 10*10=100 times because on every iteration of cycle a we have 10 iterations of cycle b, and if cycle b itself has nested cycle c which has 5 iterations, on every iteration of cycle b we have 5 iterations of cycle c, so on every iteration of cycle a we have 10*5 iterations of cycle c.