when I run my TCL script is there a way I can add a command or a variable with my run command? - tcl

What I am trying to do is run my TCL script with a variable.
So for example lets say my script was called example.tcl.
I want to be able to run that script doing something like this:
tclsh example.tcl success
Then in my tcl script I will have:
set status = variable <---- this variable should be equal to "success"
puts $status
Is there anyway in TCL I can do something like that?

The arguments to tclsh after the script name are stored as a list in the global argv variable.
set status [lindex $argv 0]
puts "status = $status"
The length of the list is in argc, but nobody really uses that. The script is in argv0, not in argv; that's very convenient sometimes.

Related

Reading cmd arguments in TCL file

I am trying to run a tcl script through .bat file. I want to read some cmd arguments in the tcl script. Below is my code:
Command to run:
D:\Cadence\Sigrity2021.1\tools\bin\PowerSI.exe -tcl abcd.tcl %new_var%.spd %new_file_name%
Below is how I am trying to read the variable in the tcl file:
sigrity::open document [lindex $argv 0] {!}
It open up the Cadence Sigrity, but I see the below error:
How do I read cmd argument in tcl?
If you have no other way to do it that you can find (and it sounds like that might be the case) then you can fake it by writing a helper file with content like this, filling in the real arguments in the appropriate places:
# Name of script to call
set ::argv0 "abcd.tcl"
# Arguments to pass
set ::argv {}
lappend ::argv "%new_var%.spd"
lappend ::argv "%new_file_name%"
# Number of arguments (rarely used)
set ::argc [llength $::argv]
# Do the call
source $::argv0
Then you can pass that file to PowerSI and it will set things up and chain to the real file. It's messy, but practical.
If you're writing this from Tcl, use the list command to do the quoting of the strings (instead of putting them in double quotes) as it will do exactly the right thing for you. If you're writing the file from another language, you'll want to make sure you put backslashes in before \, ", $ and [ characters. The fiddlyness of doing that depends on your language.

invoke a tcl script from another tcl script with multiple arguements

i am invoking script(tclscript) from the current script seeing this "invalid command name error" the tcl script just checks the proper version of package is installed or not.
#!/bin/tclsh
# i am doing this for multiple packages in a loop
set list {/usr/local/script}
lappend list -check
lappend list -package
lappend list tcl-devel
lappend list version
[eval exec $list]
output:
invalid command name "
checking the version [ ok ] #expected output
-checks successful! #expected output
"
while executing
"[eval exec $list]"
dont understand why i get this "invalid command name error"can anyone help
The problem is that you've successfully run the command, have got the results back, and are then trying to use those results as the name of a command because you put [brackets] around the eval exec. Either remove the brackets, or put a command name before them so that you use the result as an argument.
set list …
# Leaving out the details of how you build the list
eval exec $list
set list …
# Leaving out the details of how you build the list
set result [eval exec $list]
puts "result is \"$result\""

TCL command wrapping

I am trying to write a TCL proc that will allow me to wrap statements and then 'execute' them internally.
For example, if I originally have :
set var $tmp
I want to have some procedure :
proc wrapper {command} {
puts "do something here"
puts $command #ie below : write "set var $tmp" to stdout
EXECUTE COMMAND HERE
puts "do something else here"
}
set void [wrapper {set var $tmp}]
My motivation is that I'm trying to write my own timing/profiler for individual statements.
Instead of "EXECUTE COMMAND", use
uplevel 1 $command
Note that if you attempt to time a script using a command like your wrapper, it won't be byte-compiled, which means that it won't perform the way it would inside a procedure.
Documentation: uplevel
You can give a try with time command which is already available with Tcl if your only intention is to just get the time taken by a set of code to execute.
time script ?count?
This command will call the Tcl interpreter count times to evaluate script (or once if count is not specified). It will then return a string of the form
503.2 microseconds per iteration
which indicates the average amount of time required per iteration, in microseconds. Time is measured in elapsed time, not CPU time.
Example :
set code_block {
foreach x "1 2 3" {
set y [expr {$x*20}];
}
}
puts [time $code_block 10]; # Executing the code for 10 times
which might produce random outputs such as
11.9 microseconds per iteration
Update 1 :
If you want to print the commands along with execution which is also possible. You can override the time command with rename command.
rename time _time; # Changing the 'time' to '_time'
# Defining your own 'time' command
proc time {command {count 1}} {
# Printing the commands here
puts $command
# Calling the actual '_time' command
# and returning that value from here
return [_time $command $count]
}
Place the above code at the top of your code and usage of time command after this will be using our customized procedure only.
They are passed as set of command which in turn, yes, a string. But, while the evaluation of the code, it will behave as if like how it will work in a tclsh.
As answered by Mr.Peter, if your code involves accessing the previous level of commands, variables, then you have to use uplevel or upvar based on your needs.
Reference : time, rename

Procedure tracking in log with proc renaiming

I have a custom test tool written in tcl and bash (mainly in tcl, some initial config and check were done by bash). It isn't have an exact starting point, the outside bash (and sometimes the application which is tested) call specific functions which they find with a "tclIndex" file, created by auto_mkindex.
This tool create a log file, with many "puts" function, which is directed to the file location.
Most of the functions have a "trackBegin" function call at the beginning, and one "trackEnd" function at the end of it. These two get the functions name as parameter. This help us to track where is the problem.
Sadly, this tracker was forgotten in some modification in the near past, and its not even too reliable because its not going to track if there is any abnormal exit in the function. Now, i tried to remove all of them, and create a renamed _proc to override the original and place this two tracker before and after the execution of the function itself.
But i have a lots of error (some i solved, but i dont know its the best way, some are not solved at all, so i'm stuck), these are the main ones:
Because there is no exact entry point, where should i define and how, this overrided proc, to work on all of the procedures in this execution? Some of my files had to be manually modified to _proc to work (mostly the ones where there are code outside the procedures and these files as scripts were called, not functions through the tclIndex, the function called ones are all in a utils folder, and only there, maybe it can help).
In the tracker line i placed a "clock" with format, and its always cause abnormal exit.
I had problems with the returned values (if there was one, and some time when there isn't). Even when that was a return, or Exit.
So my question is in short:
How can i solve an overrided proc function, which will write into a logfile a "begin" and "end" block before and after the procedure itself (The log file location was gained from the bash side of this tool), when there is no clear entry point in this tool for the tcl side, and use an auto_mkindex generated procedure index file?
Thanks,
Roland.
Untested
Assuming your bash script does something like
tclsh file.tcl
You could do
tclsh instrumented.tcl file.tcl
where instrumented.tcl would contain
proc trackBegin {name} {...}
proc trackEnd {name output info} {...}
rename proc _proc
_proc proc {name args body} {
set new_body [format {
trackBegin %s
catch {%s} output info
trackEnd %s $output $info
} $name $body $name]
_proc $name $args $new_body
}
source [lindex $argv 0]
See the return and catch pages for what to do with the info dictionary.
You'll have to show us some of your code to provide more specific help, particularly for your clock error.
I'd be tempted to use execution tracing for this, with the addition of the execution tracing being done in an execution trace on proc (after all, it's just a regular Tcl command). In particular, we can do this:
proc addTracking {cmd args} {
set procName [lindex $cmd 1]
uplevel 1 [list trace add execution $procName enter [list trackBegin $procName]]
uplevel 1 [list trace add execution $procName leave [list trackEnd $procName]]
}
proc trackBegin {name arguments operation} {
# ignore operation, arguments might be interesting
...
}
proc trackEnd {name arguments code output operation} {
# ignore operation, arguments might be interesting
...
}
trace add execution proc leave addTracking
It doesn't give you quite the same information, but it does allow you to staple code around the outside non-invasively.

Is there a way to pass params to a script in TCL that is run using eval source?

I have a script that calls:
eval source \{$scriptfile\}
where $scriptfile is another TCL script. Is there way to pass parameters to the script? I'd like to do something like:
set sampleData "ID=14678934"
eval source \{$scriptfile\} $sampleData
I know that this isn't allowed but, is there a way to pass data to a script that is being executed using eval source?
That's a horrible practice to start. It's much cleaner to call a proc that is within the script you're sourcing.
source script.tcl ;# defines proc run_script_with_data
run_script_with_data $data
The solution is given in the Tclers Wiki, in the following articles: source with args and SrcFile.
The solution I like the most is srcfile:
proc srcfile { filename args } {
global argv
set argv $args
source $filename
}
The only drawback of this approach is that it will modify argv, so you have to make sure you will not need it in the rest of the script, or backup and restore it.