Unable to puts in a tcl script - tcl

I am trying to create a trace function in tcl .Function will list down all the called procs/nested calls and there arguments. Below is the script
rename proc _proc
proc proc {nm params body} {
_proc $nm $params $body
trace add execution $nm enter [list track_entry $nm $params]
trace add execution $nm leave [list track_leave $nm]
}
_proc track_entry {nm params real args} {
puts "Enter proc $nm"
foreach formal $params actual [lrange $real 1 end] {
append p " [lindex $formal 0]=$actual,"
}
puts "Parameters:$p and body"
}
_proc track_leave {nm args} {
puts "Exit proc $nm"
}
proc test1 { param1 param2 } {
puts “parameters are $param1 and $param2”
test2 value1
}
proc test2 { value} {
puts “value is $value”
}
I am getting below output
test1 arg1 arg2
Enter proc test1
Parameters: param1=arg1, param2=arg2, and body
Exit proc test1
wrong # args: should be "puts ?-nonewline? ?channelId? string"
Any clue why it is giving error in puts

Provided that what you posted is correct, the problem is you're not using the correct quoting character.
Tcl only understands two kinds of quoting:
quoting with substitutions: ""
quoting without substitutions: {}
The character “ in tcl will just be treated as any other character such as a or 5.
Note that without quoting, tcl treats the word as a string without spaces. For example, the following examples are all valid strings:
this_is_a_valid_string
"this_is_a_valid_string"
{this_is_a_valid_string}
Following this simple rule, the following are also valid strings and are all equivalent:
“hello
"“hello"
{“hello}
So when you ask tcl to execute the following:
puts “parameters are $param1 and $param2”
it treats it as:
puts {“parameters} {are} "$param1" {and} "$param2”"
passing 5 arguments to puts.
Obviously this would trigger an error since puts expects either one or two arguments.

Related

What is the proper way to create hyphen parameters in TCL that are Boolean or have values? [duplicate]

I am writing a proc to create a header in an output file.
Currently it needs to take an optional parameter, which is a possible comment for the header.
I have ended up coding this as a single optional parameter
proc dump_header { test description {comment = ""}}
but would like to know how I can achieve the same using args
proc dump_header { test description args }
It's quite easy to check for args being a single blank parameter ($args == ""), but doesn't cope well if passing multiple parameters - and I need the negative check anyway.
Your proc definition is incorrect (you'd get the error message too many fields in argument specifier "comment = """). Should be:
proc dump_header { test description {comment ""}} {
puts $comment
}
If you want to use args, you could examine the llength of it:
proc dump_header {test desc args} {
switch -exact [llength $args] {
0 {puts "no comment"}
1 {puts "the comment is: $args"}
default {
puts "the comment is: [lindex $args 0]"
puts "the other args are: [lrange $args 1 end]"
}
}
}
You might also want to pass name-value pairs in a list:
proc dump_header {test desc options} {
# following will error if $options is an odd-length list
array set opts $options
if {[info exists opts(comment)]} {
puts "the comment is: $opts(comment)"
}
puts "here are all the options given:"
parray opts
}
dump_header "test" "description" {comment "a comment" arg1 foo arg2 bar}
Some prefer a combination of args and name-value pairs (a la Tk)
proc dump_header {test desc args} {
# following will error if $args is an odd-length list
array set opts $args
if {[info exists opts(-comment)]} {
puts "the comment is: $opts(-comment)"
}
parray opts
}
dump_header "test" "description" -comment "a comment" -arg1 foo -arg2 bar
I use tcllib's cmdline library to do option parsing.
This is the example from cmdline documentation:
set options {
{a "set the atime only"}
{m "set the mtime only"}
{c "do not create non-existent files"}
{r.arg "" "use time from ref_file"}
{t.arg -1 "use specified time"}
}
set usage ": MyCommandName \[options] filename ...\noptions:"
array set params [::cmdline::getoptions argv $options $usage]
if { $params(a) } { set set_atime "true" }
set has_t [expr {$params(t) != -1}]
set has_r [expr {[string length $params(r)] > 0}]
if {$has_t && $has_r} {
return -code error "Cannot specify both -r and -t"
} elseif {$has_t} {
...
}
So, in your case, you'd just use args in place of argv in the above example.
It should be mentioned explicitly that args is a special word in Tcl that, when used at the end of the argument list, contains a list of all the remaining arguments. If no args are given, then no error is produced (unlike any other variable name, which would be considered a required argument).
I was looking for a way to have functionality similar to python's kwargs (optional key-value pair arguments), and something that works nicely is (similar to Glenn's last example):
proc my_proc {positional_required1 {positional_optional1 "a_string"} args} {
# Two optional arguments can be given: "opt1" and "opt2"
if {![string equal $args ""]} {
# If one or more args is given, parse them or assign defaults.
array set opts $args
if {[info exists opts(opt1)]} { set opt1 $opts(opt1) } else { set opt1 0 }
if {[info exists opts(op2)]} { set opt2 $opts(opt2) } else { set opt2 -1 }
} else {
# If no args are given, assign default values.
set op1 0
set op2 -1
}
# DO STUFF HERE
}
And can be called like:
my_proc "positional_required1_argument"
# OR
my_proc "positional_required1_argument" "a_string"
# OR
my_proc "positional_required1_argument" "a_string" opt1 7
# OR
my_proc "positional_required1_argument" "a_string" opt1 7 opt2 50
# etc.
A potential downside (as I've currently implemented it) is that if a user passes a non-approved key-value option, there is no error.

pass square brace to uplevel TCL

I have a code in which I am passing a list to be evaluated, by TCl uplevel #0.
While it works well if I give it a code which uses curly braces in order to wrap the square braces, for example:
uplevel #0 { puts [ info vars CCK_* ] }
I cannot get to accept when I use a list, i.e.:
uplevel #0 [list puts "\[" info vars CCK_* "\]" ]
I get:
wrong # args: should be "puts ?-nonewline? ?channelId? string"
while executing
"puts {[} info var CCK_* \]"
("uplevel" body line 1)
invoked from within
"uplevel #0 [ list puts "\[" info var CCK_* "\]" ]"
I need the list command , because some of the rest of the code requires evaluation of variable names, that must happen before uplevel takes order ( i.e., input to uplevel). For example:
if { [ getpoint $elem ] == $pointy }
when [ getpoint $elem ] is to be evaluated in the uplevel, but pointy actually is defined and set in the calling proc , hence I cannot use curly braces for it, there will be evaluation before uplevel is called, and it would get just a number.Thanks,
You can only use the list command to build a single substitution-free command. It quotes everything for you specifically to do just that, and puts [info vars CCK_*] is a compound command. There are a few ways around this, but you should think carefully about what you're really doing:
I've quoted the #0 for reasons of highlighting only.
Only uplevel What Needs It
puts [uplevel "#0" [list info vars CCK_*]]
Or in this case:
puts [uplevel "#0" {info vars CCK_*}]
Wrap The Compound In eval
You can send arbitrary stuff like this, but I'm not sure why you'd do it:
uplevel "#0" [list eval { puts [ info vars CCK_* ] }]
Send A Lambda Term
uplevel "#0" [list apply {{} {
set vars [uplevel 1 {info vars CCK_*}]
puts $vars
}}]
It's not so useful here, but when you're wanting to send in arbitrary additional value from the current scope it becomes superb:
set value "this is a {complex string with \[some bits\] that might} make \$tcl choke"
uplevel "#0" [list apply {{value} {
set vars [uplevel 1 {info vars CCK_*}]
puts $value
puts $vars
puts $value
}} $value]
Assembling a script (or command sequence) to be submitted to uplevel etc. is not necessarily best achieved using list. This is the case for a script with nested evaluations, for instance.
Your question wording is not fully clear to me (so I might have interpreted it incorrectly), but you might want to consider using [subst] or [string map] for your purposes?
Watch:
set CCK_1 ""
proc foo {someVarName} {
uplevel "#0" [subst -nocommands {
if {"$someVarName" in [info vars CCK_*]} {
puts "Found $someVarName"
}
}]
}
foo CCK_1; # prints "Found CCK_1"
foo CCK_2
List are better suited for command sequences without excessive evaluation nesting; for complete scripts, better use script templates based on [subst] or [string map]. A word of caution: [subst] and [string map] don't protect the substitution values and position them in the script in their literal form.
Update
This is not to say that your original snippet could not be made to work:
set CCK_1 ""
# a) non-robust variant
proc bar {pattern} {
uplevel "#0" puts "\[info vars $pattern\]"
# equiv of
uplevel "#0" [concat puts "\[info vars $pattern\]"]
# versus
uplevel "#0" [list puts "\[info vars $pattern\]"]
}
bar CCK_*
set "CCK _1" ""
# b) robust variant
proc bar-robust {pattern} {
uplevel "#0" puts "\[[list info vars $pattern]\]"
# equiv of
uplevel "#0" [concat puts "\[[list info vars $pattern]\]"]
}
bar-robust "CCK _*"
uplevel assembles the script to be evaluated by [concat]ing its arguments. Like providing a single [concat]'ed the argument. You would not use list here to assemble the entire script, but rather to protect the script components under assembly (see bar-robust). Protection here means that complex values are maintained in their original meaning during script assembly (e.g., a match pattern incl. whitespace: CCK _*).
My recommendation would be to run the code in two steps. First, run the code in the square brackets, then use the result of that in the second call. Of course, since you're only doing a puts, there's no need to run that via uplevel:
set result [uplevel #0 [list info vars CCK_*]
puts $result
If using puts was for illustrative purposes, then I think the general idea of calling uplevel twice is still valid: run the code in square brackets as a distinct step, then combine it with your other code to get the final result.

execute tcl commands line by line

I have a file like this:
set position {0.50 0.50}
set visibility false
set text {ID: {entity.id}\n Value: {entity.contour_val}}
And I want to do something similar to source, but I want to use a file handle only.
My current attempt looks like this:
proc readArray {fileHandle arrayName} {
upvar $arrayName arr
set cl 0
while {! [eof $fileHandle]} {
set cl [expr "$cl + 1"]
set line [gets $fileHandle]
if [$line eq {}] continue
puts $line
namespace eval ::__esg_priv "
uplevel 1 {*}$line
"
info vars ::__esg_priv::*
foreach varPath [info vars ::__esg_priv::*] {
set varName [string map { ::__esg_priv:: "" } $varPath]
puts "Setting arr($varName) -> [set $varPath]"
set arr($varName) [set $varPath]
}
namespace delete __esg_priv
}
puts "$cl number of lines read"
}
In place of uplevel I tried many combinations of eval and quoting.
My problem is, it either fails on the lines with lists or it does not actuall set the variables.
What is the right way to do it, if the executed commands are expected to be any valid code.
An extra question would be how to properly apply error checking, which I haven't tried yet.
After a call to
readArray [open "myFile.tcl" r] arr
I expect that
parray arr
issues something like:
arr(position) = 0.50 0.50
arr(text) = ID: {entity.id}\n Value: {entity.contour_val}
arr(visibility) = false
BTW: The last line contains internal {}, which are supposed to make it into the string variables. And there is no intent to make this a dict.
This code works, but there are still some problems with it:
proc readArray {fileHandle arrayName} {
upvar $arrayName arr
set cl 0
while {! [eof $fileHandle]} {
incr cl ;# !
set line [gets $fileHandle]
if {$line eq {}} continue ;# !
puts $line
namespace eval ::__esg_priv $line ;# !
foreach varPath [info vars ::__esg_priv::*] {
set varName [string map { ::__esg_priv:: "" } $varPath]
puts "Setting arr($varName) -> [set $varPath]"
set arr($varName) [set $varPath]
}
namespace delete __esg_priv
}
puts "$cl number of lines read"
}
I've taken out a couple of lines that didn't seem necessary, and changed some lines a bit.
You don't need set cl [expr "$cl + 1"]: incr cl will do.
if [$line eq {}] continue will fail because the [...] is a command substitution. if {$line eq {}} continue (braces instead of brackets) does what you intend.
Unless you are accessing variables in another scope, you won't need uplevel. namespace eval ::__esg_priv $line will evaluate one line in the designated namespace.
I didn't change the following, but maybe you should:
set varName [string map { ::__esg_priv:: "" } $varPath] works as intended, but set varName [namespace tail $varPath] is cleaner.
Be aware that if there exists a global variable with the same name as one of the variables in your file, no namespace variable will be created; the global variable will be updated instead.
If you intend to use the value in the text variable as a dictionary, you need to remove either the \n or the braces.
According to your question title, you want to evaluate the file line by line. If that requirement can be lifted, your code could be simplified by reading the whole script in one operation and then evaluating it with a single namespace eval.
ETA
This solution is a lot more robust in that it reads the script in a sandbox (always a good idea when writing code that will execute arbitrary external code) and redefines (within that sandbox) the set command to create members in your array instead of regular variables.
proc readArray {fileHandle arrayName} {
upvar 1 $arrayName arr
set int [interp create -safe]
$int alias set apply {{name value} {
uplevel 1 [list set arr($name) $value]
}}
$int eval [read $fileHandle]
interp delete $int
}
To make it even more safe against unexpected interaction with global variables etc, look at the interp package in the Tcllib. It lets you create an interpreter that is completely empty.
Documentation: apply, continue, eof, foreach, gets, if, incr, info, interp package, interp, list, namespace, proc, puts, set, string, uplevel, upvar, while

Writing procedures in TCL

I am very new for TCL. Just I want to know that how to write TCL procedures without argument and how to call and how to execute it.
To write a procedure that doesn't take any arguments, do this:
proc someName {} {
# The {} above means a list of zero formal arguments
puts "Hello from inside someName"
}
To call that procedure, just write its name:
someName
If it was returning a value:
proc example2 {} {
return "some arbitrary value"
}
Then you'd do something with that returned value by enclosing the call in square brackets and using that where you want the value used:
set someVariable [example2]
To execute it... depends what you mean. I assume you mean doing so from outside a Tcl program. That's done by making the whole script (e.g., theScript.tcl) define the procedure and do the call, like this:
proc example3 {} {
return "The quick brown fox"
}
puts [example3]
That would then be run something like this:
tclsh8.5 theScript.tcl
You can define a procedure like this:
proc hello_world_proc {} {
puts "Hello world"
}
And you can execute it by simply writing:
hello_world_proc
If you want to use a return value of the procedure, you can do:
# Procedure declaration
proc hello_world_proc2 {} {
return "Hello world"
}
# Procedure call
puts [hello_world_proc2]
proc myProc {} {
# do something
}
# call proc
myProc
Te official Tcl website has some documentation on functions (procedures) that could help you at https://www.tcl.tk/man/tcl/TclCmd/proc.htm.
Procedure with no argument
If you don't need any argument here is how to write the procedure you want:
proc funcNameNoArgs {} {
puts "Hello from funcNameNoArgs"
}
And you can call it as follows:
funcNameNoArgs
Procedure with arguments
Now let's say you need arguments in the future. Here is the way to write that precedure in TCL:
proc funcNameWithArgs {arg1 arg2 arg3} {
puts "Hello from funcNameWithArgs "
}
You can call that function by doing:
funcName arg1 arg2 arg3
Here is a piece of code for you to try!
Remember to define functions before you call them, or you will get an error.
Try to copy paste this code in your interpreter to get started and play with it:
proc funcNameNoArgs {} {
puts "Hello from a function with no arguments"
}
funcNameNoArgs
proc funcNameWithArgs {arg1 arg2 arg3} {
puts "Hello from a function with 3 arguments"
puts $arg1
puts $arg2
puts $arg3
}
funcNameWithArgs "Argument 1" "Argument 2" "Argument 3"
Syntax of procedure
proc <Name Of procedure> {No of arguments, if u want don't need simply left empty} {
<Body>
}
Let See the Example:
Without Arguments:
proc Hello_eg { } { puts "Hello I M In procedure" }
How to run:
step 1: write tclsh on prompt
step 2: write the procedure as per above mention
step 3: write just the procedure name (i.e Hello_eg) to run the procedure
2.With Arguments:
proc Hello_Arg { first second }
{
puts "The first argument is: $first"
puts "The Second argument is: $second"
}
How to run this:
step 1: write tclsh on prompt
step 2: write the procedure as per above mention
step 3: write just the procedure name with arguments (i.e Hello_Arg Ramakant Singla) to run the procedure
It's pretty simple.
Defining :
proc myproc {} {
}
calling :
myproc
Since you are New, I advise you to go through tutorial point. They have simple and consolidated content.
Procedure is a set of statements which is being preapeated in a program.
Syntax
proc <Name> {INPUTS} {
BODY
}
Eg:
proc add {m n} {
set s 0
set s [expr $m + $n]
return $s
}
#Main Program Starts Here
set x 2
set y 3
set Result [add $x $y]
puts "$Result"
In the above example....in procedure we have provide a name (add) to the set of statements which can be call in the main program.
Any amount of arguments
What maybe would come in handy is using args.
By using args you can pass any amount of arguments to your procedure.
proc withAnyNumberOfArguments {args} {
if {$args eq ""} {
puts "got no arguments"
}
foreach arg $args {
puts "got $arg"
}
}
Optional Arguments
Another tip: Enclosing arguments with { } makes them optional arguments.
proc atLeastOneArgument {a1 {args}} {
puts -nonewline "got a1=$a1"
foreach arg $args {
puts -nonewline " and $arg"
}
puts "."
}
Default Values
If you want to have default values you can specify them as follows:
proc putsTime { {secondsSinceBeginOfEpoch "now"} } {
if {$secondsSinceBeginOfEpoch eq "now"} {
set secondsSinceBeginOfEpoch [clock seconds]
}
return [clock format $secondsSinceBeginOfEpoch]
}
Some Example Calls
1 % withAnyNumberOfArguments
got no arguments
2 % withAnyNumberOfArguments one
got one
3 % withAnyNumberOfArguments ready steady go!
got ready
got steady
got go!
4 % atLeastOneArgument "this is one argument" ;# because its in double quotes
got a1=this is one argument.
5 % atLeastOneArgument 3 2 1 go!
got a1=3 and 2 and 1 and go!.
6 % puts [formatTime]
Fri Dec 18 16:39:43 CET 2015
7 % puts [formatTime 0]
Thu Jan 01 01:00:00 CET 1970
In addition to the answers above, I would recommend using tcltutor.exe (available from http://tcltutor.software.informer.com/3.0b/) to learn TCL.
It'll have a chapter on Subroutines that'll help you define a TCL proc without and with arguments.
Regards
Sharad
To create a TCL procedure without any parameter you should use the proc keyword followed by the procedure name then the scope of your procedure.
proc hello_world {} {
// Use puts to print your output in the terminal.
// If your procedure return data use return keyword.
}
You can use the created procedure by simply calling its name:
hello_world
This solution is based on previous questions about writing procs. I personally feel this is one of the better ways to write a procedure in tcl.
Code
proc sampleProc args {
# Defaults
array set options {-device router0 -ip "10.16.1.62"}
# Read args
array set options $args
# Assign
set device $options(-device)
set ip $options(-ip)
# Usage
puts "Device under use is $device and IP is $ip"
# Return
return "${sd} :: $ip"
}
Execution
tclsh> source sampleProc.tcl
Device under use is router0 and IP is 10.16.1.62
router0 :: 10.16.1.62

How can I safely deal with optional parameters

I am writing a proc to create a header in an output file.
Currently it needs to take an optional parameter, which is a possible comment for the header.
I have ended up coding this as a single optional parameter
proc dump_header { test description {comment = ""}}
but would like to know how I can achieve the same using args
proc dump_header { test description args }
It's quite easy to check for args being a single blank parameter ($args == ""), but doesn't cope well if passing multiple parameters - and I need the negative check anyway.
Your proc definition is incorrect (you'd get the error message too many fields in argument specifier "comment = """). Should be:
proc dump_header { test description {comment ""}} {
puts $comment
}
If you want to use args, you could examine the llength of it:
proc dump_header {test desc args} {
switch -exact [llength $args] {
0 {puts "no comment"}
1 {puts "the comment is: $args"}
default {
puts "the comment is: [lindex $args 0]"
puts "the other args are: [lrange $args 1 end]"
}
}
}
You might also want to pass name-value pairs in a list:
proc dump_header {test desc options} {
# following will error if $options is an odd-length list
array set opts $options
if {[info exists opts(comment)]} {
puts "the comment is: $opts(comment)"
}
puts "here are all the options given:"
parray opts
}
dump_header "test" "description" {comment "a comment" arg1 foo arg2 bar}
Some prefer a combination of args and name-value pairs (a la Tk)
proc dump_header {test desc args} {
# following will error if $args is an odd-length list
array set opts $args
if {[info exists opts(-comment)]} {
puts "the comment is: $opts(-comment)"
}
parray opts
}
dump_header "test" "description" -comment "a comment" -arg1 foo -arg2 bar
I use tcllib's cmdline library to do option parsing.
This is the example from cmdline documentation:
set options {
{a "set the atime only"}
{m "set the mtime only"}
{c "do not create non-existent files"}
{r.arg "" "use time from ref_file"}
{t.arg -1 "use specified time"}
}
set usage ": MyCommandName \[options] filename ...\noptions:"
array set params [::cmdline::getoptions argv $options $usage]
if { $params(a) } { set set_atime "true" }
set has_t [expr {$params(t) != -1}]
set has_r [expr {[string length $params(r)] > 0}]
if {$has_t && $has_r} {
return -code error "Cannot specify both -r and -t"
} elseif {$has_t} {
...
}
So, in your case, you'd just use args in place of argv in the above example.
It should be mentioned explicitly that args is a special word in Tcl that, when used at the end of the argument list, contains a list of all the remaining arguments. If no args are given, then no error is produced (unlike any other variable name, which would be considered a required argument).
I was looking for a way to have functionality similar to python's kwargs (optional key-value pair arguments), and something that works nicely is (similar to Glenn's last example):
proc my_proc {positional_required1 {positional_optional1 "a_string"} args} {
# Two optional arguments can be given: "opt1" and "opt2"
if {![string equal $args ""]} {
# If one or more args is given, parse them or assign defaults.
array set opts $args
if {[info exists opts(opt1)]} { set opt1 $opts(opt1) } else { set opt1 0 }
if {[info exists opts(op2)]} { set opt2 $opts(opt2) } else { set opt2 -1 }
} else {
# If no args are given, assign default values.
set op1 0
set op2 -1
}
# DO STUFF HERE
}
And can be called like:
my_proc "positional_required1_argument"
# OR
my_proc "positional_required1_argument" "a_string"
# OR
my_proc "positional_required1_argument" "a_string" opt1 7
# OR
my_proc "positional_required1_argument" "a_string" opt1 7 opt2 50
# etc.
A potential downside (as I've currently implemented it) is that if a user passes a non-approved key-value option, there is no error.