The $args variable should, by definition, contain all arguments passed to a script function. However if I construct a pipeline inside my function, the $args variable evaluates to null. Anyone knows why?
See this example:
function test { 1..3 | % { echo "args inside pipeline: $args" } ; echo "args outside pipeline: $args" }
This is the output, when passing parameter "hello":
PS> test hello
args inside pipeline:
args inside pipeline:
args inside pipeline:
args outside pipeline: hello
Is there a specific reason for this? I know how to work around this, however I wonder if anonye out there can explain the reason for this.
Pipes use $input. Try this:
function test { 1..3 | % { echo "args inside pipeline: $input" } ; echo "args outside pipeline: $args" }
Related
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.
I ran into some strange behavior writing a .ps1 script. I wrote a function that takes two parameters, but for some reason the second parameter was always null.
Upon closer inspection, it seems like my two parameters are somehow getting collapsed into the first one.
Given the following script, I would have expected, one line of output showing ...
function Foo($first, $second) {
echo $first
}
$x = "..."
$y = "why?"
Foo($x, $y)
But when I run this script, I get
...
why?
Is there some PowerShell syntax I don't know about that I'm accidentally (mis-)using?
Do not use parens around your arguments and don't use commas to separate arguments. Invoke your functions just like you would any other PowerShell command - using space separated arguments e.g.:
foo $x $y
When you put parens around ($x, $y) PowerShell passes that as a single expression/argument, in this case an array containing two items to the first parameter ($x) of your function. You can use Strict-Mode -Version Latest to warn you when you do this e.g.:
114> function foo($x,$y){}
115> foo(3,4)
116> Set-StrictMode -Version latest
117> foo(3,4)
The function or command was called as if it were a method. Parameters should be separated by
spaces. For information about parameters, see the about_Parameters Help topic.
At line:1 char:1
+ foo(3,4)
+ ~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : StrictModeFunctionCallWithParens
Is there some PowerShell syntax...that I'm accidentally (mis-)using?
Yes, you are not calling your function properly. In PowerShell, function calls do not use parenthesis or have their arguments separated by commas.
Instead, you would call Foo like so:
Foo $x $y
See a demonstration below:
PS > function Foo($first, $second) {
>> echo $first
>> }
>>
PS > $x = "..."
PS > $y = "why?"
PS > Foo $x $y
...
PS >
PS > function Foo($first, $second) {
>> echo "$first and $second"
>> }
>>
PS > Foo $x $y
... and why?
PS >
In case you are wondering, your current code has PowerShell interpreting ($x, $y) as a single argument to Foo: a two-item array. Thus, it assigns this array to $first and $null to $second:
PS > function Foo($first, $second) {
>> echo "Type of `$first: $($first.Gettype())"
>> echo "`$second is `$null: $($second -eq $null)"
>> }
>>
PS > $x = "..."
PS > $y = "why?"
PS > Foo($x, $y)
Type of $first: System.Object[]
$second is $null: True
PS >
The "," is used differently in powershell than in other programming languages. Here it is the array constructor.
(1,2,3).GetType() # System.Object[]
(,'element').GetType() # System.Object[]
Because you havent specified a datatype on your parameters the powershell assumes it is dealing with plain old System.Objects (the superclass of all classes). It then takes the array and assigns it to the first input parameter because it is the only one in this line. It can do that because an array is, by extension, also a System.Object.
Also, the old synthax for defining functions is not recommended anymore:
function foo ( $first, $second ) {}
When writing this, the powershell interpreter would internally convert this into an advanced function:
function foo {
PARAM(
[Parameter(Position=1)]
[object]$first,
[Parameter(Position=2)]
[object]$second
)
BEGIN { <# Do stuff here #> }
PROCESS { <# Do stuff here #> }
END { <# Do stuff here #> }
}
foo -first 'first' -second 'second'
foo 'first' 'second'
Causing unneeded overhead.
I hope that clears it up a little :)
Basically the problem is to execute Tcl code defined in one namespace, using calls to functions in that namespace, in an eval inside another namespace.
The following code works fine:
namespace eval ::eggs {
namespace export e1 eeval
proc e1 {} { puts pe1 }
proc eeval body { puts "in cb enter"; eval $body; puts "in cb exit" }
}
namespace import ::eggs::*
namespace eval ::spam {
namespace export s1 scall
proc scb {} { puts pscb }
proc scall {} { puts "in call enter"; eeval {::spam::scb}; puts "in call exit" }
e1
scall
}
namespace import ::spam::*
and prints:
% % % % % % pe1
in call enter
in cb enter
pscb
in cb exit
in call exit
Now I want to replace the ::spam::scb with a plain scb. I would trade in a wrapper for eeval.
My use case:
The namespace eggs is a very basic library for regression testing. The namespace spam is a small library implementing nice to have functions. They shall be tested upon reload. For this scall is called and uses a special test function in eggs called eeval.
The eeval is the unit test. For convenience and "do-not-repeat-yourself" reasons I'd like to not have to use the fully qualified namespace name of any function defined in spam.
Ideally scall would look like this:
proc scall {} { puts "in call enter"; eeval {scb}; puts "in call exit" }
However, during execution the eval in ::eggs::eeval does not know where to find scb. Since eggs is just a test library I cannot import the spam namespace.
Is there any way one could e.g. devise a wrapper for e.g. eeval to make it run in the other namespace?
If you're going to pass the code to eval (possibly appending arguments) then the simplest method to generate the callback script is with namespace code:
eeval [namespace code {scb}]
That generates all the wrapping code to ensure that things work correctly, including handling all sorts of cases you've not dealt with:
% namespace eval boo {
puts [namespace code {foo bar}]
}
::namespace inscope ::boo {foo bar}
However, if you're doing the callback immediately then the most common approach is not the above, but rather to use uplevel 1 to do the callback:
proc eeval body {
puts "in cb enter"
uplevel 1 $body
puts "in cb exit"
}
This has the advantage that the called-back code really is in the stack context of the caller, allowing it to do useful things like access local variables. Since the code is running in the same context that you define it[*], it's fine using that for resolving command names.
[*] That's a big fat lie — Tcl's semantics are rather more complex than that — but everything works like what I've said is true.
namespace current is the DRYer you're probably looking for.
namespace eval ::spam {
proc scall {} {
puts {in call 2 enter}
eeval [namespace current]::scb
puts {in call 2 exit}
}
}
Or pass the current namespace to the eeval proc
proc ::eggs::eeval {body {namespace ""}} {
puts "in cb enter"
if {$namespace == ""} {
eval $body
} else {
namespace eval $namespace $body
}
puts "in cb exit"
}
proc ::spam::scall {} {
puts "in call 3 enter"
eeval scb [namespace current]
puts "in call 3 exit"
}
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
A Purely theoretical question on Tcl.
Following this question I was thinking on what would be the best way to implement anonymous functions in Tcl.
The end result should be allowing a developer to pass a full proc as an argument to anohter proc:
do_something $data {proc {} {input} {
puts $input;
}};
which would be similar to javascript's
do_something(data, function (input) {
alert(input);
});
now, naturally this will not work OOTB. I was thinking on something of this sort:
proc do_something {data anon_function} {
anon_run $anon_function $data
}
proc anon_run {proc args} {
set rand proc_[clock clicks];
set script [lreplace $proc 1 1 $rand];
uplevel 1 $script;
uplevel 1 [concat $rand $args];
uplevel 1 rename $rand {}; //delete the created proc
}
This works. But I was hoping to get suggestions for a better pattern then this, as it's not very elegant and not really using cool Tcl features. Mostly I'd like to get rid of manually calling anon_run.
In Tcl 8.5, you can use the apply command.
proc do_something {data anon_function} {
apply $anon_function $data
}
do_something $data {{input} {
puts $input
}}
Of course, if you're structuring your callbacks as command prefixes (recommended!) then you can do this:
proc lambda {arguments body} {
# We'll do this properly and include the optional namespace
set ns [uplevel 1 namespace current]
return [list ::apply [list $arguments $body $ns]]
}
proc do_something {data command} {
{*}$command $data
}
do_something $data [lambda {input} {
puts $input
}]
If you're using 8.4 or before, you need the code from the Tcler's Wiki as a substitute, but be aware that those solutions are only semantically equivalent (at best); they're not performance-equivalent.