When defining a procedure in tcl like the one below, How can I call the proc defining just a and c? Is there any way to do this?
proc test1 { a {b 2} {c 3} } {
puts "$a $b $c"
}
Here's one technique, a bit messier that what you're hoping for but not too messy:
proc test1 { args } {
# set the default values
array set values {b 2 c 3}
# todo: validate that $args is a list with an even number of items
# now merge in the args
array set values $args
# and do stuff with the values ...
parray values
}
test1 a 10 c 14
You sometimes see applications use this technique where the array keys have a leading dash, to look like options:
proc test1 args {
array set values {-b 2 -c 3}
array set values $args
parray values
}
test1 -a 10 -c 14
Thanks Glenn and Peter, I joined your posts and I got
proc test1 { a args } {
array set valores [list a $a -b 2 -c 3]
array set valores $args
puts "$valores(a) $valores(-b) $valores(-c)"
}
which solved what I wanted.
So now I can call
> proc 12 -c 8
> 12 2 8
Related
I have the following procedure:
proc test {a {b 10} {c 30}} {
puts "$a $b $c"
}
I would like to call the test procedure by passing value to argument a and c and keep the default value of the argument b. In other words, I want to pass arguments value by name.
Is it possible to do it in TCL?
No.
The usual method to do this is:
proc test { args } {
# set up the defaults
set a(-a) {}
set a(-b) 10
set a(-c) 30
set len [llength $args]
# some basic argument checking
if { $len > 6 || $len < 2 || $len % 2 != 0 } {
error "Invalid arguments"
}
array set a $args
...
}
set result [test -a 3 -c 40]
I à stuck with a TCL issue. I would like to have access to the content of a second level variable with keeping the format (list).
please see my code :
At first, I declare variable contents
set x1y {1 2 3 4}
set x2y {10 11 12 13}
After I perform a for loop
for { i 0} {i < 4} { incr i}
I would like to have in xy variable the content of x1y with keeping the list format
set xy [eval ["x${i}y"]]
foreach x $xy {
....
}
Do you have any idea / proposal. I tried subst but it doesn’t keep the format.
Thank you in advance
The way to read from a variable whose name is not a constant is to use the single argument form of set:
set x1y {1 2 3 4}
set x2y {10 11 12 13}
foreach i {1 2} {
foreach val [set x${i}y] {
puts "$i --> $val"
}
}
However, it is usually easier to make an alias to the variable with upvar 0, like this:
foreach i {1 2} {
upvar 0 x${i}y xy
foreach val $xy {
puts "$i --> $val"
}
}
And in almost every case where you're doing this, you should consider using arrays instead (remembering that Tcl's arrays are associative arrays; you can use compound keys as well as simple integers):
set xy(1) {1 2 3 4}
set xy(2) {10 11 12 13}
foreach i {1 2} {
foreach val $xy($i) {
puts "$i --> $val"
}
}
You probably want to try to avoid using eval or subst for this sort of thing; those commands have side effects that may hurt the stability of your code if you're not careful. Definitely not ones for cases like these. (Also, they'll be slower as they force Tcl to recompile its internal bytecode more frequently. All the solutions I present above don't have that misfeature.)
set x1y {1 2 3 4}
set x2y {10 11 12 13}
for {set i 1} {$i <= 2} {incr i} {
foreach e [set x${i}y] {
puts $e
}
}
I am new to TCL and was trying to write a TCL procedure which take dynamic value.
Like I want to pass n number of interface and vlan pair to the proc.
proc proc_data {device, intf_in, intf_out, args} {
foreach vlan $args {
set inter_vlan [$device exec "show interface $intf_in $vlan"]
set inter_vlan [$device exec "show interface $intf_out $vlan"]
....
}
}
Is there any way I can pass :
{ device [interface vlan] <<<<< dynamic no of pair
It depends on how you want to map the arguments, but the key commands are likely to be foreach and lassign.
The foreach command can consume several values each time through the loop. Here's a simple example:
proc foreachmultidemo {args} {
foreach {a b} $args {
puts "a=$a, b=$b, a+b=[expr {$a+$b}]"
}
}
foreachmultidemo 1 2 3 4 5 6
You can also iterate over two lists at once (and yes, this mixes with the multi-variable form if you want):
proc foreachdoubledemo {list1 list2} {
foreach a $list1 b $list2 {
puts "a=$a, b=$b, a+b=[expr {$a+$b}]"
}
}
foreachdoubledemo {1 2 3} {4 5 6}
The lassign command can take a list and split it into variables. Here's a simple example:
proc lassigndemo {mylist} {
foreach pair $mylist {
lassign $pair a b
puts "a=$a, b=$b, a+b=[expr {$a+$b}]"
}
}
lassigndemo {{1 2} {3 4} {5 6}}
I'm not quite sure how to make these do what you're after, but it is bound to be one or the other, possibly in a mix.
Thanks #Donal Fellows
Posting the code I was looking for:
proc data_proc {device intr vlan} {
puts "Logged in device is: $device"
foreach a $intr {
set interface [$device "show interface $a"
foreach b $vlan {
set inter_vlan [$device "show interface $a vlan $b"
}
}
}
data_proc device {interface1 interface2 ...} {vlan1 vlan2 ...}
the answer you have posted is not very efficient. The logic you have used will check all the interfaces one by one and check all the vlans for each interface.
What if you need to check a particular vlan set instead of all the vlans for a few interfaces?
I am trying to write a tcl procedure which does the following -
proc myProc {arg1 def1} {arg2 def2} {
...
...
}
tcl> myProc -arg1 val1 -arg2 val2
arg1 variable has val1
arg2 variable has val2
tcl> myProc -arg1 val1
arg1 variable has val1
arg2 variable has def2
tcl> myProc -?
myProc -arg1 <value1> -arg2 <value2>
arg1 - required argument [default value is 10]
arg2 - optional argument [default value is 20]
help - print this message
? - print this message
Is this possible in tcl?
I looked up some of the questions that have been asked and what I see is this question. This does partially what I require but I couldn't find anything that would help me solve my problem. Please guide me!
Read the proc man page carefully: the list of arguments has to be a single list. You were thinking about this:
% proc myproc {{a 42} {b 54}} { puts "a=$a b=$b" }
% myproc
a=42 b=54
% myproc 1
a=1 b=54
% myproc 1 2
a=1 b=2
Note that the first argument is assigned to a -- you cannot provide a value for b and use the default value for a with this method.
To use command-line-like options, the simplest way is this:
% proc myproc {args} {
array set data [list -a 42 -b 54 {*}$args]
puts "a=$data(-a) b=$data(-b)"
}
% myproc
a=42 b=54
% myproc -a 1
a=1 b=54
% myproc -b 2
a=42 b=2
% myproc -b 2 -a 3
a=3 b=2
% myproc -c 4
a=42 b=54
One problem with this method is that you must pass an even number of arguments, or array set will throw an error:
% myproc 12
list must have an even number of elements
You can use Tcllib module cmdline. On wiki you can find example how to use this module with proc.
proc printdata args {
array set param [::cmdline::getoptions args {\
{page.arg 1 "current page"}
{pages.arg 1 "number of pages"}
} "printdata ?options? Data"]
if {1 != [llength $args]} {
return -code error "No data given"
}
set param(data) [lindex $args 0]
# processing here
parray param
}
% printdata -pages 2 -- "--Head data to print--"
param(data) = --Head data to print--
param(page) = 1
param(pages) = 2
% printdata -?
printdata ?options? Data
-page value current page <1>
-pages value number of pages <1>
-help Print this message
-? Print this message
I wrote a utility function for this years ago. The arguments have the same syntax as proc but it generates procs that work the way you describe. The code is simple:
proc optproc {name args script} {
proc $name args [
string map [list ARGS $args SCRIPT $script] {
foreach var {ARGS} {
set [lindex $var 0] [lindex $var 1]
}
foreach {var val} $args {
set [string trim $var -] $val
}
SCRIPT
}
]
}
What the code does above is basically call proc with two foreach loops injected to the function body to process the arguments.
With this you can declare your function like this:
optproc myProc {{arg1 def1} {arg2 def2}} {
# you can use arg1 and arg2 just like a regular proc:
puts $arg1
puts $arg2
}
Which you can then use the way you wanted:
myProc ;# default values for arg1 and arg2
myProc -arg1 foo ;# default values for arg2
myProc -arg2 bar ;# default values for arg1
myProc -arg1 foo -arg2 bar
For more info and discussion on this you can read the wiki page: http://wiki.tcl.tk/20066
For even more flexible proc argument processing you can use a while loop: http://wiki.tcl.tk/16032
Thank you all for your valuable inputs. I have written the following code which helps me achieve what I want -
Code
proc argsParser args {
set args [string map { - "" } $args]
if {[expr [llength $args] % 2] != 0 } {
puts "Wrong Arguments :: $args "
return "For Help :: argsParser -?";
}
switch $args {
h -
help -
? { puts "Usage :: argParser -arg1 val1 -arg2 val2"
puts "arg1 :: Number"
puts "arg2 :: Number"
}
default {
set arg1 10 ;
set arg2 20 ;
set args [string map { - "" } $args]
for {set i 0} {$i < [llength $args]} { incr i 2} {
set a [lindex $args $i]
if { $a != "arg1" && $a != "arg2" } {
return "Unknown Args :: $a - For Help :: argsParser -?";
}
set b [lindex $args [expr $i+1]]
set $a $b
}
puts "arg1 - $arg1"
puts "arg2 - $arg2"
}
}
}
Result
tcl> argsParser -arg1 20 -arg4 40
Unknown Args :: arg4 - For Help :: argsParser -?
tcl> argsParser -arg1 20 -arg2 40
arg1 - 20
arg2 - 40
Could you help me how to use arguments with optionals I used this function but it does not work
proc {my_proc} {n1 n2 -args{{u2 "5"} {u1 "5"}} } {
puts "n1:'$n1', n2:'$n2', u1:'$u1', u2:'$u2'"
}
->my_proc 1 -args 5 7
n1:'1', n2:'$n2', u1:'7', u2:'5'
I would like to call function like
my_proc 1 -args {u2 5} {u1 7}
my_proc 1 {u2 5} {u1 7} (required + optional arguments)
my_proc 1 (only required arguments)
You are strongly recommended to use only one of these patterns in a particular command:
Optional arguments, where the optional-ness is by position.
Optional key-value pairs.
Combining the two is relatively hard to get right, and is always rather confusing!
Optional by position
proc my_proc {n1 n2 {u2 "5"} {u1 "5"}} {
puts "n1:'$n1', n2:'$n2', u1:'$u1', u2:'$u2'"
}
my_proc 7 8 9
#### n1:'7', n2:'8', u1:'5', u2:'9'
Optionality by key-value pair
proc my_proc {n1 n2 args} {
# Add the defaults
set args [dict merge {-u1 5 -u2 5} $args]
# Magic! (Keys start with “-” by arbitrary convention.)
# Copies from the value for key “-u1” to $u1 (and similarly “-u2”/$u2)
# The empty value is an update script; empty here as we don't want to update
dict update args -u1 u1 -u2 u2 {}
# Use...
puts "n1:'$n1', n2:'$n2', u1:'$u1', u2:'$u2'"
}
my_proc 7 8 -u1 123 -u2 456
#### n1:'7', n2:'8', u1:'123', u2:'456'
There's a few other ways to do this, e.g., with dict set options $args;puts $options(-u1). These are particularly useful in Tcl 8.4 (and before, for the truly behind-the-times):
proc my_proc {n1 n2 args} {
# Defaults
array set opt {-u1 5 -u2 5}
# Parse
array set opt $args
# Use
puts "n1:'$n1', n2:'$n2', u1:'$opt(-u1)', u2:'$opt(-u2)'"
}
my_proc 7 8 -u1 123 -u2 456
#### n1:'7', n2:'8', u1:'123', u2:'456'
As Donal suggested, I like to use args and an array to handle options. It allows an easy way to set default values:
proc p {args} {
array set options {-u1 defU1 -u2 defU2} ;# the default values
array set options $args ;# merge the user's values
parray options
}
p -foo bar -u1 42
options(-foo) = bar
options(-u1) = 42
options(-u2) = defU2
You will need to check that $args contains an even number of elements:
% p 1 2 3
list must have an even number of elements