Error in TCL Script - tcl

i have return a command to invoke my RTL compiler (Cadence Tool)using tcl script line
puts [exec rc -f script.g ]
i am getting error like abort child process. while if write it directly on my console the to invoke the tool $-rc -f script.g it is getting perfectly exectuded.

exec returns an error condition if:
the exec'ed command returns with a non-zero status
or it prints to stderr
Do this
if {[catch {exec rc -f script.g} output] == 0} {
# normal exit status
puts $output
} elseif {$errorCode eq NONE} {
# normal exit status, but some output to stderr (contained in $output)
puts $output
} else {
# some error condition.
# see http://wiki.tcl.tk/1039 for ways to handle
puts "some error occurred: $errorCode"
}

Related

tcl8.6: what is the built-in equivalent to 'atexit()' in stdlib or 'trap "..." EXIT' in bash?

I'm looking for a builtin or standard package that provides functionality that is similar or equivalent to stdlib's atexit() and bash' trap "..." EXIT.
It should catch termination due to any programatic way of ending execution, including all of the following:
naturally reached end of script execution
explicitly invoked exit
uncaught error
In most cases, all you need to do to intercept such terminations is to intercept the exit command.
rename exit real_exit
proc exit args {
puts "EXITING"; flush stdout; # Flush because I'm paranoid...
tailcall real_exit {*}$args
}
That will obviously work if you call it explicitly, but it also gets called if you just drop off the end of the script, signal end of file in an interactive session, or if your script has an error in it later and terminates with an error message. This is because the Tcl C API call, Tcl_Exit(), works by calling exit and, if that doesn't exit the process, directly does the exit itself.
Be careful with exit scripts BTW; errors in them are harder to debug than normal.
The cases where it doesn't work? Mainly where the interpreter itself has become unable to execute commands (perhaps because it has been deleted out from under itself) or where some signal closes down the interpreter (e.g., SIGINT isn't handled by default for various reasons).
A more-or-less complete atexit based on #Donal's answer:
proc atexit { procbody } {
if { [catch {set oldbody [info body exit]}] } {
rename exit builtin_exit
set oldbody { builtin_exit $returnCode }
}
proc exit { {returnCode 0} } [subst -nocommands {
apply [list [list {returnCode 0}] [list $procbody]] \$returnCode
tailcall apply [list [list {returnCode 0}] [list $oldbody]] \$returnCode
}]
}
Sample code for atexit-test.tcl:
#!/usr/bin/tclsh8.6
source atexit.tcl
atexit {
puts "EXITING($returnCode)"; flush stdout; # Flush because I'm paranoid...
}
atexit {
puts "done($returnCode)..."; flush stdout; # Flush because I'm paranoid...
}
atexit {
puts "almost($returnCode)..."; flush stdout; # Flush because I'm paranoid...
}
{*}$argv
puts "fell through argv for implicit exit..."
... and terminal session:
$ ./atexit-test.tcl
fell through argv for implicit exit...
almost(0)...
done(0)...
EXITING(0)
$ ./atexit-test.tcl exit
almost(0)...
done(0)...
EXITING(0)
$ ./atexit-test.tcl exit 5
almost(5)...
done(5)...
EXITING(5)
$ ./atexit-test.tcl error "unhandled exception"
unhandled exception
while executing
"{*}$argv"
(file "./atexit-test.tcl" line 17)
almost(1)...
done(1)...
EXITING(1)
$

detect assert(0) using tcl/expect

Here is my test.cpp program. It exits abnormally via an assert(0).
#include <cassert>
int main() {
assert(0);
}
When I run this program directly I get the expected output including a non-zero exit status:
$ ./test
...
$ echo $?
134
But when I try to detect the abnormal exit in tcl/expect I don't seem to be able to:
#!/usr/bin/expect
spawn ./test
expect eof
lassign [wait] pid spawnid os_error_flag value
if {$os_error_flag != 0} {
puts "OS error"
exit 1
}
if {$value != 0} {
puts "Application error"
exit 1
}
puts "No error"
When I run that script:
$ ./test.expect
No error
If I use exit(1) instead of assert(0) then the tcl script is able to detect the abnormal exit. Why doesn't tcl/expect provide an OS- or application-returned error code for assertion failures and how can I uniformly detect all abnormal program exits by checking the exit code?
Not an answer, but an extended comment:
Running that code, I see:
$ ./a.out; echo $?
Assertion failed: (0), function main, file x.c, line 4.
Abort trap: 6
134
And in expect, I see:
$ expect
expect1.1> spawn ./a.out
spawn ./a.out
47429
expect1.2> expect eof
Assertion failed: (0), function main, file x.c, line 4.
expect1.3> wait
47429 exp6 0 0 CHILDKILLED SIGABRT SIGABRT
Looks like you need to look at the elements returned by wait beyond the 4th.
When I look up what wait does, I see that:
Additional elements may appear at the end of the return value from wait. An optional fifth element identifies a class of information. Currently, the only possible value for this element is CHILDKILLED in which case the next two values are the C-style signal name and a short textual description.
The assert() call uses abort() to terminate the process if the assertion fails, and that shows up as an exit via SIGABRT.
set result [wait]
if {[lindex $result 4] eq "CHILDKILLED"} {
if {[lindex $result 5] eq "SIGABRT"} {
# assertion failed, or other abort
} else {
# other signal
}
} else {
# normal exit, may be with conventional error
}
Error handling can definitely be fiddly!

How to prevent tcl script from exiting?

I am running tclsh some.tcl and it exits after it hits eof. I want it not to exit and gives control to user for interaction. Note that we can do this by invoking shell and sourcing script but that doesn't solve my problem as it cannot be used in automation.
If you can load the TclX package (old but still useful) then you can do:
package require Tclx; # Lower case at the end for historical reasons
# Your stuff here
commandloop
That's very much like how Tcl's own interactive command line works.
Otherwise, here's a scripted version that does most of what an interactive command session does:
if {![info exists tcl_prompt1]} {
set tcl_prompt1 {puts -nonewline "% ";flush stdout}
}
if {![info exists tcl_prompt2]} {
# Note that tclsh actually defaults to not printing anything for this prompt
set tcl_prompt2 {puts -nonewline "> ";flush stdout}
}
set script ""
set prompt $tcl_prompt1
while {![eof stdin]} {
eval $prompt; # Print the prompt by running its script
if {[gets stdin line] >= 0} {
append script $line "\n"; # The newline is important
if {[info complete $script]} { # Magic! Parse for syntactic completeness
if {[catch $script msg]} { # Evaluates the script and catches the result
puts stderr $msg
} elseif {$msg ne ""} { # Don't print empty results
puts stdout $msg
}
# Accumulate the next command
set script ""
set prompt $tcl_prompt1
} else {
# We have a continuation line
set prompt $tcl_prompt2
}
}
}
Getting the remaining bits right (e.g., the interaction with the event loop when the Tk package is loaded) would require quite a bit more complexity...

How to run executable within the tcl?

I am trying to execute program which has some options, and take as an input txt file. So I have try this:
set myExecutable [file join $::env(path_to_the_program) bin executable_name]
if { ![file exists $myExecutable ] } {
puts "error"
}
if { ![file executable $myExecutable ] } {
puts "error"
}
set arguments [list -option1 -option2]
set status [catch { exec $myExecutable $arguments $txtFileName } output]
if { $status != 0 } {
puts "output = $output"
}
So it's print:
output = Usage: executable_name -option1 -option2 <txt_file_name>
child process exited abnormally
You didn't actually provide the arguments to you executable. Just the textFileName. Try:
set status [catch {exec $myExecutable -option1 -option2 $txtFileName} output]
or if you prefer to keep the arguments in a list:
set status [catch {exec $myExecutable {*}$arguments} output]
where the {*} syntax will cause the list to be expanded in place. In Tcl versions before this was added (8.5) you would use:
set status [catch {eval exec [list $myExecutable] $arguments} output]
where the eval command unwraps the lists so that exec sees a single set of arguments. Adding the extra [list] statement around your $myExecutable protects it's contents against being treated as a list by the interpreter pass.

Expect is not behaving as expected when I send a command that is too long to fit on a single line

I am trying to create an expect script which will grep a file and return the line which contains the string I am looking for, in this case the string will be a terminal ID. For example I have a file called terminal_list.txt with the following contents:
0x400 192.168.62.133 10006
0x420 192.168.62.133 10021
0x440 192.168.62.133 10022
and I want the line returned which starts with 0x420
My code is as follows:
#!/usr/bin/expect -f
set terminal_list "/home/linkway/terminal_list.txt"
set termid "0x400"
spawn /bin/bash
expect "] "
# Get ip and port of terminal
# Check if termid exists in terminal_list file
set command "grep -q '$termid' '$terminal_list' && echo 'true' || echo 'false'"
send "$command\r"
expect "$command\r\n"
expect -re "(.*)\r\n.*] "
set val $expect_out(1,string)
# If terminal_list does not exist print error and exit
if { [string first "No such file or directory" $val] >= 0 } {
puts "$terminal_list does not exist. Script Failed"
exit
# If terminal_list does not contain termid print error and continue
} elseif { [string match "false" $val] } {
puts "\nTerminal $termid does not exist in ${terminal_list}. Cannot update bootfile.\n"
# If termid is found in terminal_list, get the ip and port of the terminal
} else {
set command "grep '$termid' '$terminal_list'"
send "$command\r"
expect "$command\r\n"
expect -re "(.*)\r\n.*] "
set val $expect_out(1,string)
set ip_port [string range $val 6 end]
}
This works perfectly when I ssh to the RHEL server via putty and run the script in a maximized putty window. HOWEVER when I shrink the window length so that the grep command no longer fits on a single line my code breaks! Can anyone please help me come up with a solution to this? I have been struggling with the processing of expect_out and could really use some guidance.
EDIT: I found what was causing the bug. It turns out that when the grep command is split over multiple lines, a \r is added to the command where the line break is. Here is some debugging info from exp_internal 1. You can see how the \r is added into the grep command where the command ran onto the next line:
expect: does "grep -q '0x400' '/home/linkway/term \rinal_list.txt'
&& echo 'true' || echo 'false'\r\n" (spawn_id exp6)
match glob pattern "grep -q '0x400' '/home/linkway/terminal_list.txt'
&& echo 'true' || echo 'false'\r\n"? no
Why is this happening and what is the proper way to get just the output of the grep command? I find it very strange that expect would behave differently base on how the output of a command is displayed on screen. Any help is greatly appreciated.
I was able to find a cleaner solution to my problem by making my script more expect-like. Here's what it looks like:
set command "grep -q '$termid' '$terminal_list' && echo 'true' || echo 'false'"
send "$command\r"
expect {
-re ".*\r\ntrue\r\n.*] " {
send "grep '$termid' '$terminal_list'\r"
expect ".*\r\n"
expect -re "(.*)\r\n.*] "
set val $expect_out(1,string)
set ip_port [string range $val 6 end]
puts "\nUpdating $termid bootfile"
updatebootfile $ip_port $boot_data $termid
}
-re ".*\r\nfalse\r\n.*] " {
puts "\nTerminal $termid does not exist in ${terminal_list}. Cannot update bootfile.\n"
}
-re "No such file or directory.*] " {
puts "$terminal_list does not exist. Script Failed"
exit 1
}
timeout {
puts "expect timeout when searching for $termid in $terminal_list"
}
}