Introduction to Tcl =================== Tcl (tool command language) is a general purpose system programming language, developed by John Ousterhout at UCB (University of California, Berkeley) starting in 1989. As a scripting language aiming at providing the ability for applications to communicate with each other.It can be easily expanded by adding modules, such as Tk, a cross platform widget toolkit used for building GUIs. To install, run through the standard installation procedure: sudo apt update -y sudo apt upgrade -y sudo apt install -y tcl If you intend to develop your own Tcl extensions, then also install tcl-dev. To invoke tcl, issue tclsh or (version might differ): tcl8.6 or (version might differ), on Windows: tclsh86 To run a Tcl script directly from the command line, issue tclsh yourScriptsName.tcl Without the script name, you end up in an interactive environment: pi@pi2b01:~ $ tclsh % Enter exit to exit: pi@pi2b01:~ $ tclsh % exit pi@pi2b01:~ $ Running Tcl interactively pretty much gives you "just" an ever so slightly souped up shell: pi@pi2b01:~ $ tclsh % df -k . Filesystem 1K-blocks Used Available Use% Mounted on /dev/root 29784124 24712440 3800316 87% / % hostname pi2b01 % who am i pi pts/1 2022-04-30 10:59 (192.168.123.28) % Run this simple "Hello, World" program: pi@pi2b01:~ $ cat hello.tcl puts "Hello, Word" pi@pi2b01:~ $ pi@pi2b01:~ $ tclsh hello.tcl Hello, Word pi@pi2b01:~ $ To avoid explicitly invoking the Tcl interpreter, insert a hash-bang, then add execution permission: pi@pi2b01:~ $ cat hello.tcl #!/usr/bin/tclsh puts "Hello, Word" pi@pi2b01:~ $ chmod 0755 hello.tcl pi@pi2b01:~ $ ./hello.tcl Hello, Word pi@pi2b01:~ $ Commands are delimited by ; (semicolon) or line breaks. Comments start with # (hash). The comment delimiter acts like a command, means ending a line in a comment, so it needs to be separated from the beginning of the line by a semicolon: pi@pi2b01:~ $ tclsh % puts "this is gonna fail" # my comment here wrong # args: should be "puts ?-nonewline? ?channelId? string" % puts "this is gonna work" ; # my comment here this is gonna work % If a Tcl statement gets lengthy, it may be spread out across multple lines, by splitting a line using a \ (backslash) character as the very last (!!!) character of the each line of the continuation, apart fromm the last one: up? % puts "Hello, \ what's \ up?" Hello, what's up? % Variable names follow the usual conventions, consist of letters, digits and underscore, and are case sensitive. To obtain the value of a variable, prefix it with a $ (dollar) sign: % pi@pi2b01:~ $ tclsh % set a 1 1 % puts $a 1 % Tcl uses weak typing, so an operation against one or more variables inspects variable contents at execution time, and errors out if the contents of any variable are not applicable to the intended operation. That's often referred to as "Duck Typing", as in "if it looks like a duck and quacks like a duck, then it is a duck.". So there is no need to declare a variable to be of a certain type, so any integer can be treated as a string without explicit conversion: % set a 1 1 % set b " <-- this is a one" <-- this is a one % set c "$a $b" 1 <-- this is a one % puts $c 1 <-- this is a one % To evaluate an statement and hold on to the result, place the statement into a block delimited by brackets, set result [ someCommand oneArgument anotherArgument ] One could embed evaluations into other evaluations: set result [ someCommand oneArgument [ otherCommand anotherArgument ] ] From a debugging and readability perspective, that's not a good idea. "expr" takes as arguments any mathematical expression. Operators are duck-typed: % puts $a 1 % set b 2 2 % puts $a 1 % puts $b 2 % puts [ expr $a + $b ] 3 % The "+" operator doesn't work on strings, so adding an integer value and a string value fails: % set b "blah" blah % puts [ expr $a + $b ] invalid bareword "blah" in expression "1 + blah"; should be "$blah" or "{blah}" or "blah(...)" or ... % Dereferencing an unset variable isn't allowed: % puts $unknownVar can't read "unknownVar": no such variable % A condition is any expression that evaluates to 0 (zero, false) or non-zero for true. Expressions syntax match what we are used to from C and Java. Operators are the same. Control structures execute blocks of statements depending on a condition. Braces combine multiple statements into a block. The opening brace must(!) follow the controlling statement. Control structures include if-elseif-else: if { condition } { do this } elseif { some other condition } { do that } else { complain loudly } The elseif and else clauses are optional. There is no "then" keyword. If the opening brace does not follow the control statement, Tcl complains; % if { 1 == 1 } wrong # args: no script following " 1 == 1 " argument % if { 1 == 1 } { puts "yay!" } yay! % Comparing one value against a list of them is done using switch: switch $someString { this { doThis } $nextBlock - that { doThat } default { hail-Mary-here } } The value of someString is compared to each value provided. If there is no match, the default-block (if present) is executed. To execute one block for multiple values, line them up before the block to be executed, and suffix them with "-". Of course there's the while loop: while { condition } { statement block } and the for-loop: for { start } { condition } { change } { statement block } start and change are statements, such as setting a variable to start out with, then change it for every iteration. The loop is executed as long as the condition holds true. To exit out of the loop from somewhere within the statement block, invoke "break". To skip the remainder of the statement block, and start the next iteration, execute "continue". Then there are functions, called procs: proc funcName { parm1 ... { defParm1 val1 } { defParm2 val2 } } { statement block return $someReturnValue } Parameters are optional. Not having any is fine. Mandatory parameters need to be provided first, optional ones trail behind. An optional parameter is declared by pairing a variable name and a default value in a list delimited by braces. Variables declared in a proc are local, unless declared global before use: % set globalVar 1234 1234 % proc myProc { } { set localVar 2345 global globalVar puts "localVar: $localVar, globalVar: $globalVar" } % myProc localVar: 2345, globalVar: 1234 % If a proc does not exit via a "return $someValue" or "return [someExpression]" statement, its result is empty. Tcl script execution terminates as soon as there is an error. To catch errors and act upon them, invoke catch, providing the command(s) as a braced list: catch { this could go sideways } result Catch returns 0 (zero) for no errors, non-zero in case something went wrong. The script's result or error message will be stored in result, if present. Documentation is avaiable here: http://tcl.tk/man/tcl8.5/ Replace 8.5 with your Tcl version. To find out what you are using, issue % info tclversion 8.6 % There's a bunch of tutorials, such as: https://www.tcl.tk/man/tcl8.5/tutorial/Tcl0a.html https://www.tutorialspoint.com/tcl-tk/index.htm https://wiki.tcl-lang.org/page/An+Introduction+to+Tcl+Scripting https://en.wikibooks.org/wiki/Tcl_Programming/Introduction https://zetcode.com/lang/tcl/ https://www.guru99.com/tcl-tutorial.html https://www.ee.columbia.edu/~shane/projects/sensornet/part1.pdf https://www.ee.columbia.edu/~shane/projects/sensornet/part2.pdf