+++ title = "Tcl" author = ["Kaushal Modi"] description = "Notes as I am learning Tcl syntax using its [official tutorial](https://www.tcl.tk/man/tcl8.5/tutorial/Tcl0.html)." date = 2019-08-21T00:00:00-04:00 lastmod = 2021-05-19T16:44:20-04:00 tags = ["tcl"] categories = ["unix"] draft = false creator = "Emacs 28.0.50 (Org mode 9.4.5 + ox-hugo)" +++
Table of Contents
- [`tclsh` version](#tclsh-version) - [Simple Text Output](#simple-text-output) - [Assigning values to variables](#assigning-values-to-variables) - [Evaluation & Substitutions 1: Grouping arguments with `""`](#evaluation-and-substitutions-1-grouping-arguments-with) - [Evaluation & Substitutions 2: Grouping arguments with `{}`](#evaluation-and-substitutions-2-grouping-arguments-with) - [Evaluation & Substitutions 3: Grouping arguments with `[]`](#evaluation-and-substitutions-3-grouping-arguments-with) - [Results of a command - Math 101](#results-of-a-command-math-101) - [TODO Operands](#operands) - [Math Functions](#math-functions) - [Type Conversions](#type-conversions) - [Computers and Numbers](#computers-and-numbers) - [Numeric Comparisons 101 - `if`](#numeric-comparisons-101-if) - [TODO Textual Comparison - `switch`](#textual-comparison-switch) - [Looping 101 - `while` loop](#looping-101-while-loop) - [Looping 102 - `for` and `incr`](#looping-102-for-and-incr) - [Adding new commands to Tcl - `proc`](#adding-new-commands-to-tcl-proc) - [Variations in `proc` arguments and return values](#variations-in-proc-arguments-and-return-values) - [TODO Variable scope - `global` and `upvar`](#variable-scope-global-and-upvar) - [Tcl Data Structures 101 - The list](#tcl-data-structures-101-the-list) - [TODO Adding & Deleting members of a list](#adding-and-deleting-members-of-a-list) - [TODO More list commands - lsearch, lsort, lrange](#more-list-commands-lsearch-lsort-lrange) - [TODO Simple pattern matching - "globbing"](#simple-pattern-matching-globbing) - [TODO String Subcommands - length index range](#string-subcommands-length-index-range) - [TODO String comparisons - compare match first last wordend](#string-comparisons-compare-match-first-last-wordend) - [TODO Modifying Strings - tolower, toupper, trim, format](#modifying-strings-tolower-toupper-trim-format) - [Regular Expressions 101](#regular-expressions-101) - [TODO More Examples Of Regular Expressions](#more-examples-of-regular-expressions) - [String replacement using regular expressions](#string-replacement-using-regular-expressions) - [Removing square brackets in strings](#removing-square-brackets-in-strings) - [TODO More Quoting Hell - Regular Expressions 102](#more-quoting-hell-regular-expressions-102) - [TODO Associative Arrays](#associative-arrays) - [TODO More On Arrays - Iterating and use in procedures](#more-on-arrays-iterating-and-use-in-procedures) - [TODO Dictionaries](#dictionaries) - [TODO File Access 101](#file-access-101) - [TODO Information about Files - file, glob](#information-about-files-file-glob) - [TODO Invoking Subprocesses from Tcl - exec, open](#invoking-subprocesses-from-tcl-exec-open) - [TODO Learning the existence of commands and variables ? - info](#learning-the-existence-of-commands-and-variables-info) - [TODO State of the interpreter - info](#state-of-the-interpreter-info) - [TODO Information about procs - info](#information-about-procs-info) - [TODO Modularization - source](#modularization-source) - [TODO Building reusable libraries - packages and namespaces](#building-reusable-libraries-packages-and-namespaces) - [TODO Creating Commands - eval](#creating-commands-eval) - [TODO More command construction - format, list](#more-command-construction-format-list) - [TODO Substitution without evaluation - format, subst](#substitution-without-evaluation-format-subst) - [TODO Changing Working Directory - cd, pwd](#changing-working-directory-cd-pwd) - [TODO Debugging & Errors - errorInfo errorCode catch error return](#debugging-and-errors-errorinfo-errorcode-catch-error-return) - [TODO More Debugging - trace](#more-debugging-trace) - [Command line arguments and environment strings](#command-line-arguments-and-environment-strings) - [TODO Leftovers - time, unset](#leftovers-time-unset) - [TODO Channel I/O: socket, fileevent, vwait](#channel-i-o-socket-fileevent-vwait) - [TODO Time and Date - clock](#time-and-date-clock) - [TODO More channel I/O - fblocked & fconfigure](#more-channel-i-o-fblocked-and-fconfigure) - [TODO Child interpreters](#child-interpreters) - [Miscellaneous](#miscellaneous) - [Line continuation](#line-continuation) - [Setting array value](#setting-array-value) - [Cycling through all elements in an array](#cycling-through-all-elements-in-an-array) - [Cycling through all elements in a list](#cycling-through-all-elements-in-a-list) - [List available namespaces](#list-available-namespaces) - [List all procs in a namespace](#list-all-procs-in-a-namespace) - [Reference](#reference)
## `tclsh` version {#tclsh-version} ```tcl puts $tcl_version ``` ```text 8.6 ``` ## Simple Text Output {#simple-text-output} - If a string has more than one word (i.e. has space), it must be enclosed in `" "` or `{ }`. ```tcl puts "Hello, World - In Double Quotes" puts {Hello, World - In Braces} ``` Note that single quotes have no significance in Tcl. ```text Hello, World - In Double Quotes Hello, World - In Braces ``` - If the string has no space, the quotes or braces are not needed. ```tcl puts Hello ``` ```text Hello ``` - This is how you normally type a comment ```tcl # This is a comment at beginning of a line ``` - A Tcl command is terminated by a newline or a semicolon. ```tcl puts "This is line 1" puts "this is line 2" puts "This is line 3"; puts "this is line 4" puts "Hello, World; - With a semicolon inside the quotes" ``` ```text This is line 1 this is line 2 This is line 3 this is line 4 Hello, World; - With a semicolon inside the quotes ``` - The same "semicolon ends a command" applies when ending a command and starting a comment on the same line. ```tcl puts "Hello, World - In quotes" ;# This is a comment after the command. ``` ```text Hello, World - In quotes ``` - Below will not work as there is no semicolon separating the command and the comment. ```tcl puts {Bad comment syntax example} # *Error* - there is no semicolon! ``` ## Assigning values to variables {#assigning-values-to-variables} `set` assigns a value to a variable and then also returns the same. ```tcl set fruit Cauliflower ``` ```text Cauliflower ``` ```tcl set X "This is a string" set Y 1.24 puts $X puts $Y puts "..............................." set label "The value in Y is: " puts "$label $Y" ``` ```text This is a string 1.24 ............................... The value in Y is: 1.24 ``` The dollar sign tells Tcl to use the value of the variable - in this case `X` or `Y`. ## Evaluation & Substitutions 1: Grouping arguments with `""` {#evaluation-and-substitutions-1-grouping-arguments-with} Grouping words within double quotes allows substitutions to occur within the quotations - or, in fancier terms, "interpolation". The substituted group is then evaluated as a single argument. In general, the backslash (`\`) disables substitution for the single character immediately following the backslash. Any character immediately following the backslash will stand without substitution. However, there are specific "Backslash Sequence" strings which are replaced by specific values during the substitution phase. ```tcl puts "abc\n\tdef \u0A95" ``` ```text abc def ક ``` ```tcl set Z Albany set Z_LABEL "The Capital of New York is: " puts "$Z_LABEL $Z" ;# Prints the value of Z puts "$Z_LABEL \$Z" ;# Prints a literal $Z instead of the value of Z puts "\nBen Franklin is on the \$100.00 bill" set a 100.00 puts "Washington is not on the $a bill" ;# This is not what you want puts "Lincoln is not on the $$a bill" ;# This is OK puts "Hamilton is not on the \$a bill" ;# This is not what you want puts "Ben Franklin is on the \$$a bill" ;# But, this is OK puts "\n................. examples of escape strings" puts "Tab\tTab\tTab" puts "This string prints out \non two lines" puts "This string comes out\ on a single line" ``` ```text The Capital of New York is: Albany The Capital of New York is: $Z Ben Franklin is on the $100.00 bill Washington is not on the 100.00 bill Lincoln is not on the $100.00 bill Hamilton is not on the $a bill Ben Franklin is on the $100.00 bill ................. examples of escape strings Tab Tab Tab This string prints out on two lines This string comes out on a single line ``` ## Evaluation & Substitutions 2: Grouping arguments with `{}` {#evaluation-and-substitutions-2-grouping-arguments-with} In contrast to words grouped in double quotes, no substitution happens in words grouped in curly braces. ```tcl set Z Albany set Z_LABEL "The Capital of New York is: " puts "\n................. examples of differences between \" and \{" puts "grouped in double quotes: $Z_LABEL $Z" puts {grouped in braces: $Z_LABEL $Z} puts "\n....... examples of differences in nesting \{ and \" " puts "braces in double quotes: $Z_LABEL {$Z}" puts {double quotes in braces: Who said, "What this country needs is a good $0.05 cigar!"?} puts "\n................. examples of escape strings" puts {There are no substitutions done within braces \n \r \x0a \f \v} puts {But, the escaped newline at the end of a\ string is still evaluated as a space} ``` ```text ................. examples of differences between " and { grouped in double quotes: The Capital of New York is: Albany grouped in braces: $Z_LABEL $Z ....... examples of differences in nesting { and " braces in double quotes: The Capital of New York is: {Albany} double quotes in braces: Who said, "What this country needs is a good $0.05 cigar!"? ................. examples of escape strings There are no substitutions done within braces \n \r \x0a \f \v But, the escaped newline at the end of a string is still evaluated as a space ``` ## Evaluation & Substitutions 3: Grouping arguments with `[]` {#evaluation-and-substitutions-3-grouping-arguments-with} You obtain the results of a command by placing the command in square brackets (`[]`). This is the functional equivalent of the back single quote (`` ` ``) in shell scripting. As the Tcl interpreter reads in a line it replaces all the $variables with their values. If a portion of the string is grouped with square brackets, then the string within the square brackets is evaluated as a command by the interpreter, and the result of the command replaces the square bracketed string. Except .. - A square bracket that is escaped with a `\` is considered as a literal square bracket. - A square bracket within braces is not modified during the substitution phase. ```tcl set x abc puts "A simple substitution: $x\n" set y [set x "def"] puts "Remember that set returns the new value of the variable: X: $x Y: $y\n" set z {[set x "This is a string within quotes within square brackets withing braces"]} puts "Note that the curly braces prevented evaluation of the string in square brackets: $z\n" set a "[set x {This is a string within braces within square brackets within double quotes}]" puts "See how the set is executed: $a" puts "\$x is: $x\n" set b "\[set y {This is a string within braces beginning with an escaped square bracket within quotes}]" # Note the \ escapes the bracket, and must be doubled to be a # literal character in double quotes puts "Note the \\ escapes the bracket:\n \$b is: $b" puts "\$y is still \"$y\" from the first assignment" ``` ```text A simple substitution: abc Remember that set returns the new value of the variable: X: def Y: def Note that the curly braces prevented evaluation of the string in square brackets: [set x "This is a string within quotes within square brackets withing braces"] See how the set is executed: This is a string within braces within square brackets within double quotes $x is: This is a string within braces within square brackets within double quotes Note the \ escapes the bracket: $b is: [set y {This is a string within braces beginning with an escaped square bracket within quotes}] $y is still "def" from the first assignment ``` ## Results of a command - Math 101 {#results-of-a-command-math-101} The Tcl command for doing math type operations is `expr`.
It's recommended to enclose `expr` arguments in curly braces -- It is faster, and also more secure. So do `expr {$i * 10}` instead of simply `expr $i * 10`.
The `expr` command performs its own round of substitutions on variables and commands, so you should use braces to prevent the Tcl interpreter doing this as well (leading to double substitution). In the below example, the `puts` in `$userinput` is evaluated when evaluating `expr`, which very well might be unintended. ```tcl set userinput {[puts DANGER!]} set foo [expr $userinput == 1] puts $foo ``` ```text DANGER! 0 ``` Below as the `expr` arguments are wrapped in `{ }`, the "DANGER!" is avoided. ```tcl set userinput {[puts DANGER!]} set foo [expr {$userinput == 1}] puts $foo ``` ```text 0 ``` ### TODO Operands {#operands} ### Math Functions {#math-functions} Tcl supports the following mathematical functions in expressions: ```text abs acos asin atan atan2 bool ceil cos cosh double entier exp floor fmod hypot int isqrt log log10 max min pow rand round sin sinh sqrt srand tan tanh wide ``` ```tcl set x 1 set w "Abcdef" expr { [string length $w] - 2*$x } ``` ```text 4 ``` ### Type Conversions {#type-conversions} | Function | Description | |------------|-----------------------------------------------------------------------------------| | `double()` | Convert to a float | | `int()` | Convert to an ordinary integer using truncation | | `wide()` | Convert to a "wide" integer number | | `entier()` | Coerses a number to an integer of appropriate size to hold it without truncation. | ```tcl set X 100 set Y 256 set Z [expr {$Y + $X}] set Z_LABEL "$Y plus $X is " puts "$Z_LABEL $Z" puts "The square root of $Y is [expr { sqrt($Y) }]\n" puts "Because of the precedence rules \"5 + -3 * 4\" is: [expr {-3 * 4 + 5}]" puts "Because of the parentheses \"(5 + -3) * 4\" is: [expr {(5 + -3) * 4}]" set A 3 set B 4 puts "The hypotenuse of a triangle: [expr {hypot($A,$B)}]" # # The trigonometric functions work with radians ... # set pi6 [expr {3.1415926/6.0}] puts "The sine and cosine of pi/6: [expr {sin($pi6)}] [expr {cos($pi6)}]" ``` ```text 256 plus 100 is 356 The square root of 256 is 16.0 Because of the precedence rules "5 + -3 * 4" is: -7 Because of the parentheses "(5 + -3) * 4" is: 8 The hypotenuse of a triangle: 5.0 The sine and cosine of pi/6: 0.49999999226497965 0.8660254082502546 ``` ```tcl # Working with arrays set a(1) 10 set a(2) 7 set a(3) 17 set b 2 puts "Sum: [expr {$a(1)+$a($b)}]" ``` ```text Sum: 17 ``` ## Computers and Numbers {#computers-and-numbers} ```tcl # Division puts "1/2 is [expr {1/2}]" puts "-1/2 is [expr {-1/2}]" puts "1/2 is [expr {1./2}]" puts "1/3 is [expr {1./3}]" puts "1/3 is [expr {double(1)/3}]" ``` ```text 1/2 is 0 -1/2 is -1 1/2 is 0.5 1/3 is 0.3333333333333333 1/3 is 0.3333333333333333 ``` ```tcl set tcl_precision 17 ;# One of Tcl's few magic variables: ;# Show all decimals needed to exactly ;# reproduce a particular number puts "1/2 is [expr {1./2}]" puts "1/3 is [expr {1./3}]" set a [expr {1.0/3.0}] puts "3*(1/3) is [expr {3.0*$a}]" set b [expr {10.0/3.0}] puts "3*(10/3) is [expr {3.0*$b}]" set c [expr {10.0/3.0}] set d [expr {2.0/3.0}] puts "(10.0/3.0) / (2.0/3.0) is [expr {$c/$d}]" set e [expr {1.0/10.0}] puts "1.2 / 0.1 is [expr {1.2/$e}]" ``` ```text 1/2 is 0.5 1/3 is 0.33333333333333331 3*(1/3) is 1.0 3*(10/3) is 10.0 (10.0/3.0) / (2.0/3.0) is 5.0000000000000009 1.2 / 0.1 is 11.999999999999998 ``` ## Numeric Comparisons 101 - `if` {#numeric-comparisons-101-if}
Put the `if` test condition in curly braces.
```tcl set x 1 if {$x == 2} {puts "$x is 2"} else {puts "$x is not 2"} if {$x != 1} { puts "$x is != 1" } else { puts "$x is 1" } if $x==1 {puts "GOT 1"} ``` ```text 1 is not 2 1 is 1 GOT 1 ``` ```tcl set x 1 # # Be careful, this is just an example # Usually you should avoid such constructs, # it is less than clear what is going on and it can be dangerous # set y x if "$$y != 1" { puts "$$y is != 1" } else { puts "$$y is 1" } # # A dangerous example: due to the extra round of substitution, # the script stops # set y {[exit]} if "$$y != 1" { puts "$$y is != 1" } else { puts "$$y is 1" } ``` ```text $x is 1 ``` - For numbers, any non-zero value is a TRUE expression. - For strings, `"yes"` and `"true"` is a TRUE expression. ```tcl set str1 yes if { $str1} { puts "yep" } else { puts "nope" } set str2 true if { $str2 } { puts "yep" } else { puts "nope" } ``` ```text yep yep ``` ## TODO Textual Comparison - `switch` {#textual-comparison-switch} ## Looping 101 - `while` loop {#looping-101-while-loop}
In Tcl **everything** is a command, and everything goes through the same substitution phase. For this reason, the test must be placed within braces.
```tcl set x 1 while {$x < 5} { ;# Notice the while test enclosed in curly braces puts "x is $x" set x [expr {$x + 1}] } puts "exited first loop with X equal to $x" ``` ```text x is 1 x is 2 x is 3 x is 4 exited first loop with X equal to 5 ``` The next example shows the difference between `".."` and `{..}`. ```tcl set x 0 while "$x < 5" { ;# This test is first evaluated and transformed to "0 < 5" ;# .. which will always be true! set x [expr {$x + 1}] if {$x > 7} break ;# This while loop would have run infinitely without this break if "$x > 3" continue ;# the while loop short-circuts back to the top for x>3 puts "x is $x" } puts "exited second loop with X equal to $x" ``` ```text x is 1 x is 2 x is 3 exited second loop with X equal to 8 ``` ## Looping 102 - `for` and `incr` {#looping-102-for-and-incr}
Put the `for` test condition in curly braces.
When braces are used for grouping, the newline is not treated as the end of a Tcl command. This makes it simpler to write multiple line commands. However, the opening brace **must** be on the line with the `for` command. ```tcl for {set i 0} {$i < 4} {incr i} { puts "I'm inside the first loop: $i" } for {set i 3} {$i < 2} {incr i} { puts "I'm inside the second loop: $i" } ``` ```text I'm inside the first loop: 0 I'm inside the first loop: 1 I'm inside the first loop: 2 I'm inside the first loop: 3 ``` Below, a `while` loop is written similar to the first `for` loop above: ```tcl puts "Start" set i 0 while {$i < 4} { puts "I'm inside the while loop: $i" incr i puts "I'm after incr: $i" } ``` ```text Start I'm inside the while loop: 0 I'm after incr: 1 I'm inside the while loop: 1 I'm after incr: 2 I'm inside the while loop: 2 I'm after incr: 3 I'm inside the while loop: 3 I'm after incr: 4 ``` ```tcl set i 0 incr i ;# now i is 1 # This is equivalent to: set i [expr {$i + 1}] ;# now i is 2 incr i 3 ;# now i is 5 ``` ```text 5 ``` ## Adding new commands to Tcl - `proc` {#adding-new-commands-to-tcl-proc} In Tcl there is actually no distinction between commands (often known as 'functions' in other languages) and "syntax". There are no reserved words (like `if` and `while`) as exist in C, Java, Python, Perl, etc. So below works (but please don't code like that!)! ```tcl set xxx set $xxx foo if $xxx bar puts $xxx zoo else $foo { true } { $bar "yep" } $zoo { $bar "nope" } ``` ```text yep ``` ```tcl proc sum {arg1 arg2} { set x [expr {$arg1 + $arg2}] return $x } puts " The sum of 2 + 3 is: [sum 2 3]" ``` ```text The sum of 2 + 3 is: 5 ``` In the below example, the original `for` command has been destroyed to do something else. ```tcl proc for {a b c} { puts "The for command has been replaced by puts commands!" puts "The arguments were:\n\t$a\n\t$b\n\t$c" } for {set i 1} {$i < 10} {incr i} ``` ```text The for command has been replaced by puts commands! The arguments were: set i 1 $i < 10 incr i ``` ## Variations in `proc` arguments and return values {#variations-in-proc-arguments-and-return-values} A proc can be defined with a set number of required arguments (as was done with `sum` in the previous section, or it can have a variable number of arguments. An argument can also be defined to have a default value. Variables can be defined with a default value by placing the variable name and the default within braces within args. For example: ```tcl proc justdoit {a {b 1} {c -1}} { puts "a = $a, b = $b, c = $c" } justdoit 10 justdoit 10 20 justdoit 10 20 30 ``` ```text a = 10, b = 1, c = -1 a = 10, b = 20, c = -1 a = 10, b = 20, c = 30 ``` A proc will accept a variable number of arguments if the last declared argument is the word `args`. ```tcl proc example {first {second ""} args} { if {$second eq ""} { puts "There is only one argument and it is: $first" return 1 } else { if {$args eq ""} { puts "There are two arguments - $first and $second" return 2 } else { puts "There are many arguments - $first, $second and $args" return "many" } } } set count1 [example ONE] set count2 [example ONE TWO] set count3 [example ONE TWO THREE ] set count4 [example ONE TWO THREE FOUR] puts "The example was called with $count1, $count2, $count3, and $count4 Arguments" ``` ```text There is only one argument and it is: ONE There are two arguments - ONE and TWO There are many arguments - ONE, TWO and THREE There are many arguments - ONE, TWO and THREE FOUR The example was called with 1, 2, many, and many Arguments ``` Using [`foreach`](https://www.tcl.tk/man/tcl/TclCmd/foreach.htm) to loop through all the args of a proc: ```tcl proc my_proc {args} { puts "Number of args = [llength $args]" set idx 0 foreach arg $args { puts " arg $list_idx = $arg" incr idx } } my_proc x my_proc a b c d e f ```
Code Snippet 1: Looping though a proc's args using foreach
```text Number of args = 1 arg 0 = x Number of args = 6 arg 0 = a arg 1 = b arg 2 = c arg 3 = d arg 4 = e arg 5 = f ``` ## TODO Variable scope - `global` and `upvar` {#variable-scope-global-and-upvar} ## Tcl Data Structures 101 - The list {#tcl-data-structures-101-the-list} The list is the basic Tcl data structure. A list is simply an ordered collection of stuff; numbers, words, strings, or other lists. ```tcl set x "a b c" ;# Yes, Tcl interprets this string as a list too. puts "Item at index 2 of the list {$x} is: [lindex $x 2]\n" set i 0 foreach j $x { puts "$j is item number $i in list x" incr i } ``` ```text Item at index 2 of the list {a b c} is: c a is item number 0 in list x b is item number 1 in list x c is item number 2 in list x ``` `lindex lst idx` : Returns the item at `idx` index from the `lst` list. Note that the list indices begin from 0. `llength lst` : Returns the length of `lst` list. ```tcl set y [split 7/4/1776 "/"] puts "We celebrate on the [lindex $y 1]'th day of the [lindex $y 0]'th month" set z [list puts "arg 2 is $y" ] puts "A command resembles: $z" puts "Length of \$z list = [llength $z]" eval $z ``` ```text We celebrate on the 4'th day of the 7'th month A command resembles: puts {arg 2 is 7 4 1776} Length of $z list = 2 arg 2 is 7 4 1776 ``` ```tcl proc my_proc {args} { puts "Number of args = [llength $args]" for {set idx 0} {$list_idx < [llength $args]} {incr idx} { puts " arg $list_idx = [lindex $args $list_idx]" } } my_proc x my_proc a b c d e f ```
Code Snippet 2: Looping though a proc's args using for
```text Number of args = 1 arg 0 = x Number of args = 6 arg 0 = a arg 1 = b arg 2 = c arg 3 = d arg 4 = e arg 5 = f ``` `foreach` can be used to take more than one variable at a time from a list: ```tcl set x "a b c d e f" foreach {var1 var2} $x { puts "var1 = $var1, var2 = $var2" } foreach {var1 var2 var3} $x { puts "var1 = $var1, var2 = $var2, var3 = $var3" } ``` ```text var1 = a, var2 = b var1 = c, var2 = d var1 = e, var2 = f var1 = a, var2 = b, var3 = c var1 = d, var2 = e, var3 = f ``` `foreach` can even take a variable at a time from multiple lists: ```tcl set x "a b c" set y "A B C" set z "1 2 3" foreach foo $x bar $y zoo $z { puts "$foo $bar $zoo" } ``` ```text a A 1 b B 2 c C 3 ``` ## TODO Adding & Deleting members of a list {#adding-and-deleting-members-of-a-list} ```tcl set b [list a b {c d e} {f {g h}}] puts "Treated as a list: $b\n" set b [split "a b {c d e} {f {g h}}"] puts "Transformed by split: $b\n" set a [concat a b {c d e} {f {g h}}] puts "Concated: $a\n" lappend a {ij K lm} ;# Note: {ij K lm} is a single element puts "After lappending: $a\n" set b [linsert $a 3 "1 2 3"] ;# "1 2 3" is a single element puts "After linsert at position 3: $b\n" set b [lreplace $b 3 5 "AA" "BB"] puts "After lreplacing 3 positions with 2 values at position 3: $b\n" ``` ```text Treated as a list: a b {c d e} {f {g h}} Transformed by split: a b \{c d e\} \{f \{g h\}\} Concated: a b c d e f {g h} After lappending: a b c d e f {g h} {ij K lm} After linsert at position 3: a b c {1 2 3} d e f {g h} {ij K lm} After lreplacing 3 positions with 2 values at position 3: a b c AA BB f {g h} {ij K lm} ``` ## TODO More list commands - lsearch, lsort, lrange {#more-list-commands-lsearch-lsort-lrange} ## TODO Simple pattern matching - "globbing" {#simple-pattern-matching-globbing} ## TODO String Subcommands - length index range {#string-subcommands-length-index-range} ## TODO String comparisons - compare match first last wordend {#string-comparisons-compare-match-first-last-wordend} ## TODO Modifying Strings - tolower, toupper, trim, format {#modifying-strings-tolower-toupper-trim-format} ## Regular Expressions 101 {#regular-expressions-101} ```tcl set sample "Where there is a will, There is a way." # # Only check if the match happened # set result [regexp {[a-z]+} $sample] puts "Result: $result" # # Match the first substring with lowercase letters only, and save match to $match # set result [regexp {[a-z]+} $sample match] puts "Result: $result match: $match" # # Match the first two words, the first one allows uppercase set result [regexp {([A-Za-z]+) +([a-z]+)} $sample match sub1 sub2 ] puts "Result: $result Match: $match 1: $sub1 2: $sub2" # # Replace a word # regsub "way" $sample "lawsuit" sample2 puts "New: $sample2" # # Use the -all option to count the number of "words" # puts "Number of words: [regexp -all {[^ ]+} $sample]" ``` ```text Result: 1 Result: 1 match: here Result: 1 Match: Where there 1: Where 2: there New: Where there is a will, There is a lawsuit. Number of words: 9 ``` ```tcl proc get_type {str} { ## Returns the string parsed between "_" and "_result_1234.log". set result [regexp {_([a-z]+)_result_[0-9]+\.log} $str match sub1] puts [format "%s -> match=%s, sub1=%s" $str $match $sub1] return $sub1 } puts [get_type "foo_test_bar_aaa_bar_result_123.log"] puts [get_type "foo_test_bar_aaa_zoo_result_123.log"] ``` ```text foo_test_bar_aaa_bar_result_123.log -> match=_bar_result_123.log, sub1=bar bar foo_test_bar_aaa_zoo_result_123.log -> match=_zoo_result_123.log, sub1=zoo zoo ``` ## TODO More Examples Of Regular Expressions {#more-examples-of-regular-expressions} ### String replacement using regular expressions {#string-replacement-using-regular-expressions} ```tcl set str "foo1foo2foo3" puts "str = $str" set str [regsub -all foo $str bar] puts "str = $str" regsub -all bar $str zoo str puts "str = $str" set numMatches [regsub -all zoo $str tar str] puts "str = $str, numMatches = $numMatches" ``` ```text str = foo1foo2foo3 str = bar1bar2bar3 str = zoo1zoo2zoo3 str = tar1tar2tar3, numMatches = 3 ``` ### Removing square brackets in strings {#removing-square-brackets-in-strings} ```tcl set str "abc" puts "str = $str" set str {abc[1]} puts "str = $str" if {[string match *\\\[* $str]} { puts yes } else { puts no } set str1 $str puts "str1 = $str1" ``` ```text str = abc str = abc[1] yes str1 = abc[1] ``` ## TODO More Quoting Hell - Regular Expressions 102 {#more-quoting-hell-regular-expressions-102} ## TODO Associative Arrays {#associative-arrays} ## TODO More On Arrays - Iterating and use in procedures {#more-on-arrays-iterating-and-use-in-procedures} ## TODO Dictionaries {#dictionaries} ## TODO File Access 101 {#file-access-101} ## TODO Information about Files - file, glob {#information-about-files-file-glob} ## TODO Invoking Subprocesses from Tcl - exec, open {#invoking-subprocesses-from-tcl-exec-open} ## TODO Learning the existence of commands and variables ? - info {#learning-the-existence-of-commands-and-variables-info} ## TODO State of the interpreter - info {#state-of-the-interpreter-info} ## TODO Information about procs - info {#information-about-procs-info} ## TODO Modularization - source {#modularization-source} ## TODO Building reusable libraries - packages and namespaces {#building-reusable-libraries-packages-and-namespaces} Below snippet shows that just declaring a `variable` in a namespace does not cause its `info exists` status to become _true_ -- It needs to be explicitly `set` to a value. ```tcl namespace eval ::foo { variable bar } puts [info exists ::foo::bar] if {![info exists ::foo::bar]} { puts empty } set ::foo::bar zoo puts [info exists ::foo::bar] ``` ```text 0 empty 1 ``` ## TODO Creating Commands - eval {#creating-commands-eval} ## TODO More command construction - format, list {#more-command-construction-format-list} ## TODO Substitution without evaluation - format, subst {#substitution-without-evaluation-format-subst} ## TODO Changing Working Directory - cd, pwd {#changing-working-directory-cd-pwd} ## TODO Debugging & Errors - errorInfo errorCode catch error return {#debugging-and-errors-errorinfo-errorcode-catch-error-return} ## TODO More Debugging - trace {#more-debugging-trace} ## Command line arguments and environment strings {#command-line-arguments-and-environment-strings} - The number of command line arguments to a Tcl script is passed as the global variable `argc`. - The name of a Tcl script is passed to the script as the global variable `argv0`. - The rest of the command line arguments are passed as a list in `argv`. ```tcl puts "There are $argc arguments to this script" puts "The name of this script is $argv0" if {$argc > 0} {puts "The other arguments are: $argv" } ``` ```text There are 0 arguments to this script The name of this script is tclsh ``` Below outputs the full path of the executable that runs the Tcl scripts; usually the path to `tclsh`: ```tcl puts [info nameofexecutable] ``` Environment variables are available in Tcl via a _global_ associative array `env`. The index into `env` is the name of the environment variable. Below snippet dumps the names and values of all the set environment variables: ```tcl puts "You have these environment variables set:" foreach index [array names env] { puts "$index: $env($index)" } ``` ```tcl puts $env(EDITOR) if {[info exists env(FOO)]} { ;# https://stackoverflow.com/a/7712763/1219634 puts "env var FOO is set" } else { puts "env var FOO is not set" set env(FOO) abc if {[info exists env(FOO)]} { puts "Now env var FOO is set" } } puts $env(FOO) ``` ```text emacsclient env var FOO is not set Now env var FOO is set abc ``` ## TODO Leftovers - time, unset {#leftovers-time-unset} ## TODO Channel I/O: socket, fileevent, vwait {#channel-i-o-socket-fileevent-vwait} ## TODO Time and Date - clock {#time-and-date-clock} ## TODO More channel I/O - fblocked & fconfigure {#more-channel-i-o-fblocked-and-fconfigure} ## TODO Child interpreters {#child-interpreters} ## Miscellaneous {#miscellaneous} ### Line continuation {#line-continuation} The tcl commands can continue onto the next line when the current line ends with a backslash. ```tcl puts "abc\ def" set \ str\ "abc" puts "str = $str" ``` ```text abc def str = abc ``` ### Setting array value {#setting-array-value} ```tcl array set colorcount { red 1 green 5 blue 4 white 9 } foreach {color count} [array get colorcount] { puts "Color: $color Count: $count" } ``` ```text Color: blue Count: 4 Color: white Count: 9 Color: green Count: 5 Color: red Count: 1 ``` ### Cycling through all elements in an array {#cycling-through-all-elements-in-an-array} ```tcl set idx 0 array set arr { a b c d e f } puts $list_idx puts [array size arr] foreach {el} [array get arr] { puts "$el" } foreach {el1 el2} [array get arr] { puts "$el1 $el2" } ``` ```text 0 3 e f a b c d e f a b c d ``` ### Cycling through all elements in a list {#cycling-through-all-elements-in-a-list} ```tcl set lst [list a b c d e f] puts $lst puts "size of lst = [llength $lst]" puts "first element of lst = [lindex $lst 0]" puts "second element of lst = [lindex $lst 1]" puts "second-to-last element of lst = [lindex $lst end-1]" puts "last element of lst = [lindex $lst end]" set list_idx 0 proc cycle_thru_lst {some_lst {inc 1}} { global list_idx set max_idx [llength $some_lst] # puts "max index = $max_idx" puts "some_list\[$list_idx\] = [lindex $some_lst $list_idx]" if {$inc} { if {$list_idx == $max_idx-1} { set list_idx 0 } else { incr list_idx } puts "increased list_idx = $list_idx" } else { if {$list_idx == 0} { set list_idx [expr {$max_idx - 1}] } else { incr list_idx -1 } puts "decreased list_idx = $list_idx" } } cycle_thru_lst $lst cycle_thru_lst $lst cycle_thru_lst $lst cycle_thru_lst $lst cycle_thru_lst $lst cycle_thru_lst $lst cycle_thru_lst $lst cycle_thru_lst $lst cycle_thru_lst $lst cycle_thru_lst $lst 0 cycle_thru_lst $lst 0 cycle_thru_lst $lst 0 cycle_thru_lst $lst 0 cycle_thru_lst $lst 0 cycle_thru_lst $lst 0 cycle_thru_lst $lst 0 cycle_thru_lst $lst 0 cycle_thru_lst $lst 0 ``` ```text a b c d e f size of lst = 6 first element of lst = a second element of lst = b second-to-last element of lst = e last element of lst = f some_list[0] = a increased list_idx = 1 some_list[1] = b increased list_idx = 2 some_list[2] = c increased list_idx = 3 some_list[3] = d increased list_idx = 4 some_list[4] = e increased list_idx = 5 some_list[5] = f increased list_idx = 0 some_list[0] = a increased list_idx = 1 some_list[1] = b increased list_idx = 2 some_list[2] = c increased list_idx = 3 some_list[3] = d decreased list_idx = 2 some_list[2] = c decreased list_idx = 1 some_list[1] = b decreased list_idx = 0 some_list[0] = a decreased list_idx = 5 some_list[5] = f decreased list_idx = 4 some_list[4] = e decreased list_idx = 3 some_list[3] = d decreased list_idx = 2 some_list[2] = c decreased list_idx = 1 some_list[1] = b decreased list_idx = 0 ``` ### List available namespaces {#list-available-namespaces} [Ref](https://stackoverflow.com/a/10834179/1219634) ```tcl puts [namespace children ::] ``` ```text ::tcl ``` ### List all procs in a namespace {#list-all-procs-in-a-namespace} [Ref](https://stackoverflow.com/a/2886508/1219634) ```tcl puts "Procs in global (::) namespace:" puts [info commands ::*] puts "" puts "Procs in ::tcl namespace:" puts [info commands ::tcl::*] ``` ```text Procs in global (::) namespace: ::tell ::socket ::subst ::open ::eof ::pwd ::glob ::list ::pid ::exec ::auto_load_index ::time ::unknown ::eval ::lassign ::lrange ::fblocked ::lsearch ::auto_import ::gets ::case ::lappend ::proc ::break ::variable ::llength ::auto_execok ::return ::linsert ::error ::catch ::clock ::info ::split ::array ::if ::fconfigure ::concat ::join ::lreplace ::source ::fcopy ::global ::switch ::auto_qualify ::update ::close ::cd ::for ::auto_load ::file ::append ::lreverse ::format ::unload ::read ::package ::set ::binary ::namespace ::scan ::apply ::trace ::seek ::while ::chan ::flush ::after ::vwait ::dict ::continue ::uplevel ::foreach ::lset ::rename ::fileevent ::regexp ::lrepeat ::upvar ::encoding ::expr ::unset ::load ::regsub ::history ::interp ::exit ::puts ::incr ::lindex ::lsort ::tclLog ::string Procs in ::tcl namespace: ::tcl::pkgconfig ::tcl::CopyDirectory ::tcl::HistIndex ::tcl::HistEvent ::tcl::Bgerror ::tcl::HistInfo ::tcl::HistKeep ::tcl::HistAdd ::tcl::HistRedo ::tcl::dtrace ::tcl::HistChange ::tcl::HistClear ``` ## Reference {#reference} - [Tcl 8.5 Tutorial](https://www.tcl.tk/man/tcl8.5/tutorial/Tcl0.html) [//]: # "Exported with love from a post written in Org mode" [//]: # "- https://github.com/kaushalmodi/ox-hugo"