Tcl Tutorial
Introduction Simple Text Output Assigning values to variables Evaluation & Substitutions 1: Grouping arguments with "" Evaluation & Substitutions 2: Grouping arguments with {} Evaluation & Substitutions 3: Grouping arguments with [] Results of a command - Math 101 Numeric Comparisons 101 - if Textual Comparison - switch Looping 101 - While loop Looping 102 - For and incr Adding new commands to Tcl - proc Variations in proc arguments and return values Variable scope - global and upvar Tcl Data Structures 101 - The list Adding & Deleting members of a list More list commands - lsearch, lsort, lrange String Subcommands - length index range String comparisons - compare match first last wordend Modifying Strings - tolower, toupper, trim, format Regular Expressions 101 More Examples Of Regular Expressions More Quoting Hell - Regular Expressions 102 Associative Arrays More On Arrays - Iterating and use in procedures File Access 101 Information about Files - file, glob Invoking Subprocesses from Tcl - exec, open Learning the existence of commands and variables ? - info State of the interpreter - info Information about procs - info Modularization - source Building reusable libraries - packages and namespaces Creating Commands - eval More command construction - format, list Substitution without evaluation - format, subst Changing Working Directory - cd, pwd Debugging & Errors - errorInfo errorCode catch error return More Debugging - trace Command line arguments and environment strings Leftovers - time, unset Channel I/O: socket, fileevent, vwait Time and Date - clock More channel I/O - fblocked & fconfigure Child interpreters
Introduction Index
|
Next lesson
Welcome to the Tcl tutorial. We wrote it with the goal of helping you to learn Tcl. It is aimed at those who have some knowledge of programming, although you certainly don't have to be an expert. The tutorial is intended as a companion to the Tcl manual pages which provide a reference for all Tcl commands. It is divided into brief sections covering different aspects of the language. Depending on what system you are on, you can always look up the reference documntation for commands that you are curious about. On Unix for example, man while would bring up the man page for the command. Each section is accompanied by relevant examples showing you how to put to use the material covered.
Additional Resources The Tcl community is an exceedingly friendly one. It's polite to try and figure things out yourself, but if you're struggling, we're more than willing to help. Here are some good places to get help:
The comp.lang.tcl newsgroup. Accessible via a newsreader, or Goo gle Groups. The Wiki has a great deal of useful code, code , examples and discussions of the finer points of Tcl usage. If you need help right away, there is often someone on the # tcl channel on irc.freenode.net who can help you out, but please don't be impatient if no one can help you instantly - if you need that level of support, consider hiring a consultant. There are several recommended books for those who wish to gain more in-depth knowledge of Tcl. Clif Flynt, Flynt, the original author of this tutorial is also the author of Tcl/Tk: of Tcl/Tk: A Developer's Guide. Guide. Other popular books: Practical Programming in Tcl and Tk. Tk .
Credits Thanks first and foremost to Clif Flynt for making his material available under a BSD license. The following people also contributed:
Neil Madden Arjen Markus David N. Welton
Of course, we also welcome comments and suggestions about how it could be improved - or if it's great the way it is, we don't mind a bit of thanks, either! Index
|
Next lesson
Simple Text Outp ut Previous lesson
|
Index
|
Next lesson
The traditional starting place for a tutorial is the classic "Hello, World" program. Once you can print out a string, you're well on your way to using Tcl for fun and pro fit! The command to output a string in Tcl is the
command.
A single unit of text after the command will be printed to the standard output device (in this case, the lower window). The default behavior is to print a newline character ("return") appropriate for the system after printing the text. If the string has more than one word, you must enclose the string in double quotes or braces ({}). A set of words enclosed in quotes or braces is treated as a single unit, while words separated by whitespace are treated as multiple arguments to the command. Quotes and braces can both be used to group several words into a single unit. However, they actually behave differently. In the next lesson you'll start to learn some of the differences between their behaviors. Note that in Tcl, single quotes are not significant, as they are in other programming languages such as C, Perl and Python. Many commands in Tcl (including ) can accept multiple arguments. If a string is not enclosed in quotes or braces, the Tcl interpreter will consider each word in the string as a separate argument, and pass each individually to the command. The command will try to evaluate the words as optional arguments. This will probably result in an error. A command in Tcl is a list of words terminated by a newline or semicolon. Tcl comments are a # at the beginning of the line, or after the command is closed with a ; semicolon.
Example puts "H ello, World World - In q uotes" uotes" pu ts {H ello, ell o, World - In Braces}
;# This is a com m ent aft after the com m and. # *Err Error* or* - there is no sem icol on !
pu ts "Thi "This s is line li ne 1"; pu ts "this hi s is line li ne 2" puts "Hel "Hel lo, World; World; - With With a sem icolon in side the the q uotes" uotes"
Previous lesson
|
Index
|
Next lesson
Assigning values to variables |
Previous lesson
Index
|
Next lesson
In Tcl, everything may be represented as a string, although internally it may be represented as a list, integer, double, or other type, in order to make the language fast. The assignment command in Tcl is When
.
is called with two arguments, as in:
set fru it "Cau "Cau liflower" li flower"
it places the second argument ("Cauliflower" ) in the memory space referenced by the first argument (fruit ). always returns the contents of the variable named in the first argument. Thus, when is called with two arguments, it places the second argument in the memory space referenced by the first argument and then returns the second argument. In the above example, for instance, it would return "Cauliflower", without the quotes. The first argument to a command can be either a single word, like fruit or pi , or it can be a member of an array. Arrays will be discussed in greater detail later, for the time being just remember that many data can be collected under a single variable name, and an individual datum can be accessed by its index within that array. Indexing into an array in Tcl is handled by putting the index within parentheses after the name of the variable. can also be invoked with only one argument. When called with just one argument, it will return the contents of that argument. Here's a summary of the
command.
varN arN am e ?value? e ?value? If value is specified, then the contents of the variable varName are set equal to value. If varName consists only of alphanumeric characters, and no parenthese, it is a scalar variable. If varN arN am e has the form varN arN am e(index e(ind ex) ) , it is a member of an associative array. If you look at the example code, you'll notice that in the command the first argument is typed with only its name, but in the statement the argument is preceeded with a $ . The dollar sign tells Tcl to use the value of the variable - in this case X or Y . Tcl passes data to subroutines either by name or by value. Commands that don't change the contents of a variable usually have their arguments passed by value. Commands that d o change the value of the data must have the data passed by name.
Example set X "Th "Th is is i s a st s trin g" set Y 1.24 1. 24 pu ts $X
pu ts $Y puts puts "....... "....... ........ ........ ........" set label "The "The valu e in Y is: " pu ts "$lab "$label el $Y"
Previous lesson
|
Index
|
Next lesson
Evaluation & Substitutio ns 1: Grouping arguments arguments w ith "" Previous lesson
|
Index
|
Next lesson
This lesson is the first of three which discuss the way Tcl handles substitution during command evaluation. In Tcl, the evaluation of a command is done is 2 phases. The first phase is a single pass of substitutions. The second phase is the evaluation of the resulting command. Note that only one pass of substitutions is made. Thus in the command puts $varName
the contents of the proper variable are substituted for $varN varN am e , and then the command is executed. Assuming we have set varN , the sequence would look like arN am e to "H ello Wor Wo rld" ld " this: puts $varName ⇒ puts "Hello World" , which is then executed and prints out Hello World. During the substitution phase, several types of substitutions occur. A command within square brackets ([]) is replaced with the result of the execution of that command. (This will be explained more fully in the lesson "Results of a Command - Math 101.") Words within double quotes or braces are grouped into a single argument. However, double quotes and braces cause different behavior during the substitution phase. In this lesson, we will concentrate on the behavior of double quotes during the substitution phase. 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. Thus, in the command: puts "The current stock value is $varName"
the current contents of varName are substituted for $varName, and then the entire string is printed to the output device, just like the example above. 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. The following backslash strings will be substituted as shown below. St r i n g
\a \b \f \n \r \t \v
Ou t p u t
Audible Bell 0x07 Backspace 0x08 Form Feed (clear screen) 0x0c New Line 0x0a Carriage Return 0x0d Tab 0x09 Vertical Tab 0x0b
Hex Value
\0dd \xhh
Octal Value Hex Value
d is a number from 1-7 h is a hex digit 0-9,A-F,a-f
The final exception is the backslash at the end of a line of text. This causes the interpreter to ignore the newline, and treat the text as a single line of text. The interpreter will insert a blank space at the location of the ending backslash.
Example set Z "Alban "Alban y" set Z_LABEL LABEL "Th "Th e Cap C apitol itol of New N ew York is: " pu ts "$Z_ "$Z_LABE LABEL L $Z" ;# Prin ts th e value valu e of Z pu ts "$Z_ "$Z_LABEL $Z" ;# Prints Prints a literal literal $Z in stead stead of the the valu e of Z puts puts " nBen Franklin Franklin is on the the $100.00 $100.00 bill" set a 100.00 100.0 0 put pu ts "Wash "Washin in gton gton is not no t on the the $a bil l" ;# This is not what you wan wantt put pu ts "Lin "Lin coln is not no t on the $$a $$a bil l" ;# Thi Thi s is OK puts puts "H am ilton ilton is not on the $a bill" ;# This This is not what you want want puts puts "Ben Franklin is on the $$a $$a bill " ;# But, But, this is OK puts puts " n.. .... .... .... ... exam exam ples of of escape escape st strings" puts puts "Tab tTab tTab" puts "This string print prints s out non two lines" pu ts "Thi "This s str strin g com co m es ou t on a single line"
Previous lesson
|
Index
|
Next lesson
Evaluation & Substitutio ns 2: Grouping arguments arguments w ith {} Previous lesson
|
|
Index
Next lesson
During the substitution phase of command evaluation, the two grouping operators, the brace ({) and the double quote ("), are treated differently by the Tcl interpreter. In the last lesson you saw that grouping words with double quotes allows substitutions to occur within the double quotes. By contrast, grouping words within double braces disables substitution within the braces. Characters within braces are passed to a command exactly as written. The only "Backslash Sequence" that is processed within braces is the backslash at the end of a line. This is still a line continuation character. Note that braces have this effect only when they are used for grouping (i.e. at the beginning and end of a sequence of words). If a string is already grouped, either with quotes or braces, and braces occur in the middle of the grouped string (i.e. "foo{bar"), then the braces are treated as regular characters with no special meaning. If the string is grouped with quotes, substitutions will occur within the quoted string, even between the braces.
Example set Z "Alban "Alban y" set Z_LABEL LABEL "Th "Th e Cap C apitol itol of New N ew York is: " puts puts " n...... .......... . ex examples of differenc erences es between een p u ts "$Z_LABEL $Z" p u ts {$Z_ {$Z _LABEL $Z}
" and
{"
puts puts " n...... . examples examples of diff differences ences in in nesting { and and " " pu ts "$Z_LABE LABEL L {$Z}" {$Z} " pu ts {Wh o said , "What this hi s cou ntry need s is a good $0.05 cigar cig ar!"? !"?} puts puts " n.. .... .... .... ... exam exam ples of of escape escape st strings" puts puts {T {There here are are no subst substitutions utions done within ithin braces aces pu ts {Bu t, the escaped escape d n ewlin e at the end of a str strin g is i s still still evaluated evaluated as a space} space }
Previous lesson
|
n r
x0a
f
Index
v}
|
Next lesson
Evaluation & Substitutio ns 3: Grouping arguments arguments w ith [] Previous lesson
|
Index
|
Next lesson
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 sh programming, or using the return value of a function in C. 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. Let's take the following code segment, for example: puts [readsensor [selectsensor]]] [selectsensor]]]
The parser scans the entire command, and sees that there is a command substitution to perform: readsensor [selectsensor] , which is sent to the interpreter for evaluation. The parser once again finds a command to be evaluated and substituted, selectsensor The fictitious selectsensor command is evaluated, and it presumably returns a sensor to read. At this point, readsensor has a sensor to read, and the readsensor command is evaluated. Finally, the value of readsensor is passed on back to the puts command, which prints the output to the screen.
The exceptions to this rule are as follows:
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.
Example set x "abc" "abc " puts puts "A "A sim ple substit substitut ution: ion: $x n" set y [set [s et x "de "def" f"]] put pu ts "Rem em ber that set returns eturns the the n ew valu value e of the variabl variabl e: X: $x Y: $y $y n " set z {[set x "Th "Th is is a st s trin g within qu otes otes with with in braces"]} puts "No te the curly braces: braces: $z $z n" set a "[set "[set x {Thi {This s is a str strin g withi withi n braces within hi n qu otes}]" otes}]" pu ts "See how ho w the set s et is execu ted: ed : $a" puts " $x is: is: $x $x n" set b " [set y {This {This i s a str string within braces braces within quotes}]" quotes}]" puts "Not "Note the esca escape pes s the br bracke ackett: n $b is: is: $b" $b" puts puts " $y is: is: $y $y"
Previous lesson
|
Index
|
Next lesson
Results of a command - Math 101 Previous lesson
|
Index
|
Next lesson
The Tcl command for doing math type operations is . The following discussion of the command is extracted and adapted from the man page. takes all of its arguments ("2 + 2" for example) and evaluates the result as a Tcl "expression" (rather than a normal command), and returns the value. The operators permitted in Tcl expressions include all the standard math functions, logical operators, bitwise operators, as well as math functions like , , and so on. Expressions Expr essions almost always yield numeric results (integer or floating-point values). Performance tip: enclosing the arguments to
in curly braces will result in faster code. So
do expr {$i * 10} instead of simply expr $i * 10
OPERANDS A Tcl expression consists of a combination of operands, operators, and parentheses. White space may be used between operands, operators and parentheses; it is ignored by the expression processor. Where possible, operands are interpreted as integer values. Integer values may be specified in decimal (the normal case), in octal (if the first character of the operand is 0), or in hexadecimal (if the first two characters of the operand are 0x). Note that the octal and hexadecimal conversion takes place differently in the expr command than in the Tcl substitution phase. In the substitution phase, a \ x 3 2 would be converted to an ascii "2", while would covert 0x32 to a decimal 50. If an operand does not have one of the integer formats given above, then it is treated as a floating-point number, if that is possible. Floating-point numbers may be specified in any of the ways accepted by an ANSI-compliant C compiler. For example, all of the following are valid floating-point numbers: 2.1, 3., 6e4, 7.91e+16. If no numeric interpretation is possible, then an operand is left as a string (and only a limited set of operators may be applied to it). Operands may be specified in any of the following ways:
As an numeric value, either integer or floating- point. As a Tcl variable, using standard $ notation. The variable's value will be used as the operand.
OPERATORS The valid operators are listed below, grouped in decreasing order of pre cedence: -+~! Unary minus, unary plus, bit-wise NOT, logical NOT. None of these operators may be applied to string operands, and bit-wise NOT may be applied only to integers. */% Multiply, divide, remainder. None of these operators may be applied to string operands, and remainder may be applied only to integers. The remainder will always have the same sign as the divisor and an absolute value smaller than the divisor. +Add and subtract. Valid for any numeric operands. << >> Left and right shift. Valid for integer operands only.
& Bit-wise AND. Valid for integer operands only. ^ Bit-wise exclusive OR. Valid for integer operands oper ands only. | Bit-wise OR. Valid for integer operands only. && Logical AND. Produces a 1 result if both operands are non-zero, 0 otherwise. Valid for numeric operands only (integers or floating-point). || Logical OR. Produces a 0 result if both operands are zero, 1 otherwise. Valid for numeric operands only (integers or floating-point). x?y:z If-then-else, as in C. If x evaluates to non-zero, then the result is the value of y. Otherwise the result is the value of z. The x operand must have a numeric value.
MATH FUNCTIONS FUNCTIONS Tcl supports the following mathematical functions in expressions: acos asin atan atan2 ceil
cos cosh exp floor fmod
hypot log log10 pow sin
sinh sqrt tan tanh
TYP E CONVERS CONVERSIONS IONS Tcl supports the following functions to convert from one representation of a number to another: abs double int round
Example set X 100; set Y 256; set se t Z [exp [ exprr "$Y "$Y + $X"] set Z_LABE LABEL L "$Y "$Y plu p lu s $X is " p u ts "$Z_LABEL $Z" pu ts "The "The squ are are root root of $Y is [expr sqrt sqrt($Y)] ($Y)] n" puts "Because "Because of the precedence rules rules "5 + -3 * 4 " is: [expr -3 * 4 + 5]" pu ts "Because of the paren parenttheses hes es "(5 "(5 + -3) * 4 " is: [expr (5 + -3) * 4]" puts puts p u ts p u ts puts puts
" n...... ......... .. m ore ex examples of differences ences bet between een " and {" {$Z_ { $Z_LABEL [expr [e xpr $Y + $X]} "$Z_LABEL {[exp {[ exprr $Y $Y + $X]}" $X]} " "T "The comm comm and to to add two num bers is: is: [ex [expr $a + $b]"
Previous lesson
|
Index
|
Next lesson
Num eric Comp Comp arisons 10 1 - if |
Previous lesson
Index
|
Next lesson
Like most languages, Tcl supports an if command. The syntax is:
expr1
The words
body1 and
body2
...
?bodyN?
are optional, although generally then is left out and else is used.
The test expression following False
expr2
should return one of:
Tr u e
a numeric value 0 y es es / n o true/ true/ false false
all others no yes false true
If the test expression returns a string "yes"/"no" or "true"/"false", the case of the return is not checked. True/FALSE or YeS/nO are legitimate returns. If the test expression evaluates to True, then body1 will be executed. If the test expression evaluates to False, then the word after body1 will be examined. If the next word is , then the next test expression will be tested as a condition. If the next word is then the final body will be evaluated as a command. The test expression following the word is evaluated in the same manner as in the expr command. Hex strings 0xXX will be converted to their numeric equivalent before evaluation. The test expression following may be enclosed within quotes, or braces. If it is enclosed within braces, it will be evaluated within the if command, and if enclosed within quotes it will be evaluated during the substitution phase, and then another round of substitutions will be done within the command.
Example set se t x 1 if {$x == 2} { pu ts "$x is 2"} else e lse {puts {p uts "$x "$x is n ot 2"} 2"} if {$x != 1} { pu ts "$x is != ! = 1" } else { pu ts "$x is 1" 1" } if $x==1 {pu { pu ts "G OT 1"} set se t y x if "$$y != ! = 1" { pu ts "$$y is != 1" } else { pu ts "$$y "$$y is 1" }
Previous lesson
|
Index
|
Next lesson
Textual Textual Comparison - sw itch Previous lesson
|
Index
|
Next lesson
The
command allows you to choose one of several options in your code. It is similar to in C, except that it is more flexible, because you can switch on strings, instead of just integers. The string will be compared to a set of patterns, and when a pattern matches the string, the code associated with that pattern w ill be evaluated. It's a good idea to use the command when you want to match a variable against several possible values, and don't want to do a long series of if... elseif ... elseif statements. The syntax of the command is:
str strin g p g patt attern1 ern 1 body1 body1 ? ?p att attern ern 2 b ody2? od y2? ... ?p att attern ern N b ody od yN ?
- or
str strin g { g { pat pa ttern ern 1 bod b ody1 y1 ?p ?p att attern ern 2 bod b ody2? y2?. . . ?p att attern ern N b ody od yN ? }
Str Strin g is the string that you wish to test, and p att attern1, ern1, p att attern2, ern2, etc etc are the patterns that the string will be compared to. If str strin g matches a pattern, then the code within the body associated with that pattern will be executed. The return value of the body will be returned as the return value of the switch statement. Only one pattern will be matched. If the last p attern argument is the string , that pattern will match any string. This guarantees that some set of code will be executed no matter what the contents of string are. If there is no default argument, and none of the patterns match string, then the switch command will return an empty string. If you use the brace version of this command, there will be no substitutions done on the patterns. The body of the command, however, will be parsed and evaluated just like any other command, so there will be a pass of substitutions done on that, just as will be done in the first syntax. The advantage of the second form is that you can write multiple line commands more readably with the brackets. Note that you can use braces to group the body argument when using the switch or if commands. This is because these commands pass their body argument to the Tcl interpreter for evaluation. This evaluation includes a pass of substitutions just as it does for code not within a command body argument.
Example set x "ONE" "ON E" set se t y 1 set z "ONE" "ON E" # This is p robably obab ly the easiest easi est and clean est form orm of the com co m m and # to to rem rem em ber: switch $x { "$z" { set se t y1 [expr [e xpr $y+1] puts puts "MATCH "MATCH $z. $z. $y + $z is $y1" $y1" }
ONE { set se t y1 [expr [e xpr $y+1] pu ts "MATCH "MATCH O NE . $y + one on e is $y1" $y1" } TWO { set se t y1 [expr [e xpr $y+2] pu ts "MATCH TWO. $y + two is i s $y1" } TH REE { set se t y1 [expr [e xpr $y+3] pu ts "MATCH "MATCH THREE. THRE E. $y + th ree is $y1" } default de fault { pu ts "$x is N OT A MATCH" } } swit swi tch $x "$z" "$z" { set se t y1 [expr [e xpr $y+1] puts "MA "MAT TCH $z. $z. $y + $z is $y1" $y1" } ONE { set se t y1 [expr [e xpr $y+1] pu ts "MATCH "MATCH O NE. NE . $y + on e is $y1" } TWO { set se t y1 [expr [e xpr $y+2] pu ts "MATCH TWO. $y + two is $y1" } THREE { set se t y1 [expr [e xpr $y+3] pu ts "MATCH "MATCH THREE. THR EE. $y + th ree i s $y1" } default d efault { pu ts "$x "$x does n ot m atch atch any of these these cho ices" } switch $x "ON E" "put "pu ts O N E=1" E=1" "TWO" "TWO" "puts "p uts TWO=2" TWO=2" "default" "pu "pu ts N O_MATCH" switch switch $x "ONE" "ON E" "put "pu ts ON E=1" E=1" "TWO" "pu ts TWO=2" "defaul "de faultt" "pu ts NO_ NO _MATCH ";
Previous lesson
|
Index
|
Next lesson
Looping 101 - While loop Previous lesson
|
Index
|
Next lesson
Tcl includes two commands for looping, the and commands. Like the statement, they evaluate their test the same way that the does. In this lesson we discuss the command, and in the next lesson, the command. In most circumstances where one of these commands can be used, the other can be used as well.
test body
The command evaluates test as an expression. If test is true, the code in body is executed. After the code in body has been executed, test is is evaluated again. A statement within body will stop the execution of the code and the test will be reevaluated. A within body will break out of the while loop, and execution will continue with the next line of code after body In Tcl everything is a command, and everything goes through the same substitution phase. For this reason, the test must be placed within braces. If test is placed within quotes, the substitution phase will replace any variables with their current value, and will pass that test to the command to evaluate, and since the test has only numbers, it will always evaluate the same, quite probably leading to an endless loop! Look at the two loops in the example. If it weren't for the break command in the second loop, it would loop forever.
Example set se t x 1 # This is a n orm orm al way to writ write e a Tcl while loop . while whi le {$x { $x < 5} { pu ts "x is $x" set se t x [exp [e xprr $x $x + 1] } pu ts "exit "exited first first loop with with X equal equa l to to $x n" # The next ne xt example exam ple sh ows the d ifferen erence ce between between ".." ".. " and an d {. . .} # H ow man y tim es does the the following following loop run? Why does it not # print on each pass? set se t x 0 whi le "$x < 5" { set se t x [exp [e xprr $x $x + 1] if {$x > 7} b reak if "$x "$x > 3" con tin ue pu ts "x is $x" } pu ts "exit "exited ed secon se con d loop l oop with with X equal equ al to $x" $x"
Previous lesson
|
Index
|
Next lesson
Looping 102 - For and incr Previous lesson
|
Index
|
Next lesson
Tcl supports an iterated loop construct similar to the loop in C. The command in Tcl takes four arguments; an initialization, a test, an increment, and the body of code to evaluate on each pass through the loop. The syntax for the command is:
start test next body
During evaluation of the command, the start code is evaluated once, before any other arguments are evaluated. After the start code has been evaluated, the test is evaluated. If the test evaluates to true, then the body is evaluated, and finally, the next argument is evaluated. After evaluating the next argument, the interpreter loops back to the test , and repeats the process. If the test evaluates as false, then the loop will exit immediately.
Start is the initialization portion of the command. It is usually used to initialize the iteration variable, but can contain any code that you wish to execute before the loop starts. The test argument is evaluated as an expression, expr ession, just as with the
and
commands.
Next is commonly an incrementing command, but may contain any command which the Tcl interpreter can evaluate.
Body is the body of code to execute. Since you commonly do not want the Tcl interpreter's substitution phase to change variables to their current values before passing control to the command, it is common to group the arguments with 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 command, or the Tcl interpreter will treat the close of the next brace as the end of the command, and you will get an error. This is different than other languages like C or Perl, where it doesn't matter where you place your braces. Within the body code, the commands and may be used just as they are used with the command. When a is encountered, the loop exits immediately. When a is encountered, evaluation of the body ceases, and the test is re-evaluated. Because incrementing the iteration variable is so common, Tcl has a special command for this:
varN arN am e ? e ?in crem crem ent en t?
This command adds the value in the second argument to the variable named in the first argument. If no value is given for the second argument, it defaults to 1.
Example for {set {se t i 0} {$i < 10} {in cr i} { pu ts "I in side sid e first first loop : $i" } for {set {se t i 3} {$i < 2} {in cr i} { puts "I inside secon d loop: $i" }
p u ts "St "S tart" art" set i 0 while whil e {$i < 10} 10} { pu ts "I in sid e fir first st loop : $i" in cr i pu ts "I after incr: in cr: $i" } set i 0 in cr i # This is i s equ ivalent ivalen t to: set se t i [expr [ expr $i + 1]
Previous lesson
|
Index
|
Next lesson
Adding new commands to Tcl - proc Previous lesson
|
Index
|
Next lesson
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... When the Tcl interpreter starts up there is a list of known commands that the interpreter uses to parse a line. These commands include while, for, set, puts, and so on. They are, however, still just regular Tcl commands that obey the same syntax rules as all Tcl commands, both built-in, and those that you create yourself with the command. The
command creates a new command. The syntax for the
command is:
name args body args body
When is evaluated, it creates a new command with name name that takes arguments . args . When the procedure name is called, it then runs the code contained in body . When name is invoked, local Args is a list of arguments which will be passed to name variables with these names will be created, and the values to be passed to name will be copied to the local variables. The value that the body of a proc returns can be defined with the command. The command will return its argument to the calling program. If there is no return, then body will return to the caller when the last of its commands has been executed. The return value of the last command becomes the return value of the procedure.
Example proc sum {arg1 {arg1 arg2} { set x [expr {$arg1 + $arg2} $arg2}]; ]; return $x } puts puts " The sum sum of 2 + 3 is: is: [sum [sum 2 3] 3] n n" proc for {a b c} { put pu ts "T "The for com m and an d has been be en replaced by b y a put pu ts"; puts "T "The ar arguments wer were: $a $a n$b n$b n$c n$c n" } for {set i 1} {$i < 10} {in {i n cr i}
Previous lesson
|
Index
|
Next lesson
Variations in proc arguments and return values Previous lesson
|
Index
|
Next lesson
A proc can be defined with a set number of required r equired arguments (as was done with in the previous lesson, 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 . For example: within braces within args proc justdo justdoit it {a {{b b 1} {c -1}} { }
Since there are default arguments for the b and c variables, you could call the procedure one of three ways: justdoit 10, which would set a to 10, and leave b set to its default 1, and c at 1. justdoit 10 20 would likewise set b to 20, and leave C to its default. A proc will accept a variable number of arguments if the last declared argument is the word . If the last argument to a proc argument list is , then any arguments that aren't already assigned to previous variables will be assigned to . The procedure below is defined with three arguments. At least one argument *must* be present when is called. The second argument can be left out, and in that case it will default to an empty string. By declaring as the last argument, can take a variable number of arguments. Note that if there is a variable other than after a variable with a default, then the default will never be used. For example, if you declare a proc such as: proc function { a {b 1} c} {...}, you will always have to call it with 3 arguments. Tcl assigns values to a proc's variables in the order that they are listed in the command. If you provide 2 arguments when you call they will be assigned to a and b , and Tcl will generate an error because c is undefined. You can, however, declare other arguments that may not have values as coming after an argument with a default value. For example, this is valid:
proc example {required {default1 a} {default2 b} args} {...} In this case, requires one argument, which will be assigned to the variable required . If there are two arguments, the second arg will be assigned to default1 . If there are 3 arguments, the first will be assigned to required , the second to default1 , and the third to default2 . If example is called with more than 3 arguments, all the arguments after the third will be assigned to .
Example proc examp le {first {secon d ""} args} args} {
if {$seco {$se con n d == ""} { pu ts "There "There is on ly on e argum ent and it is: $first $first" return 1 } else { if {$args { $args == ""} { pu ts "There are are two two argum ent en ts - $first $first and an d $secon $se con d" return 2 } else { pu ts "There "There are are m any an y argu argu m ent en ts - $fir $first st and an d $second and an d $args" return eturn "man "m an y" } } } set coun t1 set coun t2 set coun t3 set coun t4
[examp le ON E] [examp le ON E TWO] [exam [exam ple ONE TWO THREE ] [exam [exam ple ONE TWO THREE FOU R]
put pu ts "The "The examp le was call ed with with $cou nt1, $cou $coun n t2, $cou $count3, nt3, an an d $coun t4 Argu m ent en ts"
Previous lesson
|
Index
|
Next lesson
Variabl e scope - global and upvar Previous lesson
|
Index
|
Next lesson
Tcl evaluates a variable name within one of two scopes: the local scope within a proc, and a global scope (the code and variables outside of any proc). Like C, Tcl defines only one global space. The scope in which a variable will be evaluated can be changed with the command. The instead.
or
command will cause a variable in a local scope to be evaluated e valuated in the global scope
The command behaves similarly. ties the name of a variable in the current scope to a variable in a different scope. This is commonly used to simulate pass-by-reference to procs. The syntax for
is:
upvar ?level? otherVar1 myVar1 ?otherVar2 myVar2 ...?
causes myVar1 to become a reference to otherVar1, and myVar2 to become a reference to otherVar2, etc. The otherVar variable is declared to be at level relative to the current procedure. By default level is 1, the next level up. If a number is used for the level, then level references that many levels up the stack from the current level. If the number is preceeded by a symbol, then it references that many levels down from the global scope. If level is , then the reference is to a variable at the global level. My personal opinion is that using upvar with anything except #0 or 1 is asking for trouble. The use of global is hard to avoid, but you should avoid having too many global variables. If you start needing lots of globals, you may want to look at your design again. Note that since there is only one global space it is surprisingly easy to have name conflicts if you are importing other peoples code and aren't careful. It is recommended that you start global variables with an identifiable prefix to help avoid unexpected conflicts.
Example proc SetPositive SetPositive {variab {variable le valu e } { up var $variabl $variable e m yvar yvar if {$value < 0} { set myvar m yvar [expr [e xpr -$value] $valu e] } else { set myv m yvar ar $value $value } return eturn $m yvar yvar } SetPosit SetPosi tive x 5 SetPositive y -5
puts puts "X : $x
Y: $y n"
proc two two {y} { y} { up var 1 $y z up var 2 x a pu ts "t "two: Z: $z A: $a" $a" set z 1 set a 2 }
;# tie the callin call in g value to variabl e z ;# Tie variab variable le x two levels level s up to a ;# Outpu O utputt the valu es, jus justt to con firm ;# Set z, th e passed pas sed variab variable le to 1; ;# Set x, two layers la yers u p to 2;
proc proc on e {y} { up var $y z pu ts "one: "one : Z: $z" two z }
;# This ties the call in g value to variabl e z ;# Ou tpu t that ha t value, value , to check ch eck it is 5 ;# call proc two, which will chang ch ang e the value
on e y puts puts " nX: $x Y: $y"
Previous lesson
;# Call one, on e, and out ou tpu t X and an d Y aft after the call.
|
Index
|
Next lesson
Tcl Data Structures Structures 1 01 - The list Previous lesson
|
|
Index
Next lesson
The list is the basic data structure to Tcl. A list is simply an ordered collection of stuff; numbers, words, strings, etc. For instance, a command in Tcl is just a list in which the first list entry is the name of a proc, and subsequent members of the list are the arguments to the proc. Lists can be created in several ways: by setting a variable to be a list of values set lst {{item 1} {item 2} {item 3}} with the split command set lst [split "item 1.item 2.item 3" "."] with the list command. set lst [list "item 1" "item 2" "item 3"] An individual list member can be accessed with the
command.
The brief description of these commands is: list ?arg1 arg2 ...?
makes a list of the arguments split string ?splitChars? Splits the string into a list of items at splitChars. SplitChars defaults to being whitespace. Note that if there are two splitChars in a row in the string being split, then each be parsed as marking a list entry, and will create an empty entry in the list. If splitChars is a string, then each member of the string will be used to split string into list members. Ie:
would split into a list composed of
.
lindex list index
returns the index'th item from the list. The first item is 0. llength list
Returns the number of elements in a list. The items in list can be iterated through using the
command.
foreach varname list body
will execute the body code one time for each list item in list. On each pass, varname will contain the value of the next list item.
Example set x "a "a b c" puts "Item 2 of the li st {$x} {$x} is: [li nd ex $x $x 2] 2] n" set y [sp lit 7/4/1776 "/"] puts "We "We celebrat celebrate e on the [ lin dex $y $y 1]'t 1]'th d ay of the [lin dex $y 0]'t 0]'th m onth n" set z [list [lis t pu ts "arg 2 is $y" ] puts puts "A "A comm and rese resem m bles: bles: $z $z n" set i 0;
foreac foreach h j $x { pu ts "$j "$j is item item nu m ber $i $i in list x" x" in cr i; }
Previous lesson
|
Index
|
Next lesson
Adding & Deleting Dele ting members of a list Previous lesson
|
Index
|
Next lesson
The commands for adding and deleting list members are: concat ?arg1 arg2 ... argn? Concatenates the args into a single list. It also eliminates leading and trailing spaces in the arg's and adds a single separator space between arg's. Args to may be either individual elements, or lists. If an arg is already a list, the contents of that list is concatenated with the other args. lappend listName ?arg1 arg2 ... argn? Appends the args to the list listName treating each arg as a list element. linsert listName index arg1 ?arg2 ... argn?
Returns a new list with the new listelements inserted just before the indexth element of listName. Each element argument will become a separate element of the new list. If index is less than or equal to zero, then the new elements are inserted at the beginning of the list. If index has the value end, or if it is greater than or equal to the number of elements in the list, then the new elements are appended to the list. lreplace listName first last ?arg1 ... argn?
Returns a new list with N elements of listName replaced by the args. If first is less than or equal to 0, lreplace starts replacing from the first element of the list. If first is greater than the end of the list, or the word en d , then lreplace behaves like lappend. If there are fewer args than the number of positions between first and last, then the positions for which there are no args are deleted. Take a look at the example code, and pay special attention to the way that sets of characters are grouped into single list elements.
Example set b [li st a b {c d e} {f {g h }}] puts "Treated as a list: $b n" set b [sp lit "a "a b {c d e} {f {g h }}"] puts puts "T "Transfor ansform m ed by split: split: $b n" set a [con cat a b {c d e} {f {g h }}] puts puts "Concat "Concated: $a $a n" lappend a {ij K lm } puts puts "A "After lappendin g: $a n"
;# Note: Note: {ij K lm } is a single elemen t
set b [linse [li nserrt $a 3 "1 2 3"] puts "A "After lin sert sert at position position 3: $b n"
;# "1 2 3" is a sin gle elem ent en t
set b [lr [l repl ace $b 3 5 "AA" "AA" "BB"] puts "A "After lreplacin lreplacin g 3 p ositions ositions with 2 values at position position 3: $b n"
Previous lesson
|
Index
|
Next lesson
Mo re list comm ands - lsearch, lsort, lrange Previous lesson
|
Index
|
Lists can be searched with the command, sorted with the of list entries can be extracted with the command.
Next lesson
command, and a range
lsearch list pattern searches list for an entry that matches pattern, and returns the index for the first match,
or a -1 if there is no match. lsort list
sorts list and returns a new list in the sorted order. By default, it sorts the list into alphabetic order. lrange list first last
returns a list composed of the first through last entries in the list. If first is less than or equal to 0, it is treated as the first list element. If last is en d or a value greater than the number of elements in the list, it is treated as the end. If first is greater than last then an empty list is returned. By default, lsearch uses the globbing method of finding a match. Globbing is the wildcarding technique that most Unix shells use. globbing wildcards are: *
Matches any quantity of any character ?
Matches one occurrence of any character \X
The backslash escapes a special character in globbing just the way it does in Tcl substitutions. Using the backslash lets you use glob to match a * or ? . [...]
Matches one occurrence of any character within the brackets. A range of characters can be matched by using a range between the brackets. For example, [a-z] will match any lower case letter.
Example set list [list {Washin gton 1789} {Adam s 1797} {Jeff {Jefferson erson 1801} {Madison 1809} 1809} {Mo nroe 1817} 1817} {Adam {Adam s 1825} ] set x [lsear [lse arch ch $list Washin gton*] gton*] set y [lsearch [lsearch $list Madison *] in cr x; in cr y -1
;# Set range an ge to be n ot-in clu sive
set subset subs etlilist st [lr [l range an ge $list $x $y] puts "T "The following presiden presiden ts s erve erved d between between Washi ngton an d Madison " foreach foreach item item $subsetlist $sub setlist { pu ts "Start artin g in i n [lin [ lin dex $item 1]: President Presiden t [lind [li nd ex $it $item em 0] " } set x [lsearch [l search $list $li st Mad ison iso n *] set srtlist li st [lso [l sorrt $li $li st] set y [lsearch [l search $srtlist li st Madiso Mad ison n *] puts " n$x Presiden ts came befor before e Madison chronol chronol ogicall y" pu ts "$y "$y Pr Presid ents cam e before before Madison Madi son alph abetically"
Previous lesson
|
Index
|
Next lesson
String Subcommand s - length index ran ge Previous lesson
|
Index
|
Next lesson
One feature of Tcl is that commands may have subcommands. String is an example of one of these. The string command treats the first argument as a s a subcommand. This lesson covers these string subcommands: string length string
Returns the length of string string index string index
Returns the char at the index'th position in string string range string first last
Returns a string composed of the characters from first to last
Example set strin g "th is is m y test test strin g" puts "Ther "There e are are [str [string leng th $str $string ] charact character ers s in
"$string
""
puts "[str [string i nd ex $string 1] is the the secon d charact character er in "$st "$strring
""
puts " "[strin g range $str $string 5 10] " are are characters ers between between the the 5't 5'th and 10't 10'th"
Previous lesson
|
Index
|
Next lesson
String compa risons - compare match first last wordend Previous lesson
|
Index
|
Next lesson
There are 6 string subcommands that do pattern and string matching. string compare string1 string2 Compares string1 to string2 and returns -1 ..... If string1 is less than string2 0 ........ If string1 is equal to string2 1 ........ If string1 is greater than string2
These comparisons are done lexicographically, not numerically. string first string1 string2
Returns the index of the character in string1 that starts the first match to string2, or -1 if there is no match to string2 in string1 string last string1 string2
Returns the index of the character in string1 that starts the last match to string2, or -1 if there is no match to string2 in string1 string wordend string index
Returns the index of the character just after the last one in the word which contains the index'th character of string. A word is any contiguous set of letters, numbers or underscore characters, or a single other character. string wordstart string index
Returns the index of the character just before the first one in the word which contains the index'th character of string. A word is any contiguous set of letters, numbers or underscore characters, or a single other character. string match pattern string Returns 1 if the pattern matches string. Pattern is a glob style pattern. Globbing is the wildcarding technique that the shell uses. globbing wildcards are: *
Matches any quantity of any character ?
Matches one occurrence of any character \X
The backslash escapes a special character in globbing just the way it does in Tcl substitutions. Using the backslash lets you use glob to match a * or ? . [...]
Matches one occurrence of any character within the brackets. A range of o f characters can be matched by using a range between the brackets. For example, [a-z] will match any lower case letter.
Example set ful lpat lp ath h "/usr/ho m e/cl if/TCL_ if/TCL_STUFF/TclTut STUFF/Tcl Tutor/L or/Lsn sn .17" .1 7" set rela relattivepat ivepa th "CVS/En tries" ies " set directorypath orypath "/usr/bin "/u sr/bin /" set paths [li [ list st $full $fullpath path $relativepa $relativepatth $directory $directorypa patth] foreach foreach pat p ath h $paths $paths { set se t first first [strin [strin g first "/" $pat $pa th ]
set last [str [strin g last las t "/" $path $path ] # Rep R eport ort whether path is absolu abs olu te or rela relattive if {$first { $first != 0} { pu ts "$path "$path is a relative path" } else { pu ts "$path "$path is an absolute absol ute path" path" } # If "/" is n ot th e last l ast character character in $path, report th e last l ast word. word. # el se, remove rem ove th th e last la st "/", an d find fin d the n ext to last la st "/", an d # report ep ort th e last la st word. in cr last if {$last != [strin [strin g len l en gth $path]} { set nam na m e [str [string in g range $path $path $last end] pu ts "The "The file refer referen enced ced in i n $path $path i s $nam e" } else { in cr last -2; -2; set tm p [st [s trin g rang e $path 0 $last] set last [str [strin g last las t "/" $t $tm p] in cr last; last; set nam na m e [str [string in g range $tm $tm p $last end ] pu ts "The "The fin al direct d irectory ory in $path $path is $nam e" } # CVS is a d irectory irectory created by b y the CVS sou rce cod e con trol s ystem ystem . # if {[st {[s trin g m atch atch "*CVS*" CVS*" $path] $path]}} { pu ts "$path "$path is p art of the the sou rce co de con trol tree" } # Com C om pare to to "a" to det de termi ne whether th e first first char ch ar is up per pe r or lower case set com parison [str [string in g com pare $nam e "a"] "a"] if {$com parison >= 0} { pu ts "$n "$n am e star startts with with a lower l owercase case letter n" } else { puts "$nam "$nam e star startts with with an upp ercase ercase lett letter n" } }
Previous lesson
|
Index
|
Next lesson
Mo difying Strings - tolow t olow er, toupper , trim, format Previous lesson
|
Index
|
Next lesson
These are the commands which modify a string. Note that none of these modify the string in place. In all cases a new string is returned. tolower string Returns string with all the letters converted from upper to lower case. toupper string Returns string with all the letters converted from lower to upper case. trim string ?trimChars? Returns string with all occurrences of trimChars removed from both ends. By default trimChars are whitespace (spaces, tabs, newlines) trimleft string ?trimChars? Returns string with all occurrences of trimChars removed from the left. By default trimChars are whitespace (spaces, tabs, newlines) trimright string ?trimChars? Returns string with all occurrences of trimChars removed from the right. By default trimChars are whitespace (spaces, tabs, newlines) format formatString ?arg1 arg2 ... argN? Returns a string formatted in the same manner as the ANSI sprintf procedure. FormatString is a description of the formatting to use. The full definition of this protocol is in the man page. A useful subset of the definition is that formatString consists of literal words, backslash sequences, and % fields fields . The % fields fields are strings which start with a % and end with one of: s ... Data is a string d ... Data is a decimal integer x ... Data is a hexadecimal integer o ... Data is an octal integer ... Data is a floating point number ... f The % may be followed by - ... Left justify the data in this field + ... Right justify the data in this field
The justification value may be followed by a number giving the minimum number of spaces to use for the data.
Example set up per "THIS "THIS IS A STRING IN U PPER CASE CASE LETTERS" LETTERS" set lower "th is i s a strin g i n lower case l ett etters" set trailer "Thi "This s strin g has trailing aili ng dots .. .. " set leader ".. ".. .. This str strin g h as leadin g d ots" ots" set both both "((t "((thi s str strin g is i s nest n ested ed in i n p aren arens s )))" pu ts "tolo "tolower wer converts this: $upper" $up per" puts puts " to this: [str [string tolower $upper] $upper] n" pu ts "tou "toupp pp er conver con vertts this: $lower" puts puts " to this: [str [string toupp er $lower $lower]] n" pu ts "trim righ ig h t conver con vertts thi s: $trailer" ail er" put pu ts " to this: hi s: [str [strin g trim righ t $trailer .] n " pu ts "tr "trim left con vert verts this: this : $leader" $lead er" puts puts " to this: [str [string trim left left $leader .] n" pu ts "trim con vert verts th th is: $both" puts puts " to this: [str [string trim $both "()" "()"]] n" set labels labe ls [form [form at "%-20s "%-20s %+10s %+10s " "Item "Item " "Cost"] "Cost"] set price price1 1 [form [form at "%-20s "%-20s %10d % 10d Cen C en ts Each Eac h " "Tom "Tom atoes" atoes" "30"] set price price2 2 [form [form at "%-20s "%-20s %10d % 10d Cen C en ts Each Eac h " "Pepp "Pepp ers" "20"]
set price3 [format [forma t "%-20s "%-20s %10d Cen ts Each " "On "On ion s" "10"] "10"] set price4 [format [form at "% "% -20s % 10.2f 10. 2f per Lb." "Steak" "Steak" "3.59997"] "3.59 997"] puts puts pu ts pu ts pu ts pu ts pu ts
" n Example Example of form orm at: n" "$labe "$labels" ls" "$price "$price1" 1" "$price "$price2" 2" "$price "$price3" 3" "$price "$price4" 4"
Previous lesson
|
Index
|
Next lesson
Regular Expressions 101 Previous lesson Index Next lesson | | Tcl also supports string operations known as regular expressions Several commands can access these methods with a -regexp argument, see the man pages for which commands support regular expressions.
There are also two explicit commands for parsing regular expressions.
?switch switches? es? exp str strin g ?m ?m atch atchVar Var? ? ?subM su bMat atch ch1 1 .. . . . subM su bMat atch chN N ? Searches str strin g for the regular expression exp exp . If a parameter m atch atchV Var is given, then the substring that matches the regular expression is copied to m atch . If subMatchN chV Var variables exist, then the parenthetical parts of the matching string are copied to the subMatch variables, working from left to right.
?switch switch es? exp str strin g su b Spec Sp ec varN varN am e Searches str strin g for substrings that match the regular expression exp exp and replaces them with subSpec . The resulting string is copied into varN arN am e . Regular expressions can be expressed in just a few rules. ^ Matches the beginning of a string $ Matches the end of a string . Matches any single character * Matches any count (0-n) of the previous character + Matches any count, but at least 1 of the previous character [...] Matches any character of a set of characters [^...] Matches any character *NOT* a member of the set of characters following the ^. (...) Groups a set of characters into a subSpec. Regular expressions are similar to the globbing that was discussed in lessons 16 and 18. The main difference is in the way that sets of matched characters are handled. In globbing the only way to select sets of unknown text is the symbol. This matches to any quantity of any character. In regular expression parsing, the symbol matches zero or more occurrences of the character immediately proceeding the . For example would match a, aaaaa, or a blank string. If the character directly before the is a set of characters within square brackets, then the will match any quantity of all of these characters. For example, would match aa, abc, aabcabc, or again, an empty string. The symbol behaves roughly the same as the , except that it requires at least one character to match. For example, would match a, abc, or aabcabc, but not an empty string. Regular expression parsing is more powerful than globbing. With globbing you can use square brackets to enclose a set of characters any of which will be a match. Regular expression parsing also includes a method of selecting any character not in a set. If the first character after the is a caret ( ), then the regular expression parser will match any character not in the set of
characters between the square brackets. A caret can be included in the set of characters to match (or not) by placing it in any position other than the first. The command is similar to the command in that it matches an against a string. It is different d ifferent in that it can match a portion of a string, instead of the entire string, and will place the characters matched into the variable. If a match is found to the portion of a regular expression enclosed within parentheses, will copy the subset of matching characters is to the argument. This can be used to parse simple strings. will copy the contents of the string to a new variable, substituting the characters that match with the characters in . If subSpec contains a or , then those characters will be replaced by the characters that matched exp exp . If the number following a backslash is 1-9, then that backslash sequence will be r eplaced by the appropriate portion of exp exp that is enclosed within parentheses. Note that the argument to or is processed by the Tcl T cl substitution pass. Therefore quite often the expression is enclosed in braces to prevent any special processing by Tcl.
Example set sam ple "Where "Where there there is a will , There There is a way." way." # # Mat M atch ch th e first first subs su bsttrin g with lo wercase wercase letters only on ly # set resu lt [regexp {[ a-z]+} z]+} $sam pl e m atch atch]] put pu ts "Result: $resul $resul t m atch: atch: $mat $m atch ch"" # # Mat M atch ch th e first first two words words,, the the first first on e allo al lows ws up percase set resu lt [reg [regexp exp {([A{ ([A-Za-z]+ Za-z]+)) +([a-z]+ ([a-z]+)} )} $sam pl e m atch atch sub1 su b1 sub s ub 2 ] put pu ts "Result: $resul t Match: Match: $mat $m atch ch 1: $sub 1 2: $sub 2" # # Replace Rep lace a word word # regsu b "way" "way" $sam ple "lawsui t" samp le2 puts "New: "New: $sam ple2" # # U se the the -all option o ption to coun t the nu m ber of "wor "words" ds" # put pu ts "Num ber of words: words: [regexp [regexp -all -all {[^ ]} $sam ple] "
Previous lesson
|
Index
|
Next lesson
More Examples Of R egular Expressions Previous lesson Index Next lesson | | Regular expressions provide a very powerful method of defining a pattern, but they are a bit awkward to understand and to use properly. So let us examine some more examples in detail.
We start with a simple yet non-trivial example: finding floating-point numbers in a line of text. Do not worry: we will keep the problem simpler than it is in its full generality. We only consider numbers like 1.0 and not 1.00e+ 1. 00e+01 01 . How do we design our regular expression for this problem? By examining typical examples of the strings we want to match:
Valid numbers are: 1.0, 1. 0, .02, . 02, +0. +0.,, 1, +1, +1, -0.0120 0. 0120
Invalid numbers (that is, strings we do not want to recognise as numbers but superficially look like them): -, +. , 0.0. 0. 0.1, 1, 0. . 2, ++ ++1
Questionable numbers are: +0000 an d 0001
We will accept them - because they normally are accepted and because excluding them makes our pattern more complicated. A pattern is beginning to emerge:
A number can start with a sign (- or +) or with a digit. This can be captured with the expression [-+]?, which matches a single "-", a single "+" or nothing. A number can have zero or more digits in front of a single period (.) and it can have zero or more digits following the period. Perhaps: [0-9]*.[0-9]* will do ... A number may not contain a period at all. So, revise the previous expression to: [0-9]
*.?[0-9]* The total expression is: [-+ [-+]?[0]?[0-9]* 9]* .?[0-9]*
At this point we can do three things: 1. Try the expressi expression on with a bunch bunch of examples examples like like the ones above above and see if the the proper ones match and the others do not. 2. Try to make make it look nicer, nicer, before we start start off testing testing it. it. For instance instance the the class of characters "[0-9]" is so common that it has a shor tcut, "\d". So, we could settle for: f or: [-+]? d * . ? d *
instead. Or we could decide that we want to capture the digits before and after the pe riod for special processing: [-+ [-+]?([0-9])* ]?([0-9])* . ?([0-9]*) ([0-9]*)
3. Or, and that that may be a good strate strategy gy in general!, general!, we can can carefully carefully examine examine the pattern pattern before we start actually using it. You see, there is a problem with the above pattern: all the parts are optional, that is, each part can match a null string - no sign, no digits before the period, no period, no digits after the period. In other words: Our pattern can match an empty string! Our questionable numbers, like "+000" will be perfectly acceptable and we (grudgingly) agree. But more surprisingly, the strings "--1" and "A1B2" will be accepted too! Why? W hy? Because the pattern can start anywhere in the string, so it would match the substrings "-1" and "1" respectively! We need to reconsider our pattern - it is too simple, too permissive:
The character before a minus or a plus, if there is any, can not be another digit, a period or a minus or plus. Let us make it a space or a tab or the beginning o f the string: ^|
[ t] This may look a bit strange, but what it says is: either the beginning of the string (^ outside the square br ackets) or (the vertical bar) a space or tab (remember: the string "\t" represents the tab character).
Any sequence of digits before the period (if there is one) is allowed: [0-9]+.? There may be zero digits in front of the period, per iod, but then there must be at least one digit behind it: .[0-9]+ And of course digits in front and behind the period: [0-9]+.[0-9]+ The character after the string (if any) can not be a "+","-" or "." as that would get us into the unacceptable number-like strings: $|[^+-.] (The dollar sign signifies the end of the string)
Before trying to write down the complete regular expression, let us see what different forms we have:
No period: [-+]?[0-9]+ A period without digits before it: [-+]?.[0-9]+ Digits before a period, and possibly digits after it: [-+]?[0-9]+.[0-9]*
Now the synthesis: (^| [
t])([])([-+]?([0-9]+|
.[0-9]+| [0-9]+ .[0-9]*))($ ))($|| [^ +-.])
Or: (^ | [ t])([-+]?( d +| . d +| d + . d *))($| [ ^ +-. ]) ])
The parentheses are needed to distinguish the alternatives introduced by the vertical bar and to capture the substring we want to have. Each set of parentheses also defines a substring and
this can be put into a separate variable: regexp {. .. .. } $lin e whole char_before num n um ber nosig n ch ar_ ar_aft after # # Or sim ply on ly the the recognised nu m ber (x's (x's as placehold ers, ers, the the # last las t can be lef l eftt out # regexp egexp {. ... .} $line x x num ber
Tip: To identify these substrings: just count the opening parentheses from left to right.
If we put it to the test: set pat pattern {(^| {(^| [ t])( ])([-+]?( d+| . d+| d+ . d*))($|[^+|[^+-.]) .])} set exam pl es {"1.0" {"1.0 " " . 02" " +0." 0. " "1" "+1" " -0. 0120" 012 0" "+0000 00 00"" " - " "+. " "0001" "0.. "0. . 2" "++1" "A1.0B" "A1"} foreach oreach e $exam $exam ples ple s { if { [regexp $p att attern $e whole who le char_ ch ar_before num n um ber dig d igit its_ s_before_period ] } { pu ts ">> ">>$e<< $e<<: $n um ber ($wh ($wh ole)" ol e)" } else { pu ts ">> ">>$e<<: D oes n ot contain contain a valid nu m ber" } } }
the result is: >>1.0< 1. 0<< <: 1.0 (1.0) (1.0 ) >> . 02<< 02<<: .02 .0 2 ( . 02) >> +0.<< +0. <<: +0. +0. ( +0. ) >>1<<: 1 (1) ( 1) >>+1<<: +1 (+1) >> -0.0120 0. 0120<< <<: -0.0120 -0.01 20 ( -0.0120 0. 0120)) >>+0000<<: 0000<<: +0000 (+00 (+0000) 00) >> - <<: Does D oes n ot cont con tain a valid nu m ber >>+. <<: D oes n ot con tain a valid nu m ber >>0001< 0001 <<: 0001 (0001) (000 1) >>0.. 2<< 2<<: Does Do es not no t con tain a valid valid nu m ber >>++1<< 1<<: Does Do es n ot con tain a valid n um ber >>A1.0B<< A1.0B<<: D oes n ot con tain a valid nu m ber >>A1< A1<<: D oes n ot con tain a valid nu m ber
So our pattern correctly accepts the strings we intended to be recognised as numbers and rejects the others. Let us turn to some other patterns now:
Text enclosed in a string: This is "quoted text". If we know the enclosing character in advance (double quotes, " in this case), then "([^"])*" will capture the text inside the double quotes. Suppose we do not know the enclosing character (it can be " or '). Then: regexp {(["'])[^ {(["'])[^ "']* "']* 1} $string in g enclosed_ encl osed_str strin g
will do it - the \1 is a so-called back-reference to the first captured substring. You can use this technique to see if a word occurs twice in the same line of text:
set str string "Again and ag ain an d again .. ." if { [rege [regex xp {( y w+ y).+ 1} $string => word] ord] } { pu ts "The word word $word $word occu oc currs at least leas t twice" }
(The pattern \y matches the beginning or the end of a word and \w+ indicates we want at least one character). Suppose you need to check the parentheses in some mathematical expression: (1+a)/ (1-b*x) for instance. A simple check is counting the open and close parentheses: # # U se the the return eturn value of [regexp] [regexp] to coun t the n um ber of # p arent arentheses heses . .. # if { [regexp -all -all {(} $strin g] != [regexp -all {)} $strin g] } { puts "Parentheses unbalanced!" }
Of course, this is just a rough check. A better one is to see if at any point while scanning the string there are more close parentheses than open parentheses. We can easily extract the parentheses and put them in a list (the -inline option does that): set parens [regexp [regexp -in -inlin lin e -all -all {[ ()]} $strin g] set balan ce 0 set chan ge("(") ge("(") 1 ;# Thi Thi s techn iqu e saves an if-block bloc k :) set chan ge(")") -1 foreach oreach p $parens { in cr balance $chan ge($p) if { $balan ce < 0 } { puts "Parentheses unbalanced!" } }
Finally: Regular expressions are very powerful, but they have certain theoretical limitations.
One of these limitations is that they are not suitable for parsing arbitrarily nested text. You can experiment with regular expressions using the VisualRegexp or Visual REGEXP applications. More on the theoretical background and practical use of regular expressions (there is lots to cover!) can be found in the book Mastering Regular Expressions by J. Friedl. Previous lesson Index Next lesson | |
More Quoting Hell - Regular Expressions Expre ssions 102 Previous lesson
|
Index
|
Next lesson
?switch switches? es? exp str strin g ?m ?m atch atchVar Var? ? ?subM su bMat atch ch1 1 .. . . . subM su bMat atch chN N ? Searches str strin g for the regular expression exp exp . If a parameter m atch atchV Var is given, then the substring that matches the regular expression is copied to m atch . If subMatchN chV Var variables exist, then the parenthetical parts of the matching string are copied to the subMatch variables, working from left to right.
?switch switch es? exp str strin g su b Spec Sp ec varN varN am e Searches str strin g for substrings that match the regular expression exp exp and replaces them arN am e . with subSpec . The resulting string is copied into varN The regular expression (exp exp ) in the two regular expression parsing commands is evaluated by the Tcl parser during the Tcl substitution phase. This can provide a great deal of power, and also requires a great deal of care. These examples show some of the trickier aspects of regular expression evaluation. The fields in each example are discussed in painful detail in the most verbose level. The points to remember as you read the examples are:
A left square bracket ([) has meaning to the substitution phase, and to the regular expression parser. A set of parentheses, a plus sign, and a star have meaning to the regular expression parser, but not the Tcl substitution phase. A backslash sequence (\n, \t, etc) has meaning to the Tcl substitution phase, but not to the regular expression parser. A backslash escaped character (\[) has no special meaning to either the Tcl substitution phase or the regular expression parser.
The phase at which a character has meaning affects how many escapes are necessary to match the character you wish to match. An escape can be either enclosing the phrase in braces, or placing a backslash before the escaped character. To pass a left bracket to the regular expression parser to evaluate as a range of characters takes 1 escape. To have the regular expression parser match a literal left bracket takes 2 escapes (one to escape the bracket in the Tcl substitution phase, and one to escape the bracket in the regular expression parsing.). If you have the string placed within quotes, then a backslash that you wish passed to the regular expression parser must also be escaped with a backslash. Note: You can copy the code and run it in tclsh or wish to see the effects.
Example # # Exami n e an overview overview of U NI X/Lin X/Linux ux disks # set list1 [list [li st {/dev/wd0a 17086 10958 5272 68% /} {/dev/w {/de v/wd0f d0f 179824 127798 48428 73% /news} {/dev/wd0h 1249244 967818 218962 82% /usr} {/dev/wd0g 98190 32836 60444 35% /var}] foreach oreach lin e $list1 $list1 { regexp {[^ ]* *([0-9]+)[^ /]*(/[a/]*(/[a-z]* z]*)} $lin e m atch atch size m oun ted;
pu ts "$mo "$moun un ted is $size blocks" } # # Extractin actin g a hexadecim hexadeci m al valu valu e .. . # set lin li n e {In {I n terrup t Vector? Vector? [32(0x20)]} reg ex exp " [^ t]+ t [ [0-9]+ (0x( [0-9a-fA-F]+) pu ts "Hex Defaul t is: 0x$he 0x$hexv xval" al"
)]" $li ne ne m at atch he hexval
# # Matchin Matchi n g the special spe cial ch aracters as if they he y were were ordin ary ary # set str str2 "abc^ "abc ^ def de f" regexp " [^ a-f]*def" ]*def" $str2 m atch atch puts puts "using "using [^ a-f] the the m atch is: $match" $match" regexp " [a-f [a-f^ ]*def" ]*def" $str2 m atch atch puts puts "using "using [a-f^ ] the the m atch is: $match" $match" regsub { ^ } $str2 " is followed followed by: " str3 puts puts "$s "$sttr2 wit with h the the ^ substit substitut uted ed is: "$str3 "" regs egsub "( [a[a-f]+) ^( [a[a-f]+)" $str2 " puts puts "$s "$sttr2 is convert converted to to "$str3 ""
Previous lesson
2 follo ollow ws
|
1" str3
Index
|
Next lesson
Associative Arrays. Previous lesson
|
Index
|
Next lesson
Languages like C, BASIC, FORTRAN and Java support arrays in which the index value is an integer. Tcl, like most scripting languages (Perl, Python, PHP, etc...) supports associative arrays (also known as "hash tables") in which the index value is a string. The syntax for an associative array is to put the index within parentheses: set nam e(first) e(first) "Mary" "Mary" set nam e(last) e(last) "Popp "Popp in s" pu ts "Full "Full nam na m e: $nam e(fir e(first st)) $n $n am e(last)" e(last)"
There are several array commands aside from simply accessing and creating arrays which w ill be discussed in this and the next lesson.
arr arrayNam ayNa m e Returns 1 if arr arrayNam e is an array variable. Returns 0 if arr arrayNam e is a scalar variable, proc, or does not exist.
arr arrayNam e ? e ?pattern Returns a list of the indices for the associative array arr arrayNam e . If pattern is supplied, only those indices that match pattern are returned. The match is done using the globbing technique from str strin g m atch atch .
arr arrayNam e Returns the number of elements in array arr arrayNam e .
arr arrayNam e Returns a list in which each odd member of the list (1, 3, 5, etc) is an index into the associative array. The list element following a name is the value of that array member.
arr arrayNam ayNa m e dat d ataList aList Converts a list into an associative array. DataList is a list in the format of that returned by array get . Each odd member of the list (1, 3, 5, etc) is an index into the associative g et array, and the list element following that is the value of that array member. When an associative array name is given as the argument to the command, all the elements of the associative array become available to that proc. For this reason, Bren Brent t Welch Wel ch recommends (in Practical Programming in Tcl and Tk ) using an associative array for the state structure in a package. This method makes it simpler to share data between many procs that are working together, and doesn't pollute the global namespace as badly as using separate globals for all shared data items. Another common use for arrays is to store tables of data. In the example below we use an array to store a simple database of names.
Example proc addn am e {first {first last} last} { global global nam e # Cr C reate eate a n ew ID (stored (stored in the n am e array too for easy access) acc ess)
incr nam e(ID) set id $nam e(ID) set nam na m e($id,first e($id,first) $f $first irst ;# The in dex is sim ply a strin g! set nam e($id,last) e($id,last) $last ;# So we can use both both fixed fixed and ;# varyin varying g p arts } # # In itiali itialise se the the array array and ad d a few n am es # global global nam e set nam e(ID) 0 addn am e addnam e addn am e addn am e
Mary Poppin s Uriah Uriah Heep Rene D escart escartes Leonardo Leonardo "da "da Vinci"
# # Ch eck the con ten ts of o f our database database # The parray comm com m and is a qu ick way to # p rin t it # parray nam e # # Some arr array com m and s # arr array set arr array1 [l ist {123} {Abig ail Aardvark} Aardvark} {234} {Bob Baboon} {345} { Cathy Cathy Coyot C oyote} e} {456} {456} {Daniel Dog} ] put pu ts "Ar "Array1 has [arr [array size arr array1] entries n" puts puts "A "Array1 ay1 has the the following following entr entries: n [array nam es arr array1] ay1] n" puts puts "ID "ID N um ber 123 belon gs to to $ar $arrray1(12 ay1(123) 3) n" if {[arr {[ array exist exis t array1] array1]}} { pu ts "array1 is an array" } else { pu ts "array1 is n ot an arr a rray" } if {[arr {[ array exist exis t array2] array2]}} { pu ts "array2 is an array" } else { pu ts "array2 is n ot an arr a rray" }
Previous lesson
|
Index
|
Next lesson
Mo re Array Comman Comman ds - I terating and use in procedures | Index | Next lesson Often you will want to loop through the contents of an associative array - without having to specify the elements explicitly. For this the and commands are very useful. With both you can give a (glob-style) pattern to select what elements you need: Previous lesson
foreach oreach n am e [arr [array nam es m ydata] ydata] { puts puts "Dat "Data on "$name ": $m $m ydata($ a($nam e)" e)" } # # G et nam es and values values direct directly ly # foreach oreach {n am e value} [arr [array get mydata] mydata] { puts puts "Da "Datta on "$name ": $v $value" alue" }
Note, however, that the elements will not be returned in any predictable order: this has to do with the underlying "hash table". If you want a particular ordering (alphabetical for instance), use code like: foreach oreach nam na m e [lsor [ls ortt [arr [array nam es m ydata]] ydata]] { puts puts "Dat "Data on "$name ": $m $m ydata($ a($nam e)" e)" }
While arrays are great as a storage facility for some purposes, they are a bit tricky when you pass them to a procedure: they are actually collections of variables. This will not work: proc print12 print12 {a} { pu ts "$a(1), $a(2)" } set se t array(1 array(1)) "A" set array(2) "B" prin t12 $ar $a rray
The reason is very simple: an array does not have a value. Instead the above code should be: proc p rin t12 {ar { arrray} { up var $array $array a pu ts "$a(1), $a(2)" } set se t array(1 array(1)) "A" set array(2) "B" prin t12 array array
So, instead of passing a "value" for the array, you pass the name. This gets aliased (via the upvar command) to a local variable (that behaves the as original array). You can make changes to the original array in this way too.
Example #
# The exam ple pl e of the previous lesson les son revisit evisi ted - to get ge t a # m ore ore gen eral eral "database" "database" # proc addn am e {db first last} last} { upvar $db nam e # Cr C reate eate a new n ew ID (stored (stored in i n the n am e arr array too for easy access) acce ss) incr nam e(ID) set id $nam e(ID) set nam na m e($id,first e($id,first) $f $first irst ;# The in dex is sim ply a strin g! set nam e($id,last) e($id,last) $last ;# So we can use both both fixed fixed and ;# varyin varying g p arts } proc report {d b} { upvar $db nam e # Loop over the l ast nam es: m ake a m ap from last nam e to to ID foreach oreach n [arr [array nam es n am e "*,last"] ,last"] { # # Spli Sp litt th e nam n am e to to get g et th e ID - the first par pa rt of the n am e! # regexp {^ [^ ,]+} $n i d # # Store in a tem tem po rary array: # an "inverse" "inverse" map m ap of last nam e to to ID) ID ) # set last $nam e($n) set tm p ($last) $id $id } # # N ow we can easi ly print the n am es in the order order we we want! want! # foreach oreach last [lsort [arr [array nam na m es tmp ]] { set id $tm $tm p($last p($la st)) pu ts " $nam e($id,first e($id,first) $nam e($id,last)" e($id,last)" } } # # In itiali itialise se the the array array and ad d a few n am es # set fict ic tion io n al_n am e(ID ) 0 set h istorical istorical _n am e(ID ) 0 addn am e fictional_nam e Mary Poppin Poppin s addn am e fict fictional ional _nam e Uriah H eep addn am e fict fictional_ ional_nam e Frodo Frodo Bag gin s addnam add nam e historical_ historical_nam e Rene Descar Desc arttes addn am e histor historical_ ical_nam e Richard Richard Lionheart Lionheart addn am e h istor istorical_ ical_nam e Leonardo Leonardo "da Vinci" addn am e h istor istorical_ ical_nam e Ch arles arles Baudelaire addn am e h istor istorical_ ical_nam e Julius Caesar # # Som e sim ple report reporting # puts "Fictional characters:" report ep ort fict ic tion io n al_n am e pu ts "Hist "Hi storical orical ch aracter aracters:" report ep ort h istorical istorical _n am e
Previous lesson
|
Index
|
Next lesson
File Access 101 Previous lesson Index Next lesson | | Tcl provides several methods to read from and write to files on disk. The simplest methods to access a file are via and . When there is a lot of data to be read, however, it is sometimes more efficient to use the command to load an entire file, and then parse the file into lines with the command.
These methods can also be used for communicating over sockets or over pipes. It is even possible, via the so-called virtual file system to use files stored in memory rather than on disk. Tcl provides an almost uniform interface to these very different resources, so that in general you do not need to concern yourself with the details.
fileN il eNam am e ?access? access ? ?perm perm issio iss ion n ? Opens a file and returns a token to be used when accessing the file via etc. is the name of the file to open. FileName access is the file access mode ......Open the file for reading. The file must already exist. ...Open the file for reading and writing. The file must already exist. .....Open the file for writing. Create the file if it doesn't exist, or set the length to zero if it does exist. ..Open the file for reading and writing. Create the file if it doesn't exist, or set the length to zero if it does exist. ......Open the file for writing. The file must already exist. Set the current location to the end of the file. ...Open the file for writing. The file does not exist, create it. Set the current location to the end of the file. perm ission is an integer to use to set the file access permissions. The default is perm rw-rw-rw- (0666). You can use it to set the permissions for the file's owner, the group he/she belongs to and for all the other users. For many applications, the default is fine.
fileID Closes a file previously opened with
, and flushes any remaining output.
fileID il eID ?varN arN am e? Reads a line of input from FileID , and discards the terminating newline. If there is a varN returns the number of characters read (or -1 if an arN am e argument, EOF occurs), and places the line of input in varN . arN am e If varN returns the line of input. An empty string will be arN am e is not specified, returned if: There is a blank line in the file. The current location is at the end of the file. (An EOF occurs.)
?fileID eI D? str strin g Writes the characters in string to the stream referenced by fileID .
FileID is one of:
The value returned by a previous call to stdout stderr
with write access.
fileID Reads all the remaining bytes from fileID , and returns that string. If is set, then the last character will be discarded if it is a newline. Any existing end of file condition
is cleared before the
command is executed.
fileID ileI D n um Byt Bytes Reads up to n um Byt Bytes from fileID , and returns the input as a Tcl string. Any existing end of file condition is cleared before the
command is executed.
fileI il eID D of o ffset set ?origin orig in ? Change the current position within the file referenced by fileID . Note that if the file was opened with "a" access that the current position can not be set before the end of the file for writing, but can be set to the beginning of the file for reading. fileID is one of: a File identifier returned by stdin stdout stderr is the offset in bytes at which the current position is to be set. The position offset from which the offset is measured defaults to the start of the file, but can be from the current location, or the end by setting origin appropriately. from. It defaults to the start of the file. origin is the position to measure offset Origin must be one of: .........Offset is measured from the start of the file. ...Offset is measured from the current position in the file. ...........Offset is measured from the end of the file.
fileID Returns the position of the access pointer in fileID as a decimal string.
fileID Flushes any output that has been buffered for fileID .
fileID returns 1 if an End Of File condition exists, otherwise returns 0. Points to remember about Tcl file access:
The file I/O is buffered. The output may not be sent out when you expect it to be sent. Files will all be closed and flushed when your program exits normally, but may only be closed (not flushed) if the program is terminated in an unexpected manner. There are a finite number of open file slots available. If you expect the program to run in a manner that will cause it to open several files, remember to close the files when you are done with them. An empty line is indistinguishable from an EOF with the command:
or use the other form of
. Use the command to determine if the file is at the end (see the example).
You can't overwrite any data in a file that was opened with access . You can, however seek to the beginning of the file for commands. Opening a file with the access will allow you to overwrite data, but will delete all existing data in the file. Opening a file with the access will allow you to overwrite data, while saving the existing data in the file. By default the commands assume that strings represent "readable" text. If you want to read "binary" data, you will have to use the command. Often, especially if you deal with configuration data for your programs, you can use the command instead of the relatively low-level commands presented here. Just make sure your data can be interpreted as Tcl commands and "source" the file.
Example # # Cou nt the n um ber of lin es in a text text file # set in file il e [op en "myf "m yfilile. e. txt" r] set nu m ber 0 # # g ets ets with with two argu argu m ents ret returns urns the leng l eng th of the lin e, # -1 -1 if th e en d of o f th e file is foun ou n d # while { [g ets ets $infile lin l in e] >= >= 0 } { incr num ber } close $infile puts "Nu "Nu m ber of lin es: $num ber" ber" # # Also report ep ort it in an extern ern al file # set outfile il e [op [ open en "rep "report ort. ou t" w] put pu ts $outf $outfile "Nu m ber of lin es: $num ber" close $outf $outfile
Previous lesson
|
Index
|
Next lesson
I nform ation about Files - file, glob Previous lesson Index Next lesson | | There are two commands that provide information about the file system, and . Glob provides the access to the names of files in a directory. It uses a name matching mechanism similar to ls , to return a list of names that match a pattern. File provides two sets of functionality:
string manipulation appropriate to parsing file names dirname ........ Returns directory portion of path extension ........ Returns file name extension rootname ....... Returns file name without extension .................... Returns filename without directory tail .................... information about an entry in a directory: ................ Returns time of last access atime ................ executable ..... Returns 1 if file is executable by user ................ Returns 1 if file exists exists ................ isdirectory ...... Returns 1 if entry is a directory .................. Returns 1 if entry e ntry is a regular file isfile .................. ................... Returns array of file status information lstat ................... ............... Returns time of last data modification mtime ............... owned ................ Returns 1 if file is owned by user readable............ Returns 1 if file is readable by user readlink ............. Returns name of file pointed to by a symbolic link ..................... Returns file size in bytes size ..................... ..................... Returns array of o f file status information stat ..................... .................... Returns type of file type .................... writable ............ Returns 1 if file is writeable by user
Between these two commands, a program can obtain most of the information that it may need. glob ?switches? pattern ?patternN?
returns a list of file names that match pattern or patternN Switches may be one of: -nocomplain
Allows to return an empty list without causing an error. Without this flag, an error would be generated when the empty list was returned. --
Marks the end of switches. This allows the use of "-" in a pattern without confusing the glob parser. Pattern follows the same matching rules as the string match globbing rules with these exceptions: {a,b,...} Matches any of the strings a,b, etc. A "." at the beginning of a filename must match a "." in the filename. The "." is only a wildcard if it is not the first character in a name. All "/" must match exactly. If the first two characters in pattern are ~/ , then the ~ is replaced by the value of the HOME environment variable. If the first character in pattern is a ~ , followed by a login id, then the ~loginid is replaced by the path of loginid's home directory. Note that the filenames that match pattern are not in a sorted order. file atime name
Returns the number of seconds since 1/1/1970 when the file name was last accessed. Generates an error if the file doesn't exist, or the access time cannot be queried. file dirname name
Returns the directory portion of a path/filename string. If name contains no slashes, file dirname returns a ".". If the last "/" in name is also the first character, it returns a "/". file executable name
Returns a 1 if file name is executable by the current user, otherwise returns a 0. file exists name Returns a 1 if the file name exists, and the user has search access in all the directories leading to the file. Otherwise, a 0 is returned. file extension name
Returns the file extension. file isdirector isdirector y name
Returns 1 if file name is a directory, otherwise returns 0. file isfile name
Returns 1 if file name is a regular file, otherwise returns 0. file lstat name varName This returns the same information returned by the system call lstat . The results are placed in the associative array varName. The indexes in varName are: atime .......time of last access ctime .......time of last file status change ...........inode's device de v ...........inode's gi d ............group ID of the file's group ............inode's number in o ............inode's mode .......inode protection mode mtime .....time of last data modification nlink ........number of hard links size ...........file size, in bytes type ..........Type of File ui d .............user ID of the file's owner Because this calls lstat , if name is a symbolic link, the values in varName will refer to the link, not the file that is linked to. See stat also. file mtime name
Returns the time of the last data modification in seconds since Jan 1, 1970. file ow ned name
Returns 1 if the file is owned by the current user, otherwise returns 0. file readable name
Returns 1 if the file is readable by the current user, otherwise re turns 0. file readlink name
Returns the name of the file a symlink is pointing to. If name isn't a symlink, or can't be read, an error is generated. file rootname name
Returns all the characters in name up to but not including the last ".". Returns $name if name doesn't include a ".". file size name Returns the size of name in bytes. file stat name varName This returns the same information returned by the system call stat . The results are placed in the associative array varName. The indexes in varName are: atime .......time of last access ctime .......time of last file status change ...........inode's device de v ...........inode's gi d ............group ID of the file's group ............inode's number in o ............inode's mode .......inode protection mode mtime .....time of last data modification nlink ........number of hard links size ...........file size in bytes type ..........Type of file ui d .............user ID of the file's owner file tail name Returns all of the characters in name after the last slash. Returns $name if name contains
no slashes. file type name
Returns a string giving the type of file name, which will be one of: ...................................Normal .....Normal file file .............................. directory ........................Directory characterSpecial .......Character oriented device blockSpecial .............. Block oriented device ...................................Named ......Named pipe fifo ............................. ..................................Symbolic .....Symbolic link link ............................. ...........................Named med socket socket. ...........................Na file writable name
Returns 1 if file name is writable by the current user, otherwise returns 0.
Example set ail1 [ glob glo b PATTERN1] PATTERN1] set ail2 [ glob glo b PATTERN2] PATTERN2]
set fm t "%-12s %-16s %8s % -7s" pu ts "[form "[format at "$fmt "$fmt Com m ent en t" "Di "Dirrectory" ectory" "Nam e" "Inode "Ino de"" "Ty "Type pe"]" "]"
foreach oreach n am e [con cat $ail1 $ail2] $ail2] { ;# split the n am e in to pieces for displ ay: set dir [file [file di rnam e $nam e] set filen am e [file [file tail tail $nam e] ;# Coll ect som e stat status us an d type type in fo. file stat $nam e arr set type [file type type $nam $n am e] ;# Disp lay what we've we've learn learn ed. pu ts -n on ewlin e "[forma "[formatt $fm $fm t $dir $f $filen il en am e $arr $arr(in o) $type]" ype] " ;# and part particular data depen din g on whether whether item is a file or symb olic lin k. if {[str {[string in g m atch atch [f [ file type type $nam e] "link"]} "lin k"]} { pu ts " poi nts to: [file readl readlin in k $nam $nam e]" } if {[str {[string in g m atch atch [file type type $nam e] "file"]} "file"]} { pu ts " Size: [file [file size $nam e] bytes bytes " } }
Previous lesson
|
Index
|
Next lesson
I nvoking Subproces ses from Tcl - exec, open Previous lesson Index Next lesson | | So far the lessons have dealt with programming within the Tcl interpreter. However, Tcl is also useful as a scripting language to tie other packages together. To accomplish this function, Tcl has methods for invoking subprocesses.
There are two ways to invoke a subprocess from Tcl:
open ...... run a new program with I/O connected to a file descriptor exec ...... run a new program as a subprocess
The open call is the same call that is used to open a file. If the first character in the file name argument is a pipe symbol ( | ) , then open will treat the rest of the argument as a program name, and will exec that program with the standard input or output connected to a file descriptor. A pipe can be opened to a sub-process for re ading, writing or both reading and writing. If the file is opened for both reading and writing you must be aware that the pipes are buffered. The output from a puts command will be saved in an I/O buffer until the buffer is full, or until you execute a flush command to force it to be transmitted to the subprocess. The output of the subprocess will not be available to a read or gets until the I/O buffer for the subprocess has filled its output buffer. The exec call is similar to invoking a program ( or a set of programs piped together) from the shell prompt or in a unix shell script. It supports several styles of output redirection, or it can return the output of the sub-process as the return of the exec call. open | progName ?access?
Returns a file descriptor for the pipe. The progName argument must start with the pipe symbol. If progName is enclosed in quotes or braces, it can include arguments to the subprocess. exec ?switches? arg1 ?arg2? ... ?argN? Exec treats its arguments as the names and arguments for a set of subprocesses to execute. If the first args start with a " -" , then they are treated as switches to the exec
command, instead of being invoked as subprocesses or subprocess options. Switches are: -keepnewline
Retains a trailing newline in the pipeline's output. Normally a trailing newline will be deleted. --
Marks the end of the switches. The next string will be treated as arg1, even if it starts with a " -" Arg1 - argN can be one of:
the name of a program to execute an command line argument for the subprocess an I/O redirection instruction. There are many permutations per mutations to the I/O redirection commands. The main subset of these commands is:
|
Pipes the standard output of the command preceeding the pipe symbol into the standard input of the command following the pipe symbol. < fileName
The first program in the pipe will read input from fileName . < @ fileI D
The first program in the pipe will read input from the Tcl descriptor fileID . FileID is ... "r" command. the value returned from an open ... < < value
The first program in the pipe will read value as its input. > fileName
The output of the last program in the pipe will be sent to fileName . Any previous contents of fileName will be lost. > > fileName
The output of the last program in the pipe will be appended to fileName . 2> fileName
The standard error from all the programs in the pipe will be sent to fileName . Any previous contents of fileName will be lost. 2> > fileNam e
The standard error from all the programs in the pipe will be appended to fileName . > @ fileI D
The output from the last program in the pipe will be written to fileID . FileID is the value returned from an open ... ... "w " command. If you are familiar with shell programming, there are a few differences to be aware of when you are writing Tcl scripts that use the exec and open calls.
You don't need the quotes that you would put around arguments to escape them from the shell expanding them. In the example, the argument to sed is not put in quotes. If it were put in quotes, the quotes would be passed to sed, instead of being stripped off (as the shell does), and sed would report an error. If you use the open |cmd "r+" construct, you must follow each puts with a flush to force Tcl to send the command from its buffer to the program. The output from the subprocess may be buffered in its output buffer. You can sometimes force the output from the sub-process to flush by sending an exit command to the process. You can also use the
command to make a channel unbuffered.
The expect extension to Tcl provides a much better interface to other programs, which handles the buffering problem.
If one of the commands in an open |cmd fails the open does not return an error. However, attempting to read input from the file descriptor with gets $file will return an empty string. Using the gets $file input construct will return a character count of -1. Put quotes around the s / . Q / / g in the example to see this behavior.
If one of the commands in an exec call fails to execute, the exec will return an error, and the error output will include the last line describing the error.
Example # Create a un iqu e (m ostly) ostly) file file nam na m e for for a Tcl program program set tem pFileN pFil eNam am e "TEMPDIR/in "TEMPDIR/in v_[pid]. [pi d]. tcl" # Op en the out ou tpu t file, an d # write ite a simpl sim ple e prog progrram to it set outfl [open [op en $tem $tem pFileN pFil eNam am e w] pu ts $ou tfl { set len [gets [gets stdin stdin lin e]
if {$len {$le n < 5} {exit { exit -1} for {set i $len } {$i { $i >= 0} {in { in cr i -1} { appen d l 2 [str [string range $line $i $i] } pu ts $l2 exit 0; } # Flush and cl ose the the file flu sh $outfl $outfl close clo se $out $ou tfl # Ru n the n ew Tcl Tcl file int in teract eractively ively # Op en a p ipe to the program program set io [op en "|TCL_IN TERP $t $tem pFil eNam e" r+ r+] # sen d a str strin g to the the new pr p rogram # *MU ST FLUSH FLUSH * put pu ts $io "This "This will com e back backwards. backwards. " flush $io # G et the reply, epl y, and displ di splay ay it. it. set len [gets $io $io li ne] put pu ts "To "To rever reverse: se: 'Thi 'This s will com e back backwards. backwards. '" put pu ts "Rever "Reversed sed i s: $line" put pu ts "The "The l in e is $len charact c haracters ers lon g" # Run the p rogram ogram with in put defin defin ed in an exec call set invert [exec TCL_IN TERP $tem pFi leN am e << "ABLE "ABLE WAS I ERE I SAW ELBA"] ELBA"] # displ d ispl ay the result esul ts put pu ts "The "The i n version of 'ABLE 'ABLE WAS WAS I ERE I SAW SAW ELBA' ELBA' is n $in vert vert" # Clean up file d elete elete $tem $tem pFileN am e
Previous lesson
|
Index
|
Next lesson
Learning the existence existenc e of comm ands and variables ? - info | Index | Next lesson The info command allows a Tcl program to obtain information from the Tcl interpreter about the current state of the interpreter. The next three lessons cover aspects of the info command. Previous lesson
This lesson covers the info subcommands that return information about which procs, variables, or commands are currently in existence in this instance of the interpreter. By using these subcommands you can determine if a variable or proc exists before you try to access it. The example code shows how to use the info exists command to make an incr that will never return a no such variable error, since it checks to be certain that the variable exists before incrementing it. Info commands that return lists of visible commands and variables. info commands ?pattern?
info info
info
info
Returns a list of the commands that match pattern, using the string match rules. If pattern is not provided, a list of all commands is returned. exists varName Returns 1 if varName exists as a variable in the current context, otherwise returns 0. globals ?pattern? Returns a list of the global variables that match pattern, using the string match rules. If pattern is not provided, a list of all global variables is returned. locals ?pattern? Returns a list of the local variables that match pattern, using the string match rules. If pattern is not provided, a list of all local variables is returned. procs ?pattern? Returns a list of the procs that match pattern, using the string match rules. If pattern is not provided, a list of all procs is returned.
info vars ?pattern?
Returns a list of the variables that match pattern, using the string match rules. If pattern is not provided, a list of all variables is returned.
Example
proc safeIn safeIn cr {val {am t 1}} { up var $val $val v if {[in fo exists v]} v]} { in i n cr v $am $am t} else els e { set v $am $am t } }
if {[in {[i n fo pr p rocs safeIn cr] == "safeI "safeInc ncrr"} { safeIncr a } pu ts "Aft "After calli cal lin n g SafeIn cr with with a n on existen existen t variab variable: le: $a" set a 100 safeIn safeIn cr a pu ts "Aft "After calli cal lin n g Saf Sa feInc eI ncrr with with a variab variable le with a valu e of 100: $a" safeIn cr b -3 -3
pu ts "Aft "After calli cal ling ng safeIn cr with with a n on existen existen t variab variable le b y -3: $b" set b 100 safeIn cr b -3 -3 pu ts "Aft "After calli cal lin n g safeIn s afeIncr cr with with a variable wh ose valu e is 100 by b y -3: $b" puts puts " nThese variables h ave ave been d efined efined : [lsort [in fo vars]]" puts puts " nThese globals have have been been defined: defined: [lsort [lsort [info [info globals]]"
set exist [in fo pr p rocs localproc]; local proc]; if {$exi { $exist st == == ""} { puts " nlo calproc calproc does not exist exist at poin t 1" } proc localpr localp roc {} { global argv; argv; set loc1 1; set loc2 2; puts " nLocal var variables accessible in this p roc are: are: [lsort [in fo locals]]" puts " nVariables nVariables accessible accessible fr from this this proc proc ar are: [lsort [lsort [in fo var vars]]" s]]" puts " nG lobal variables variables visible fr from this p roc are: are: [lsort [in fo glob als]]" } set exist [in fo pr p rocs localproc]; local proc]; if {$exis t != ""} { pu ts "localproc "local proc does exist at poi nt 2" } localproc;
Previous lesson
|
Index
|
Next lesson
State of the interpreter - info Previous lesson Index Next lesson | | There are a number of subcommands that provide information information about the current state of the interpreter. These commands provide access to information like the current version and patchlevel, what script is currently being executed, how many commands have been executed, or how far down in the call tree the current proc is executing.
The info tclversion and info patchlevel can be used to find out if the revision level of the interpreter running your code has the support for features you are using. If you know that certain features are not available in certain revisions of the interpreter, you can define your own procs to handle this, or just exit the program with an error message. The info cmdcount and info level can be used while optimizing a Tcl script to find out how many levels and commands were necessary to accomplish a function. Note that the p id command is not part of the info command, but a command in its own right. Commands that return information ab out the current state of the interpreter info cmdcount
Returns the total number of commands that have been executed by this interpreter. info level ?number?
Returns the stack level at which the compiler is currently evaluating code. 0 is the top level, 1 is a proc called from top, 2 is a proc called from a proc, etc. If number number is a positive value, info level returns a the name and arguments of the proc at that level on the stack. Number is that same value that file level would return if it were called in the proc being referenced. re ferenced. If number number is a negative value, it refers to the current level l evel plus number. Thus, , info level returns a the name and arguments of the proc at that level on the stack. info patchlevel
Returns the value of the global variable tcl_patchlevel. This is the revision level of this interpreter. info script
Returns the name of the file currently being evaluated, if one is being evaluated. If there is no file being evaluated, returns an empty string. info tclversion
Returns the value of the global variable tcl_version. This is the patch level of this interpreter. pi d
Returns the pid of the current Tcl interpreter.
Example puts "This is h ow man y comm ands h ave ave been executed: [in fo cm dcou nt]" nt]" puts "Now *THIS* m any com m and s have been execut executed: ed: [in fo cm dcou nt]" nt]" puts puts " nThis inter interpret preter er is rev revision ision level: [in fo tclver tclversion]" pu ts "Th "Th is in terpret erpreter er is at patch level: l evel: [i n fo patchl evel]" evel] " put pu ts "The "The Pid for thi s program program is [p id] " proc factorial {val} { val} { pu ts "Curren "Curren t level: level : [in [i n fo level] level ] - val: $val"
set lvl [info [i nfo level] if {$lvl {$l vl == == $val} {return {return $val;} $val; } retu rn [expr [e xpr ($val ($val-$lvl) * [factorial [factorial $val]] $val ]];; } set coun t1 [in fo cm dcoun t] set fact fact [factorial 3] set coun t2 [in fo cm dcoun t] pu ts "The factorial o off 3 is $fact" $fact" put pu ts "Befor "Before e calling calli ng the factorial orial p roc, $count $coun t1 com m and s had b een execut e xecuted" ed" put pu ts "Af "After callin call ing g the factorial proc, $coun $coun t2 com m and s had h ad be en executed" executed" pu ts "It took [expr [e xpr $coun $cou n t2-$cou 2-$coun n t1] com m ands an ds to calculate calcu late th is factorial" factorial"
Previous lesson
|
Index
|
Next lesson
I nformation about procs - info Previous lesson Index Next lesson | | The info command includes a set of subcommands that will provide all the info you could want about a proc. These subcommands will return the body of a proc, the arguments to the proc, and the value of any default arguments.
These subcommands can be used to:
Access the contents of a proc in a debugger. Generate custom procs from a template. Report default values while prompting for input. Info commands that return information information about a proc
info args procname
Returns a list of the names of the arguments to the procedure procname. info body procname
Returns the body of the procedure procname. info default procname arg varName Returns 1 if the argument arg in procedure procName has a default, and sets varName to the default. Otherwise, returns 0.
Example proc dem o {argu {argu m ent en t1 {default "DefaultV "DefaultValu alue"} e"} } { puts "T "This i s a dem o proc. It is bein g call ed with with $argu argu m ent1 ent1 and $default $default"" } puts puts "The "The args args for for dem o are: are: [info args args dem o] n" puts puts "T "The body for dem o is: is: [info body dem o] n" set argl arglist ist [in fo args dem o] foreach arg arg $argl $argl ist { if {[in fo def de faul t dem o $arg $arg d efaul efaulttval]} { pu ts "$arg "$arg has a def de fault au lt value valu e of $de $deffaultv au ltval" al" } else { put pu ts "$ar "$arg g has no default" } }
Previous lesson
|
Index
|
Next lesson
Modularization - source Previous lesson
|
Index
|
Next lesson
The command will load a file and execute it. This allows a program to be broken up into multiple files, with each file defining procedures and variables for a particular area of functionality. For instance, you might have a file called database.tcl that contains all the procedures for dealing with a database, or a file called gui.tcl that handles creating a graphical user interface with Tk. The main script can then simply include each file using the command. More powerful techniques for program modularization are discussed in the next lesson on packages. This command can be used to:
separate a program into multiple files. make a library file that contains all the procs for a particular set of functions. configure programs. load data files.
fileNam ileN am e Reads the script in fileNam ileN am e and executes it. If the script executes successfully, returns the value of the last statement in the script. If there is an error in the script, will return that error. If there is a return (other than within a definition) then will return immediately, without executing the remainder of the script. If fileNam ileN am e starts with a tilde ( ~) then $env(HOME) will substituted for the tilde, as is done in the command.
Example sourcedata.tcl: # Exam ple pl e data file to be sourced s ourced set scr [in fo script] script] proc testproc testproc {} { global scr pu ts "testpr "testproc oc sou rce file: file : $scr $sc r" } set abc 1 return set aaaa 1
sourcemain.tcl: set filen am e "sourcedata.tcl" "sourcedata.tcl" put pu ts "Gl "Global obal variabl variables es visib visib le before before sourcing $filen $filen am e:" puts puts "[lsor "[lsortt [info [info globals]] n" if {[in fo pr p rocs testproc] testproc] eq ""} ""} { put pu ts "testproc estproc does not n ot exist. sourcing sourcin g $filen $filen am e" source source $filen $filen am e } puts " nN ow execut executing ing test testpr proc" oc" testproc put pu ts "Global variables iabl es visible after after sourcing sourcin g $filen $filen am e:"
puts puts "[lsor "[lsortt [info [info globals]] n"
Previous lesson
|
Index
|
Next lesson
Building reusable libraries - packages and namespaces Previous lesson
|
Index
|
Next lesson
The previous lesson showed how the command can be used to separate a program into multiple files, each responsible for a different area of functionality. This is a simple and a nd useful technique for achieving modularity. However, there are a number of drawbacks to using the command directly. Tcl provides a more powerful mechanism for handling reusable units of code called packages. A package is simply a bundle of files implementing some functionality, along with a name that identifies the package, and a version number that allows multiple versions of the same package to be present. A package can be a collection of Tcl scripts, or a binary library, or a combination of both. Binary libraries are not discussed in this tutorial.
Using packages The command provides the ability to use a package, compare package versions, and to register your own packages with an interpreter. A package is loaded by using the command and providing the package name and optionally a version number. The first time a script requires a package Tcl builds up a database of available packages and versions. It does this by searching for package index files in all of the directories listed in the tcl_pkgPath and auto_path global variables, as well as any subdirectories of those directories. Each package provides a file called pkgIndex.tcl that tells Tcl the names and versions of any packages in that directory, and how to load them if they are needed. It is good style to start every script you create with a set of statements to load any packages required. This serves two purposes: making sure that any missing m issing requirements are identified as soon as possible; and, clearly documenting the dependencies that your code has. Tcl and Tk are both made available as packages and it is a good idea to explicitly require them in your scripts even if they are already loaded as this makes your scripts more portable and documents the version requirements of your script.
Creating a package There are three steps involved in creating a package:
Adding a statement to your script. Creating a pkgIndex.tcl file. Installing the package where it can be found by Tcl.
The first step is to add a statement to your script. It is good style to place this statement at the top of your script. The command tells Tcl the name of your package and the version being provided. The next step is to create a pkgIndex.tcl file. This file tells Tcl how to load your package. In essence the index file is simply a Tcl file which is loaded into the interpreter when Tcl searches for packages. It should use the command register a script which will load the package when it is required. The pkgIndex.tcl file is evaluated globally in the interpreter when Tcl first searches for any package. For this reason it is very bad style for an index script to do anything other than tell Tcl how to load a package; index scripts should not define procs, require packages, or perform any other action which may affect the state of the interpreter. The simplest way to create a pkgIndex.tcl script is to use the
command. The
command scans files which match a given pattern in a directory looking for commands. From this information it generates an appropriate pkgIndex.tcl file in the directory. Once a package index has been created, the next step is to move the package to somewhere that Tcl can find it. The tcl_pkgPath and auto_path global variables contain a list of directories that Tcl searches for packages. The package p ackage index and all the files that implement the package should be installed into a subdirectory of one of these directories. Alternatively, the auto_path variable can be extended at run-time to tell Tcl of new places to look for packages.
name ?version? Loads the package identified by name . If the
switch is given along with a version number then only that exact package version will be accepted. If a version number is given, without the switch then any version equal to or greater than that version (but with the same major version number) will be accepted. If no version is specied then any version will be loaded. If a matching package can be found then it is loaded and the command returns the actual version number; otherwise it generates an error.
name ?version? If a version is given this command tells Tcl that this version of the package indicated by name is loaded. If a different version of the same package has already been loaded then an error is generated. If the version argument is omitted, then the command returns the version number that is currently loaded, or the empty string if the package has not been loaded.
pkgPat
dir ?p att attern ern p att attern ern . . . ?
Creates a pkgIndex.tcl file for a package or set of packages. The command works by loading the files matching the p attern s in the directory, dir and seeing what new packages and commands appear. The command is able to handle both Tcl script files and binary libraries (not discussed here).
Namespaces One problem that can occur when using packages, and particularly when using code written by others is that of name collision. This happens when two pieces of code try to define a procedure or variable with the same name. In Tcl when this occurs the old procedure or variable is simply overwritten. This is sometimes a useful feature, but more often it is the cause of bugs if the two definitions are not compatible. To solve this problem, Tcl provides a command to allow commands and variables to be partitioned into separate areas, called namespaces. Each namespace can contain commands and variables which are local to that namespace and cannot be overwritten by commands or variables in other namespaces. When a command in a namespace is invoked it can see all the other commands and variables in its namespace, as well as those in the global namespace. Namespaces can also contain other namespaces. This allows a hierachy of namespaces to be created in a similar way to a file system hierachy, or the Tk widget hierachy. Each namespace itself has a name which is visible in its parent namespace. Items in a namespace can be accessed by creating a path to the item. This is done by joining the names of the items with ::. For instance, to access the variable bar in the namespace foo, you could use the path foo::bar . This kind of path is called a relative path because Tcl will try to follow the path relative to the current namespace. If that fails, and the path represents a command, then Tcl will also look relative to the global namespace. You can make a path fullyqualified by describing its exact position in the hierachy from the global namespace, which is named ::. For instance, if our foo namespace was a child of the global namespace, then the fully-qualified name of bar would be ::foo::bar. It is usually a good idea to use fully-qualified names when referring to any item outside of the current namespace to avoid surprises. A namespace can export some or all of the command names it contains. These commands can then be imported into another namespace. This in effect creates a local command in the new namespace which when invoked calls the original command in the original namespace. This is a
useful technique for creating short-cuts to frequently fr equently used commands from other namespaces. In general, a namespace should be careful about exporting commands with the same name as any built-in Tcl command or with a commonly used name. Some of the most important commands to use when dealing de aling with namespaces are:
path path scrip script t This command evaluates the script in the namespace specified by path . If the namespace doesn't exist then it is created. The namespace becomes the current namespace while the script is executing, and any unqualified names will be resolved relative to that namespace. Returns the result of the last command in script .
?nam espace espace nam espace espace .. . ? Deletes each namespace specified, along with all variables, commands and child namespaces it contains. Returns the fully qualified path of the current namespace.
?p att attern ern p att attern ern . . . ? Adds any commands matching one of the patterns to the list of commands exported by the current namespace. If the switch is given then the export list is cleared before adding any new commands. If no arguments are given, returns the currently exported command names. Each pattern is a glob-style pattern such as *, [a-z]* , or *foo*.
?patt attern ern patt attern ern . . . ? Imports all commands matching any of the patterns into the current namespace. Each pattern is a glob-style pattern such as foo::* , or foo::bar.
Using namespace w ith packages William Duquette has an excellent guide to using namespaces and packages at http://www.wjduquette.com/tcl/namespaces.html.. In general, a package should provide a http://www.wjduquette.com/tcl/namespaces.html namespace as a child of the global namespace and put all of its commands and variables inside that namespace. A package shouldn't put commands or variables into the global namespace by default. It is also good style to name your package and the namespace it provides the same to avoid confusion.
Example This example creates a package which provides a stack data structure. # Reg ister ister the packag e package provide tutstack 1.0 packag e requ requ ire ire Tcl 8.5 # Create the the n am espace nam espace eval ::t : :tutst utstack { # Export Export com m ands nam espace export export push pop p eek emp ty # Set Se t u p st s tate variab variab le st s tack variabl e id 0 } # Cr C reate a new ne w stack stack proc ::tut : :tutst stack: ack::c :crreate {} { variab variab le st s tack variabl e id set token "stack[ "stack[in in cr id ]" set stack($t stack($token oken ) [l ist] return $token oke n
} # Destr D estroy oy a stack proc :: tutstack: :destr :d estroy {token {token } { variab variable le st s tack un set stack($t stack($token oken ) } # Push an el em ent onto onto a stack stack proc ::tutst ::tutstack::pu ack:: pu sh {token elem } { variab variable le st s tack lappen lap pen d stack($t stack($token ) $elem $elem } # Ch eck if stack stack is em pty pty proc :: tutstack::em ack:: em pty pty {token} { variab variable le st s tack set nu m [ll eng th $stack($t $stack($token )] return eturn [expr {$num {$n um == 0}] } # Rem ove an elem ent fr from the top o f the stack stack proc ::t :: tutstack::pop ack:: pop {token {token}} { variab variable le st s tack if {[em pty pty $t $token ]} { error error "stack ac k em pty" } set ret [lin [l in dex de x $st $stack($t ack($token oken ) en d] set stack($token ack($token ) [lr [l range an ge $stack($t $stack($token oken ) 0 en d-1] d-1] retu retu rn $ret } # See what is on o n top of the stack stack witho without ut rem oving it proc ::t :: tutstack::peek ack:: peek {token {token}} { variab variable le st s tack if {[em pty pty $t $token ]} { error error "stack ac k em pty" } return eturn [lin dex $stack($t $stack($token ) en d] }
nd some code which uses it: packag pac kage e req requi uirre tutst tutstack 1. 0 set stack [t [ tu tstack::c stack: :crreate] foreach oreach nu m {1 2 3 4 5} { tutstack::pu ack:: pu sh $stack $stack $num } while { ! [tut [tutst stack:: ack::em em pty pty $stack] } { pu ts "[tutst "[tutstack: :p op $stack]" } tu tstack::d stack: :d estroy estroy $stack
Previous lesson
|
Index
|
Next lesson
Creating Comm ands - eval Previous lesson Index Next lesson | | One difference between Tcl and most other compilers is that Tcl will allow an executing program to create new commands and execute them while running.
A tcl command is defined as a list of strings in which the first string is a command or proc. Any string or list which meets this criteria can be evaluated and executed. The eval command will evaluate a list of strings as though they were commands typed at the % prompt or sourced from a file. The eval command normally returns the final value of the commands being evaluated. If the commands being evaluated throw an error (for example, if there is a syntax error in one of the strings), then eval will will throw an error. Note that either concat or list may be used to create the command string, but that these two commands will create slightly different command strings. eval arg1 ??arg2?? ... ??argn?? Evaluates arg1 - argn as one or more Tcl commands. The args are concatenated into a string, and passed to tcl_Eval to evaluate and execute. Eval returns the result (or error code) of that evaluation.
Example set cm d {p uts "Evaluat "Evaluatin in g a p uts"} uts"} puts puts "CMD IS: $cmd " eval $cm $cm d
if {[str {[string in g m atch atch [in fo p rocs n ewPr ewProcA] ""] ""] } { puts " nD efinin efinin g n ewPr ewProcA for this in vocation" ocation" set num 0; set cm d "proc newPr n ewProcA ocA " set cmd [concat $cmd "{} { n"] set cmd [concat $cmd "global "global num; n"] n"] set cmd [concat $cmd "incr num ; n"] set set cmd [conca [concatt $cmd " return urn "/tmp/TMP mp/TMP.[p .[pid] id].. $num "; n"] n"] set cm d [ concat $cm $cm d "}"] eval eval $cm d } puts puts " nThe body of newP newProcA is: n[info n[info body newPr newProcA] ocA] n" pu ts "newPr "n ewProcA ocA returns: urns : [ n ewProcA]" ewProcA]" pu ts "newPr "n ewProcA ocA returns: urns : [ n ewProcA]" ewProcA]" # # Defind a p roc usin g lists # if {[str {[string in g m atch atch [in fo pr p rocs n ewPr ewProcB] ""] ""] } { puts " nD efinin efinin g newProcB for for this in vocat vocation" ion" set cm d "proc newP ne wPrrocB " lappend cmd {} lappend cm d {global num ; incr num ; ret retur urn n $num ;} eval eval $cm d } puts " nThe nThe body of newP newProcB ocB is: n[info n[info body newP newProcB] ocB] n" pu ts "newProcB "newProcB returns returns:: [n ewProcB] ewProcB] "
Previous lesson
|
Index
|
Next lesson
More command construction - format, list Previous lesson Index Next lesson | | There may be some unexpected results when you try to compose command strings for eval .
For instance eval puts OK
would print the string OK. However, eval puts Not OK
will generate an error. The reason that the second command generates an error is that the eval uses concat to merge its arguments into a command string. This causes the two words Not OK to be treated as two arguments to puts . If there is more than one argument to puts , the first argument must be a file pointer. Correct ways to write the second command include these: eval [list puts {Not OK}] eval [list puts "Not OK"] set cmd "puts" ; lappend cmd {Not OK}; eval $cmd
As long as you keep track of how the arguments you present to eval will be grouped, you can use many methods of creating the strings for eval , including the string commands and format . The recommended methods of constructing commands for eval is to use the list and lappend commands. These commands become difficult to use, however if you need to put braces in the command, as was done in the previous lesson. The example from the previous lesson is re-implemented in the example code using lappend. The completeness of a command can be checked with info complete . Info complete can also be used in an interactive program to determine if the line being typed in is a complete command, or the user just entered a newline to format the command better. info complete string If string has no unmatched brackets, braces or parentheses, then a value of 1 is returned,
else 0 is returned.
Example set cm d "OK" eval puts $cmd $cm d set cm d "puts" "puts" ; lappen d cm d {Also OK}; eval $ $cm cm d
set cm d "NOT OK" eval puts puts $cmd $cm d eval [form [form at {%s {% s "%s"} pu ts "Even This Thi s Works"] set cm d "And "And even thi thi s can be m ade to to work" work" eval [for [form m at {%s "%s"} pu ts $cm d ] set tm pFileN um 0;
set cm d {proc tem pFileN am e } lappend cm d "" "" lappend lappend cmd cmd "glob "global al num ; incr incr num; ret return urn "/tmp/TMP mp/TMP.[pid] .[pid].. $num eval eval $cm d
""
puts puts " nThis is the the body of the proc defin defin ition: ition: " puts puts "[inf "[info o body tempFileName] n"
set cm d {puts {p uts "Thi "Thi s is Cool!} if {[in fo com plete plete $cm $cm d]} { eval eval $cm d } else { puts "IN "IN COMPLET COMPLETE E COMM AND : $cm d" }
Previous lesson
|
Index
|
Next lesson
Substitutio n w ithout evaluation - format, subst Previous lesson Index Next lesson | | The Tcl interpreter does only one substitution pass during command evaluation. Some situations, such as placing the name of a variable in a variable, require two passes through the substitution phase. In this case, the subst command is useful. Subst performs a substitution pass without performing any execution of commands except those required for the substitution to occur, ie: commands within [ ] will be executed, and the
results placed in the return string. In the example code: puts "[subst $$c]n" The format command can also be used to force some levels of substitution to occur. subst ?-nobackslashes? ?-nocommands? ?-novariables? ?-novariables? string Passes string through the Tcl substitution phase, and returns the original string with the
backslash sequences, commands and variables replaced by their equivalents. If any of the -no... arguments are present, then that set of substitutions will not be done. NOTE: subst does not honor braces or quotes.
Example set a "alph a" set b a put pu ts put pu ts put pu ts puts puts
{a an d b with n o su bstit bstitution: ution: $a $$b} $$b} "a and b with with on e pass of substit substitution: ution: $a $$b" $$b" "a and b with with sub s ubst st in braces: [su bst {$a $$b}]" $$b}]" "a "a and b with with subst in q uotes: uotes: [sub st "$a $$b"] $$b"] n"
pu ts "form "format at with with n o su bst [form [format at {$%s} $b]" pu ts "form "format at with with sub s ubst st:: [sub [s ubst st [form [form at {$%s} $b]]" $b]] " eval eval "puts "puts "ev "eval aft after form orm at: at: [for [form m at {$%s} $b] $b] ""
set num 0; set cm d "proc "proc tem tem pFileN am e {} " set cm d [for [form m at "%s {global nu m ; incr nu m ;" $cm $cm d] set cm d [form [form at {%s return "/tm p/TMP.% s.$num s.$n um "} $cm $cm d [pi d] ] set cm d [f [ form orm at "%s }" $cm $cm d ] eval $cm $cm d puts "[in "[in fo bod y tem pFileN am e]" set a arr arraynam e set b in dex set c n ewvalu ewvalue e eval [format [form at "set %s(%s) %s (%s) % s" $a $b $b $c] pu ts "Inde "In dex: x: $b of $a was was set to: $arr $arraynam ayn am e(in dex)"
Previous lesson
|
Index
|
Next lesson
Changing W orking Directory - cd, pw d Previous lesson Index Next lesson | | Tcl also supports commands to change and display the current working directory.
These are: cd ?dirName?
Changes the current directory to dirName (if dirName is given, or to the $HOME directory if dirName is not given. If dirname is a tilde ( ~ , changes the working directory to the users home directory. If dirName starts with a tilde, then the rest of the characters are treated as a login id, and changes the working directory to that user's $HOME. pw d
Returns the current directory.
Example set dir di rs [li st TEMPDIR] TEMPDI R] pu ts "[form "[form at "%-15s "%-15s % -20s " "FILE" "FILE" "DIRECTORY"]" "DIRE CTORY"]" foreach dir di r $dirs { catch catch {cd $dir} $dir} set c_files [glob [g lob -nocom no com plain pl ain c*] c*] foreach foreach n am e $c_files il es { pu ts "[for "[form m at "%-15s "%-15s %-20s " $nam e [pwd]]" [pwd ]]" } }
Previous lesson
|
Index
|
Next lesson
Debugging & Errors - errorI e rrorI nfo errorCode errorC ode catch error return | Index | Next lesson In previous lessons we discussed how the return command could be used to return a value from a proc. In Tcl, a proc may return a value, but it always returns a status. Previous lesson
When a command executes correctly, the return status is TCL_OK. When an error occurs within a Tcl command, it returns TCL_ERROR instead of TCL_OK. When this occurs, the Tcl command that had the error places an informational string in the global variable errorInfo and returns a status of TCL_ERROR to the calling command. As the Tcl call stack unwinds, each Tcl command appends an informational message to the global variable errorInfo, and returns TCL_ERROR to the command above it. This actually occurs when any exception condition occurs, including break and continue . Break and continue normally occur within a loop of some sort, and the loop command catches the exception and processes it properly. Interpreted Tcl code can also catch exceptions. If a Tcl command is the argument to the catch command, any exception that the command generates is captured and returned. At this point the calling proc can decide how to handle the event. For example, if an open call returns an error, the user could be prompted to pr ovide another file name. A Tcl proc can also generate an error status condition. This can be done by specifying an error return with an option to the return command, or by using the error command. In either case, a message will be placed in errorInfo , and the proc will return a TLC_ERROR status. error message ?info? ?code?
Generates an error condition and forces the Tcl call stack to unwind, with error information being added at each step. If info or code are provided, the errorInfo and errorCode variables are initialized with these values. catch script ?varName?
Evaluates and executes script. The return value of catch is the status return of the Tcl interpreter after it executes script If there are no errors in script, this value is TCL_OK. Otherwise it is an error value. If varName is supplied, the value returned by script is placed in varName. return ?-code code? ?-errorinfo info? ?-errorcode errorcode? ?value?
Generates a return exception condition. The possible arguments are: -code
the next value specifies the return status. Code must be one of: ok ........ Normal status return error ..... Proc returns error status return .... Normal return break ..... Proc returns break status continue .. Proc returns continue status -errorinfo info will be the first string in the errorInfo variable. -errorcode The proc will set errorCode to errorcode.
value
The string value will be the value returned by this proc. errorInfo errorInfo is a global variable that contains the error e rror information from commands that
have failed. errorCode errorCode is a global variable that contains the error code from command that failed.
Example proc err e rrorproc {x} { x} { if {$x > 0} { error "Error g en erated b y error" error" "Info "In fo Strin g for error" error" $x } } catch errorproc errorproc pu ts "after bad proc cal l: ErrorCode: orCode : $errorCode $errorCode"" puts puts "ERROR ERRORINFO INFO:: n$er n$errorInf orInfo o n" set errorIn errorIn fo ""; catch {errorpr {errorproc oc 0} pu ts "after proc call c all with with n o er e rror: ErrorCode ErrorCode:: $err $errorCode" orCod e" puts puts "ERROR ERRORINFO INFO:: n$er n$errorInf orInfo o n" catch {errorpr {errorproc oc 2} pu ts "after error gen erated in proc: ErrorCode: orCode : $err $errorCode" orCode " puts puts "ERROR ERRORINFO INFO:: n$er n$errorInf orInfo o n" proc retu rn Err { x } { retu rn -co -code de error error -errorin errorin fo "Retu "Retu rn G en erates erates This" Thi s" -err -errorcod orcod e "-999" "-999" } catch {retu {retu rn Err 2} pu ts "after proc th th at uses us es return return to gen ge n erate erate an error: error: ErrorCo ErrorCode de:: $errorCod $errorCod e" puts puts "ERROR ERRORINFO INFO:: n$er n$errorInf orInfo o n" proc withError withError {x} { set se t x $a } catch { withError withError 2} pu ts "after proc with an error: error: ErrorCod ErrorCod e: $errorCod e" puts puts "ERROR ERRORINFO INFO:: n$er n$errorInf orInfo o n" catch {o pen pe n "/n o_such su ch _d irectory/n irectory/n o_such su ch _file" file " "r"} "r"} pu ts "after an er e rror call to a n on existent fil file:" e:" pu ts "Err "ErrorCode: orCode : $err $errorCode" orCod e" puts puts "ERROR ERRORINFO INFO:: n$er n$errorInf orInfo o n"
Previous lesson
|
Index
|
Next lesson
Mo re Debugging - trace Previous lesson Index Next lesson | | When you are debugging a program, it's sometimes useful to know when a variable gets changed. The Tcl interpreter supports a method for tracking when and how a variable is accessed. With the trace command, a procedure can be defined to be executed whenever a variable is read, written, or unset. With this command you can determine what proc a variable is modified within, what the value was changed to, and what other variables were at the time.
The trace command executes at the same stack level as the access to the variable. The proc that trace invokes is one stack level lower. Thus, with the uplevel command, a procedure called via a trace can report on the conditions that were set when a variable was accessed. trace variable variableName operation procname Places a trace on the variable variableName. Whenever variableName is accessed for the operation specified in the operation argument, the procedure procname will be called. Operation is one of:
r ...... Read w .... Write u ...... Unset A variable can be unset either explicitly with the unset command, or implicitly when a procedure returns, and all of the local variables are released. When variableName is accessed, procName will be called with three arguments, variableName, elementName and operation. If the variable being accessed is an associative array, then elementName will contain the name of the element being accessed. If variableName is a simple variable, then elementName will be an empty string. Operation is the operation that was done on variableName to invoke the trace action. trace vdelete variableName operation procname Cancels the trace action for operation on variableName. trace vinfo variableName Returns information about the traces applied to variableName
Example proc tr traceproc {variabl {variableN eNam am e arrayElem arrayElem ent operation } { set op(w) "Writ "Write"; e"; set se t op(u) op (u) "Un "U n set"; set"; set s et op(r op (r)) "Read" set level [in fo level] in cr level -1; if {$level > 0} { set procid [i nfo level $level] } else {set procid "mai n"} if {![strin g m atch atch $arrayElem ayEle m ent en t ""]} ""]} { put pu ts "TRACE: "TRACE: $op($operat $op($operation ion ) $variableN iabl eNam am e($arrayElem ent en t) in $procid $procid"" } else { put pu ts "TRACE: "TRACE: $op($operation ) $var $variabl iableN eNam am e in $procid $procid"" } } proc testP testPrroc {in {i n pu t1 inpu in pu t2} { up var $input1 $inp ut1 i up var $input2 $inp ut2 j set i 2 set se t k $j;
} trace variable variab le i 1 w tracepr racep roc trace variable variab le i2 r tracep traceproc roc trace variable variab le i 2 w tracepr racep roc set i2 "testvalue" estvalu e" puts puts " ncall test testProc" test es tProc Proc i1 i2 puts puts " nTraces nTraces on i1: [tr [trace vin vin fo i1]" puts puts "T "Traces on i2: [tr [trace vinfo vinfo i2] n" trace vdel vd elete ete i2 r tracep traceproc roc pu ts "Traces on i2 after after vde vdelete: lete: [trace [trace vinfo i2]" i2 ]" puts puts " ncall test testProc again" test es tProc Proc i1 i2
Previous lesson
|
Index
|
Next lesson
Command line arguments and environment strings | Index | Next lesson Scripts are much more useful if they can be called with different values in the command line. Previous lesson
For instance, a script that extracts a particular value from a file could be written so that it prompts for a file name, reads the file name, and then extracts the data. Or, it could be written to loop through as many files as are in the command line, and extract the data from each file, and print the file name and data. The second method of writing the program can easily be used from other scripts. This makes it more useful. 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 , and the rest of the command line arguments are passed as a list in argv . Another method of passing information to a script is with environment variables . For instance, suppose you are writing a program in which a user provides some sort of comment to go into a record. It would be friendly to allow the user to edit their comments in their favorite editor. If the user has defined an EDITOR environment variable, then you can invoke that editor for them to use. Under Posix compliant operating systems, environment variables are passed to a Tcl script in a global associative array en v . The index into en v is the name of the environment variable. The command would print the contents of the PATH environment variable.
Example pu ts "There are are $argc argum ent en ts to th is script" s cript" pu ts "Th "Th e nam n am e of th is scr sc ript ip t is $argv0" if {$argc {$argc > 0} {pu { pu ts "The other argu argu m en ts are: $argv" $argv" } put pu ts "You h ave these he se en viron m ent en t variables iabl es set:" foreach oreach in dex [arr [array nam es en v] { pu ts "$in "$in dex: $env( $en v($ind $index)" ex)" }
Previous lesson
|
Index
|
Next lesson
Leftovers - time, unset Previous lesson Index Next lesson | | The simplest method of making a script run faster is to buy a faster processor. Unfortunately, this isn't always an option. You may need to optimize your script to run faster. This is difficult if you can't measure the time it takes to run the portion of the script that you are trying to optimize.
The time command is the solution to this problem. Time will measure the length of time that it takes to execute a script. You can then modify the script, rerun time and see how much you improved it. You may also need to optimize the memory used by a script, or perhaps clean up variables after a each pass through a loop. The unset command will delete a variable from the interpreters namespace. After you've run the example, play with the size of the loop counters in timetst1 and timetst2 . If you make the inner loop counter 5 or less, it may take longer to execute timetst2 than it takes for timetst1 . This is because it takes time to calculate and assign the variable k , and if the inner loop is too small, then the gain in not doing the multiply inside the loop is lost in the time it takes to do the outside the loop calculation. time script ?count?
Returns the number of milliseconds it took to execute script. If count is specified, it will run the script count times, and average the result. The time is elapsed time, not CPU time. unset variableName1 ?variableName2 ...? Deletes variableName from the interpreter's namespace. If variableName is an array name, then the entire array is deleted. If variableName is an element in an array, then only that element is deleted. If any of the variables to delete do not exist, then unset will
return an error, and any variables after the non-existent variable will not be deleted.
Example
proc tim etst etst1 1 {lst {l st}} { set x [lsearch $lst "5000"] "5000"] retu retu rn $x } proc tim etst2 {array} { u pvar $array $array a return $a(5000); } # Make a l on g li st and a large arr array. for {set i 0} {$i { $i < 5001} {in { in cr i} { set se t array($i) $i lapp end list $i }
pu ts "Tim "Tim e for list li st search: [ tim tim e {tim {tim etst etst1 1 $list} 10]" pu ts "Tim e for arr array in dex: [ tim e {tim etst etst2 2 array} 10]"
proc existen existence ce {variabl {variable} e} { u pvar $variab $variable le testVar; testVar;
if {[in {[ in fo exists testV testVar]} ar]} { pu ts "$variable "$variable Exists" } else { pu ts "$var "$variab iab le D oes N ot Exist" } }
set se t x 1 set se t y 2 for {set i 0} {$i < 5} {in cr i} { set a($i) $i; } puts puts " ntest ntesting unsett unsetting a sim ple variable" ;# Con firm firm th at x exis existts. existence enc e x ;# U nset x put pu ts "x has b een u nset ns et"" un set x ;# Con firm irm that x n o lon lo n ger exist exists. s. existence enc e x
puts puts " ntest ntesting ing un sett setting a m em ber of an arr array" existence a(0); put pu ts "a0 "a0 has been un set" set" un set a(0); existence a(0); put pu ts " n testin estin g un sett settin g several several m em bers of an array, with with an err error" or" existence a(3); existence a(4); catch catch {un set a(3) a(3) a(0) a(4)} put pu ts " nAft nAfter att attem ptin ptin g to to del ete ete a(3), a(0) a(0) and a(4)" existence a(3); existence a(4); puts puts " ntest ntesting ing un sett setting an arr array" existence enc e a; puts puts "a has b een u nset" nset" un set a; existence enc e a;
Previous lesson
|
Index
|
Next lesson
Channel I / O: socket, fileevent, vw ait Previous lesson Index Next lesson | | Tcl I/O is based on a channel. A channel is conceptually similar to the FILE * in C, or a stream in shell programming. The difference is that a channel may be a either a stream device like a file, tape drive, or tty, or a connection oriented construct like a socket.
A stream based channel is created with the open command, as discussed in lesson 26. A socket based channel is created with a socket command. A socket can be opened either as a TCP client, or as a server. If a channel is opened as a server, then the tcl program will 'listen' on that channel for another task to attempt to connect with it. When this happens, a new channel is created for that link (server-> new client), and the tcl program continues to listen for connections on the original port number. In this way, a single Tcl server could be talking to several clients simultaneously. When a channel exists, a handler can be defined that will be invoked when the channel is available for reading or writing. This handler is defined with the fileevent command. When a tcl procedure does a gets or puts to a blocking device, and the device isn't ready for I/O, the program will block until the device is ready. This may be a long while if the other end of the I/O channel has gone off line. Using the fileevent command, the program only accesses an I/O channel when it is ready to move data. Finally, there is a command to wait w ait until an event happens. The vwait command will wait until a variable is set. This can be used to create a semaphore style functionality for the interaction between client and server, and let a controlling procedure know that an event has occurred. Look at the example, and you'll see the socket command being used as both client and server, and the fileevent and vwait commands being used to control the I/O between the client and server. Note in particular the flush commands being used. Just as a channel that is opened as a pipe to a command doesn't send data until either a flush is invoked, or a buffer is filled, the socket based channels don't automaticly send data. socket -server command ?options? port The socket command with the -server flag opens up a server connecton on port. When a connection occurs at port, the proc command is called with the arguments:
channel . . . .The channel for the new client address . . . . The IP Address of this client port. . . . . . . . The port that is assigned to this client
socket ?options? host port The socket command without the -server option opens a client connection to the system with IP Address host and port address port. The IP Address may be given as a numeric
string, or as a fully qualified domain address. To connect to the local host, use the address 127.0.0.1 (the loopback address). fileevent channelId readable ?script? fileevent channelId writeable ?script? The fileevent command defines a handler to be invoked when a condition occurs. The conditions are readable , which invokes script when a data is ready to be read on channelId, and writeable , when channelID is ready to receive data. vwait varName The vwait command pauses the execution of a script until some background action sets the value of varName. A background action can be a proc invoked by a fileevent, or a
socket connection, or an event from a tk widget.
Example proc server serverOp en { chann cha nn el ad dr port port} { global con nected nected set conn con n ected ected 1 fileevent ileeven t $chan nel readable eadab le "readLine "readLine Server Server $chan nel" ne l" put pu ts "OPENE "OPENE D" } proc proc readL readLine ine {who chan nel} { global didRead if {[gets {[gets $chann el li ne]<0} ne]<0} { fileevent ile event $chann $chan n el readab readab le {} aft after idle "close $chan nel;s ne l;set et out 1" } else { pu ts "READ "READ LIN LIN E: $lin e" pu ts $chan n el "Thi "This s is a return" flush $channel; set did Read 1 } } set con nect nec ted 0; # cat c atch ch {socket {soc ket -server server serverOpen serverOpen 33000} server server set server server [socket [soc ket -server serverO serverO pen pe n 33000] 33000 ] after 100 update; set sock [socket [s ocket -async 127.0.0.1 127.0. 0.1 33000] vwait vwait con nect ne cted ed pu ts $sock $soc k "A Test Test Lin Lin e" flush lu sh $sock vwait vwait did Read set len [gets $sock $sock li ne] pu ts "Return "Return lin l in e: $len -- $lin e" catch catch {close { close $sock} vwait vwait out ou t close clo se $server $server
Previous lesson
|
Index
|
Next lesson
Time and Date - clock Previous lesson Index Next lesson | | The clock command provides access to the time and date functions in Tcl. Depending on the subcommands invoked, it can acquire the current time, or convert between different representations of time and date.
The clock command is a platform independent method of getting the display functionality of the unix date command, and provides access to the values returned by a unix gettime() call. clock seconds The clock seconds command returns the time in seconds since the epoch. The date of
the epoch varies for different operating systems, thus this value is useful for comparison purposes, or as an input to the clock format command. clock format clockValue ?-gmt boolean? ?-format string? The format subcommand formats a clockvalue (as returned by clock clicks into a
human readable string. The -gmt switch takes a boolean as the second argument. If the boolean is 1 or True , then the time will be formatted as Greenwich Mean Time, otherwise, it will be formatted as local time. The -format option controls what format the return will be in. The contents of the string argument to format has similar contents as the format statement (as discussed in lesson 19, 33 and 34). In addition, there are several more % * descriptors that can be used to describe the output. These include: % a . . . . Abbreviated weekday name (Mon, Tue, etc.) % A . . . . Full weekday name (Monday, Tuesday, etc.) % b . . . . Abbreviated month name (Jan, Feb, etc.) % B . . . . Full month name (January, February, etc.) % d . . . . . Day of month % j . . . . . Julian day of year % m . . . . Month number (01-12) % y . . . . . Year in century % Y . . . . Year with 4 digits
% H . . . . Hour (00-23) % I . . . . . Hour (00-12) % M . . . . Minutes (00-59) % S . . . . . Seconds(00-59) % p . . . . . PM or AM
% D . . . . Date as %m/%d/%y % r . . . . . Time as %I:%M:%S %p % R . . . . Time as %I:%M % T . . . . Time as %I:%M:%S % Z . . . . Time Zone Name clock scan dateString The scan subcommand converts a human readable string to a system clock value, as would be returned by clock seconds
The dateString argument contains strings in these forms: time A time of day in one of the formats shown below. Meridian may be AM, or PM, or a
capitalization capitalization variant. If it is not sepcified, then the hour (hh) is interpreted as a 24 hour clock. Zone may be a three letter description of a time zone, EST, PDT, etc. hh:mm:ss ?meridian? ?zone? hh:mm:ss hhmm ?meridian? ?zone? date A date in one of the formats shown below. mm/dd/yy mm/dd monthname dd, yy monthname dd dd monthname yy dd monthname day, dd monthname yy
Example set syst system em Time [clock secon ds] put pu ts "The "The tim e is: [clock [clo ck for form m at $syst $system em Tim e -form orm at %H :%M :% S]" pu ts "The "The d ate ate is: [clo [ clock ck form format at $system Tim e -format orm at %D ]" put pu ts [clock [ clock form orm at $syst $system em Time Tim e -f -form orm at {Today is: %A, the the %d of %B, %Y}] % Y}] puts " n the the default form orm at for the tim tim e is: [clock for form m at $syst $system em Tim e] n"
set halBi ha lBi rth Book Boo k "Jan "Jan 12, 1 2, 1997" set h alBirt alB irth Movie "Jan 12, 1992" set bookSecon ds [clock [clo ck scan $halBirthBook] hB ook] set m ovieSeconds ovieSecon ds [clock [cloc k scan $hal $hal Birt BirthMovie] hM ovie] put pu ts "The "The b ook an d m ovie ver version sion s of '2001, A Space Space O dd ysey' ysey' had a" put pu ts "disc "discrrepen cy of [expr $bookSecon ds - $movieSecon $mo vieSeconds] ds] secon ds in how" put pu ts "soon we woul d h ave sentient com put pu ters ers like lik e the the H AL 9000"
Previous lesson
|
Index
|
Next lesson
More channel chann el I/ O - fblocked fblocked & fconfigure fconf igure Previous lesson Index Next lesson | | The previous lessons have shown how to use channels with files and blocking sockets. Tcl also supports non-blocking reads and writes, and allows you to configure the sizes of the I/O buffers, and how lines are terminated.
A non-blocking read or write means that instead of a gets call waiting until data is available, it will return immediately. If there was w as data available, it will be read, and if no data is available, the gets call will return a 0 length. If you have several channels that must be checked for input, you can use the fileevent command to trigger reads on the channels, and then use the fblocked command to determine when all the data is read. The fblocked and fconfigure commands provide more control over the behavior of a channel. The fblocked command checks whether a channel has returned all available input. It is useful when you are working with a channel that has been set to non-blocking mode and you need to determine if there should be data available, or if the channel has been closed from the other end. The fconfigure command has many options that allow allow you to query or fine tune the behavior of a channel including whether the channel is blocking or non-blocking, the buffer size, the end of line character, etc. fconfigure channel ?param1? ?value1? ?param2? ?value2? Configures the behavior of a channel. If no param values are provided, a list of the valid
configuration paramaters and their values is returned. If a single parameter is given on the command line, the value of that parameter is returned. If one or more pairs of param/value pairs are provided, those parameters are set to the requested value. Parameters that can be set include: -blocking . . . Determines whether or not the task will block when data cannot be moved on a channel. (i.e. If no data is available on a read, or the buffer is full on a write). -buffersize . . . The number of bytes that will be buffered before data is sent, or can be buffered before being read when data is received. The value must be an integer between 10 and 1000000. -translation . . . Sets how Tcl will terminate a line when it is output. By default, the lines are teminated with the newline, carriage return, or newline/carriage return that is appropriate to the system on which the interpreter is r unning. This can be configured to be: r eturn, or newline/carriage return as an auto . . . Translates newline, carriage return, end of line marker. Outputs the correct line termination for the current platform. binary . . Treats newlines as end of line markers. Does not add any line termination to lines being output. cr . . . . Treats carriage returns as the end of line marker (and translates them to newline internally). Output lines are terminated with a carriage return. This is the Macintosh standard. crlf . . . Treats cr/lf pairs as the end of line marker, and terminates output
lines with a carriage return/linefeed combination. This is the Windows standard. lf . . . . Treats linefeeds as the end of line marker, and terminates output lines with a linefeed. This is the Unix standard.
The example is similar to the lesson 40 example with a client and server socket in the same script. It shows a server channel being configured to be non-blocking, and using the default buffering style - data is not made avaialble to the script until a newline is present, or the buffer has filled. When the first write: is done, the fileevent triggers the read, but the gets can't read characters because there is no newline. The returns a -1, and returns a 1. When a bare newline is sent, the data in the input buffer will become available, and the returns , and returns 0.
Example proc server serverOp en { chann cha nn el ad dr port port} { pu ts "chan nel : $chan nel - from Address: Address: $addr Port Port: $port" pu ts "The "The default state ate for for blockin g is: [fconfigure $ch $chann ann el -bloc -blockin king] g]"" pu ts "The d efaul efaultt buff buffer size is: [fconfigure $chan n el -buffer buffersize size ]" ;# Set this ch ann el to to be n on-blockin on-blockin g. fconfigure configure $chann el -blockin -blockin g 0 set bl [fconfigu re $ch $chann ann el -blockin blo ckin g] pu ts "After fcon figur figu re the state for blocki bl ockin n g i s: $bl" ;# Chan ge the the buffer size to to be sm aller fcon figu re $chan $ch annel nel -buf bu ffersize ersize 12 12 pu ts "Af "After Fconf Fcon figu re buf bu ffer size is: [fconfigure $ch $chann ann el -bu -bufffersize ersize ] n" ;# When inp ut is available, read read it. it. fileeven t $chan nel ne l readable "readLine "readLine Server Server $chan n el" } proc proc readL readLine ine {who chan nel} { global didRead global b locked locked pu ts "The "Therre is inp i np ut for $who $who on $chan nel" ne l" set len [gets [gets $chan $chan nel l in e] set blocked [fblocked $chann el] pu ts "Ch "Char aract acter ers s Read: $len $len Fblocked: Fbloc ked: $blocked" $blocked " if {$len < 0} { if {$blocked} {$blocked} {pu ts "Inp ut is blocked" } else { pu ts "The "The socket so cket was closed - closi ng m y end" close $chann $chann el; } } else { pu ts "Read "Read $len ch aract aracter ers: s: $line" $line " put pu ts $chan n el "Thi "Thi s is a return" return" flush $chann el; } in cr didRead; } set server server [socket [soc ket -server serverO serverO pen pe n 33000] 33000 ] aft after 120 update; up date;
# This kicks MS-Win MS-Windo dows ws m achin ach in es for this hi s app lication
set sock [socket [so cket 127.0. 0.1 33000] set bl [fconfigure $sock $sock -blo -blockin ckin g] set bu [fcon figur figu re $sock -bu -buff ffersize] put pu ts "Origi "Origi nal sett settin g for sock: Sock blockin blo ckin g: $bl bu ffersize: ersize: $bu" $bu "
fcon figu re $sock -blockin bloc king g No fcon figure ig ure $sock $soc k -bu -buff ffersize 8; set bl [fconfigure $sock -blockin bloc kin g] set bu [fconfigu [fcon figurre $sock -bu -buff ffersize] puts puts "Modified "Modified sett setting for sock: Sock blocking : $bl bu ffersize: ersize: $bu n" # Sen d a line li ne to the the server server -- N OTE flush flus h set did Read 0 pu ts -non no n ewlin e $sock "A Test Line" Line " flush lu sh $sock; # Loop un til two two reads reads h ave been don do n e. while {$didRead {$di dRead < 2} { ;# Wait for didRead to be set vwait didRead if {$blocked } {puts {p uts $sock "Ne "Newlin wline" e" ; flu flush sh $sock; p uts "SEN "SEND D N EWLINE"} } set len [gets $sock $sock line li ne]] put pu ts "Return "Return l in e: $len -- $line" close $sock vwait didRead catch {clo {c lose se $server} $server}
Previous lesson
|
Index
|
Next lesson
Child i nterpreters Previous lesson Index | For most applications, a single interpreter and subroutines are quite sufficient. However, if you are building a client-server system (for example) you may need to have several interpreters talking to different clients, and maintaining their state. You can do this with state variables, naming conventions, or swapping state to and from disk, but that gets messy.
The interp command creates new child interpreters within an existing interpreter. The child interpreters can have their own sets of variables and files, or they can be given access to items in the parent interpreter. If the child is created with the -safe option, it will not be able to access the file system, or otherwise damage your system. This feature allows a script to evaluate code from an unknown (and untrusted) site. The names of child interpreters are a hierarchical list. If interpreter fo o is a child of interpreter ba r , then it can be accessed from the toplevel interpreter as . The primary interpreter (what you get when you type tclsh) is the empty list {}. The interp command has several subcommands and options. A critical subset is: interp create ?-safe? ?name?
Creates a new interpreter and returns the name. If the -safe option is used, the new interpreter will be unable to access certain dangerous system facilities. interp delete name Deletes the named child interpreter. interp eval args This is similar to the regular eval command, except that it evaluates the script in the child interpreter instead of the primary interpreter. The interp eval command concatenates the args into a string, and ships that line to the child interpreter to evaluate. interp alias srcPath srcCmd targetPath targetCmd ?arg arg? The interp alias command allows a script to share procedures between child interpreters or between a child and the primary interpreter. Note that slave interpreters have a separate state and namespace, but do no t have separate event loops. These are not threads, and they will not execute independently. If one slave interpreter gets stopped by a blocking I/O request, for instance, no other interpreters will be prcessed until it has unblocked. The example below shows two child interpreters being created under the primary inter preter {}. Each of these interpreters is given a variable which contains the name of the interpreter. Note that the alias command causes the procedure to be evaluated in the interpreter in which the procedure was defined, not the interpreter in which it was evaluated. If you need a procedure to exist within an interpreter, you must a command within that interpreter. If you want an interpreter to be able to call back to the primary interpreter (or other interpreter) you can use the command.
Example set i1 [inter [i nterp p create first firstCh Ch ild] il d] set i2 [inter [i nterp create secon dCh ild ]
pu ts "f "first chil ch ild d in terp: $i1" puts puts "sec "second ond child inter interp: p: $i2 n" # Set a var variabl iabl e "n "n am e" in each ch ild in terp, erp, and an d # creat create e a procedure wit withi hi n each in terp erp # to return return th at valu e foreach in i n t [list [l ist $i1 $i1 $i2] { in terp erp eval $int [list set nam e $in t] interp eval $int {proc {proc n am eis {} {g lobal n am e; ret return urn "nam eis: $nam e";} e";} } } foreach in i n t [list [l ist $i1 $i1 $i2] { inter interp eval eval $int "puts puts "EVA EVAL IN $int $int:: name is $name "" pu ts "Return fr from 'nam eis' is: [i nterp eval $int nam eis]" } # # A shor sho rt program to return the valu e of "n "n am e" # proc rtn Nam e {} { global global nam e return eturn "rtnN am e is: $nam $n am e" } # # Alias that procedure proced ure to a proc in $i1 int in terp erp alias ali as $i1 rt rtnN am e {} rt rtnN am e pu ts "" # This is an error error.. The alias cau ses the the evaluat evalu ation ion # to hap pen in the {} int in terpret erpreter, er, where where nam e is # not defined . pu ts "first "firstCh ild il d reports [in terp eval $i1 rt rtn N am e]"
Previous lesson
|
Index