Based on the help page for the Tcl catch command, I was trying to use the specified result variables like TCL_OK or TCL_ERROR. However, there is some problem with the syntax of how those variables should be accessed. Does anyone have a code example that uses those variables instead of magic numbers?
From my experience, you want to use the numerical values, not the names. The names are used when programming in the C api but, from Tcl, one just uses the numbers.
set code [catch {
my script
} result]
switch -exact -- $code {
0 {
puts "normal command completion"
}
1 {
puts "code threw an error (ie: error 'wtf')"
}
2 {
puts "code used 'return' command normally"
}
3 {
puts "code used 'break' command"
}
4 {
puts "code used 'continue' command"
}
}
Related
I am looking for a way to convert a script into its fully hard-coded version. I even don't know how this kind of function is named in software industry.. so I even don't know how to google it. Here are some examples of what I want. I don't want to use any conditional or iterative function for the outputs.
Example 1) foreach case
foreach x [list 1 2 3] {
puts "x=$x"
}
I'd like to convert this script to have:
puts "x=1" ; puts "x=2" ; puts "x=3"
Example 2)
set A 1
if { $A == "1" } {
puts "A is 1"
} else {
puts "A is not 1"
}
I want this to be :
set A 1
puts "A is 1"
Example 3) If I face comment line or unknown command or procedure, I just want to pass it to the output.
set argument 1000
UnknownProcedure $argement
This now should be :
set argument 100
UnknownProcedure 100
Thanks in advance.
You could try trace add execution ... to commands. This is used to execute a body of code before the command itself if executed. If you can pass the arguments of your command to your trace function and also use info frame to get the proc/command name being executed, then you could use the trace function to write out a hardcoded version of the command.
See here:
https://www.tcl-lang.org/man/tcl8.6/TclCmd/trace.htm
https://wiki.tcl-lang.org/page/info+frame
I am using the following TCL code:
proc RunCSM { scen } {
catch { $scen start }
if { "[$scen status]" != "SUCCESS" } {
puts "$scen FAILED. Error Info:"
puts "[$scen errorInfo]" ...
I need also the line number of the code that fails. In 8.5 and onwards this is achieved by this nice solution
How can I get the code line number along with errorinfo?
How is it possible to achieve the same but in version 8.4?
The easiest approach is to parse the errorInfo variable. Here's what an example looks like:
% parray foo
"foo" isn't an array
% set errorInfo
"foo" isn't an array
while executing
"error "\"$a\" isn't an array""
(procedure "parray" line 4)
invoked from within
"parray foo"
Parsing that with regexp isn't too hard, provided we use the -line option.
proc getLineFromErrorInfo {} {
global errorInfo
if {[regexp -line { line (\d+)\)$} $errorInfo -> line]} {
return $line
} else {
# No guarantee that there's information there...
return "unknown"
}
}
On our example from before, we can then do:
getLineFromErrorInfo
and it will return 4. You might want to extend the RE to also capture the name of the procedure; line numbers in 8.4 and before are always relative to their procedure. (This is also mostly true in 8.5 onwards; this is an area where backward compatibility is a bit painful IMO.) Here's how you might do that:
proc getLocusFromErrorInfo {} {
global errorInfo
if {[regexp -line {\(procedure "(.*?)" line (\d+)\)$} $errorInfo -> proc line]} {
return [list $proc $line]
} else {
# No guarantee that there's information there...
return "unknown"
}
}
Note that merely knowing where the error came from doesn't necessarily tell you where the problem is, especially in production code, since it could be due to bad arguments elsewhere that have been passed around a bit…
I have a "library" type file that where I define a namespace and export some functions to "print" debug info or "trace" info in TCL.
proc cputs { args } {
puts "Test= $args"
}
I import this function in multiple other files. When I use it everything is fine except it add bracket to the output.
Like if I call cputs "Hi there" it will output {Hi there} instead of Hi there.
Can't find anything to explain that. I would like to get ride of those brackets.
Any help would be appreciated.
Thanks
If you do not want to pass an arbitrary, unknown numbers of arguments to your cproc, do not use args (which has a special meaning as a parameter name), but msg, for instance.
proc cputs {msg} {
puts "Test= $msg"
}
See also proc documentation:
If the last formal argument has the name “args”, then a call to the
procedure may contain more actual arguments than the procedure has
formal arguments. In this case, all of the actual arguments starting
at the one that would be assigned to args are combined into a list (as
if the list command had been used); this combined value is assigned to
the local variable args.
If you want to take an arbitrary number of arguments (since args is a special name), you should join or concat things before printing. These two options differ in their treatment of extra whitespace; use the one that suits you.
proc cputs { args } {
puts "Test= [join $args]"
}
cputs "Hello there " " from an example"
# Test= Hello there from an example
proc cputs { args } {
puts "Test= [concat {*}$args]"
}
cputs "Hello there " " from an example"
# Test= Hello there from an example
How can I implement expand in the script below (expanding commands and variables as normal in tcl), I want it to print out:
'
hello {
$b
}
{tcl world}
'
This is how I imagine the script will look:
proc clever_func {script} {
set script [uplevel 1 expand [list $script]]
puts "'$script'"
}
proc isolated_client_function {} {
set a hello
set b difficult
set c tcl
set d world
clever_func {
$a {
$b
}
[list $c $d]
}
}
isolated_client_function
An example isn't a sufficient replacement for a specification, but it seems that there are no built-in TCL facility doing what you want. It would be possible to perform a substitution on a single command tail (by prepending list to a command and evalling it at any level you want), but not for a pseudo-script with two "commands". (It's also possible to do what string interpolation does, using subst, but you probably already know why it's not what you want: it will also expand $b).
I see two possibilities to get what you want:
Tokenize your input using sugar::scriptToList, then perform substitution manually in the list of tokens, then convert the result back to textual form with sugar::listToScript. These features of sugar macroprocessor are designed to let you modify scripts semantically while preserving formatting, comments and layout.
Break down your input into separate "commands": first split at each newline and semicolon, then use info complete to collect pieces corresponding to complete "commands" (IIRC there will be some corner cases with backslash-newline continuation: beware). On each complete "command" use the trick of prepending "list ", evaluating the result in necessary context (uplevel). Thus for each command you'll get a list where substitution has already been performed when appropiate. You will lose nuances of formatting inside each "command" (like the number of spaces separating words and the kind of space), and you will lose original command separators unless you take care to remember them yourself. It might not be bad for you if the goal is to get a kind of "pre-expanded script" to be evaluated later in some other context.
The subst command will take you most of the way there.
proc expand {script} {
uplevel 1 [list subst $script]
}
set a hello
set b difficult
set c tcl
set d world
expand {
$a {
$b
}
[list $c $d]
}
outputs
hello {
difficult
}
tcl world
I'm not sure off the top of my head how to prevent $b from being substituted. You'll probably have to parse the script yourself.
If you want the last part to have the quoting you demonstrate, you need another level of list-ness
expand {
$a {
$b
}
[list [list $c $d]]
}
outputs
hello {
difficult
}
{tcl world}
I'm having serious biggies trying to figure out how to evaluate an expression in a If statement using Tcl. This is what I have:
#!/bin/sh
#The next line executes wish - wherever it is \
exec wish "$0" "$#"
set apid "/var/run/apache2.pid"
set apsta [file exist $apid]
if { $apsta == 1 }
{
set result ":)"
}
else
{
set result ":("
}
label .status -text $result
pack .status
and this is what I get from the terminal:
# wan27
Error in startup script: wrong # args: no script following " $apsta == 1 " argument
while executing
"if { $apsta == 1 }"
(file "/usr/bin/wan27" line 9)
I'm just trying to output a happy smiley if Apache is running, a sad one if Apache is stopped - based upon the boolean condition whether or not the file "/var/run/apache2.pid" exists ...
The syntax of a Tcl if statement is
if condition statement else statement
which must be on one line. Braces allow continuation across lines and their placement is mandatory:
if { $apsta == 1 } {
set result ":)"
} else {
set result ":("
}
As you have it written, Tcl sees
if { $apsta == 1 }
and stops there yielding a syntax error.
MSW have already given you the correct answer but I think a little bit more explanation is needed to clear up some other confusions you are having based on your comments.
I will first explain things using non-tcl terminology since I think it is less confusing that way.
In tcl, if is not a statement. if is a function. That is the reason why the opening brace need to be on the same line: because a newline terminates the list of arguments to a function. For example, in the following code:
a b c d
e f
the Tcl interpreter will see two function calls. The first to function a with arguments b, c and d and the second to function e with a single argumrnt f. Similarly, in the following:
if a
b
Tcl sees a call to the function if with a single argument. Since if expects at least two arguments it (not the interpreter itself) throws an error complaining about wrong number of arguments.
This also explains why there must be a space between if and its first argument. It's just because in tcl names of variables and functions are literally allowed to contain almost anything including spaces, commas and non-printing characters like NUL. For example, you can define a function called a{b}:
proc a{b} {} {puts HA!}
a{b} ;# <-- prints out HA!
So if you do something like:
if{x} {y}
tcl will complain that the function if{x} is not defined.
if is not the only thing that works like this. Tcl doesn't really have keywords, just a standard library of built-in functions like for, foreach and while. The same rules apply to all of them.
not really important:
On a side, the if function in tcl works like the ternary operator in C: it returns a value. In fact you can do the following:
# It is unfortunate that this "give" function is not built in.
# We use it to return something from if without actually
# calling return:
proc give {x} {return $x}
set something [if {$x} {give "Very true indeed."} {give "Unfortunately false."}]