Executing shell script from Tcl - tcl

I'm attempting to run a shell script from TCL. I'm having a bit of trouble though as it's not working or giving me an error to troubleshoot. I'm pretty sure my issue is coming from not having "run" formatted properly. Any help is appreciated.
set run "sshpass -p 'password' ssh user#ip 'bash -s' <"
set sh "test.sh"
set cmd [list $run $sh $arg1 $arg2]
if {[catch {eval [linsert $cmd 0 "exec"]} status]} {
foreach line [split $status "\n"] {
if {[string match *text* $line]} {
//do something
}
}
}

Ended up removing the run variable and adding it directly. Works fine now.
set sh "test.sh"
set cmd [list sshpass -p 'password' ssh user#ip 'bash -s' \< $sh $arg1 $arg2]
if {[catch {eval [linsert $cmd 0 "exec"]} status]} {
foreach line [split $status "\n"] {
if {[string match *text* $line]} {
//do something
}
}
}

Related

TCL socket reports broken pipe upon writing from server

When attempting to return a multi-line output via TCL server, the server reports a broken pipe even though the client is able to receive all the data.
Server:
proc Echo_Server {port} {
set s [socket -server EchoAccept $port]
vwait forever
}
proc EchoAccept {sock addr port} {
puts "Accept $sock from $addr port $port"
fconfigure $sock -buffering line
fileevent $sock readable [list Echo $sock]
}
proc Echo {sock} {
if { [eof $sock] || [catch {gets $sock line}] } {
puts "Close $sock"
close $sock
} else {
set returnData "
Hi
How are you?
This is test data
Close
"
puts $sock $returnData
puts $sock "EOF"
flush $sock
}
}
Echo_Server 2500
vwait forever
Client code to send/receive data:
proc Echo_Client {host port} {
set s [socket $host $port]
fconfigure $s -buffering full -buffersize 1000000
#fconfigure $s -buffering line -blocking 0
return $s
}
set s [Echo_Client localhost 2500] ; puts $s "dummy command" ; flush $s
set pp 1 ; while { $pp == 1 } { set line [gets $s] ; puts $line ; if { [regexp "EOF" $line] } { set pp 0 } } ; flush $s ; close $s
The client receives the data sent from the server but the server reports the following:
% source /edascratch/nobackup/sanjaynn/scripts/simple_server.tcl
Accept sock6 from 127.0.0.1 port 59814
error writing "sock6": broken pipe
while executing
"puts $sock $returnData"
(procedure "Echo" line 15)
invoked from within
"Echo sock6"
Is there something obvious that I am missing?
The [eof $sock] condition isn't set before the read in [gets $sock line], and the [gets] call doesn't return an error here, it returns -1 to indicate that there's no data available. Just reverse the conditions order and the server will start working properly:
...
if { [catch {gets $sock line}] || [eof $sock] } {
...

Read socket is blocked

I'm writing a socket utility to communicate a client to a server. When input to the socket from the client side, the server is receiving it fine. However, when input to the socket from the server, the client can't read. When checking for fblocked $channel. It is 1. I've tried everything including adding new line, ...
Please help.
Below is my code
proc read_command { sock } {
variable self
global connected
set len [gets $sock line]
set bl [fblocked $sock]
puts "Characters Read: $len Fblocked: $bl"
if {$len < 0} {
if {$bl} {
puts "Input is blocked"
} else {
set connected 1
puts "The socket was closed - closing my end"
close $sock
}
} else {
if {!$bl} {
puts "Read $len characters: $line"
catch {uplevel #0 $line} output
puts "1==>$output<=="
puts $sock "$output"
puts $sock "\n"
flush $sock
}
}
}
proc client { host port } {
variable self
set s [socket $host $port]
set self(csock) $s
set self($s,addr) $host
set self($s,port) $port
fconfigure $s -buffering line -blocking 0
return $s
}
proc prun { sock args} {
variable self
set result [list]
set cmd $args
set cmd [regsub -all {(^\s*\{)||(\}\s*$)} $cmd ""]
set cmd [string trimleft $cmd]
set o1 [eval $cmd]
#catch {uplevel #0 $cmd} o1
puts "1_$sock ==> $o1"
lappend result $o1
#--------------
puts $sock $cmd
flush $sock
set bl [fblocked $sock]
set file [read $sock]
set bl [fblocked $sock]
puts "Fblocked: $bl"
puts "Output: $file"
puts "2_$Comm::self(csock) ==> $file ==> $bl"
lappend result $file
return $result
}
Here is how I run it.
I call server on 1 of the terminal. It will echo the ip address and the port.
Then I call client using the address and the port above to get back the client socket
Then I call prun on the client shell to get back a pair of values, one with the value of the cmd call on the client, and the other the value of the cmd call on the server. Basically I would like to get the pair of values so I can use them for correlation between the 2 set of data.
Below is the code:
1.
On server shell
$ server
2.
On client shell
$ set s [client $addr $port]
3.
Call a proc to get the value from the client shell, then send the command to the server to get the value from the server shell, and return that value back to the client.
$ set res [prun $s {set val [get_attribute [get_nets mynet] pin_capacitance_max]}]
You wrote:
puts "2_$Comm::self(csock) ==> $file ==> $bl"
and defined self with variable. Are you working with packages?. May be you forgot something related to it.
For test you can use just global but using arrays would be a little more complicated.

How to fix 'send: spawn id exp4 not open while executing "exp_send -s "$cmd\r"" ' error

My TCL file has code like below,
proc executeCmd {cmd {file ""}} {
set out ""
set output ""
set send_slow {20 0.1}
set adminFlag 0
exp_send -s "$cmd\r"
for {set i1 0} {$i1 < 12} {incr i} {
set intimeout 0
expect {
# other options to check 'hostname', 'more', 'press any to continue' regexes
# ...
-regexp {^(.*)Press any key to continue.*$} {
set output [cleanOutput $expect_out(buffer)]
if {[regexp -- {\w+} $file]} {
append out $output
flush $fo
flush $clf
} else {
append out $output
}
exp_sleep 0.1
exp_send -s " "
exp_continue
}
timeout {
#log_msg INFO "TIMED OUT...."
puts "TIMED OUT"
set intimeout 1
puts "Executing $cmd >>> waiting for response from $hostname"
}
}
if {$intimeout} {
exp_send -s " "
} else {
break
}
}
return $out
}
spawn $plinkLoc -telnet $routerIP -P $routerPort
set out [executeCmd "term width 0"]
After executing this TCL through command prompt I am facing error saying,
send: spawn id exp4 not open
while executing
"exp_send -s "$cmd\r""
(procedure "executeCmd" line 28)
invoked from within
"executeCmd "term width 0""
invoked from within
"set out [executeCmd "term width 0"]""
This line 28 in TCL code is 'set' statement which is prior to 'executeCmd' proc, have updated the file for query purpose.
You have to either pass spawn_id to the procedure or declare the spawn_id as global.
Add this line inside procedure
global spawn_id

Compilation Error when running Echo Service example from Tcl Book

Doing the echo service example in the book, 'Practical Programming in Tcl & TK 4th edition' Brent B. Welch Ken Jones Jeffrey Hobbs.
Its on page 241, example 17-3. Copied it straight out of the book and its giving me the following error:
tclsh "theEchoService.tcl" (in directory: /home/<username>/Documents/Scripts/tcl)
Compilation failed.
wrong # args: should be "proc name args body"
while executing
"proc Echo {sock} \
{
global echo
if {[eof $sock]} || [catch {gets $sock line}]} \
{
;# end of file or abnormal connection drop
close $sock
pu..."
(file "theEchoService.tcl" line 16)
Heres my full code:
#!/usr/bin/tclsh
;#The Echo Service. Socket ProgrammingPage 241, Example 17-3
proc Echo_Server {port} \
{
global echo
set echo(main) [socket -server EchoAccept $port]
}
proc EchoAccept {sock addr port} \
{
global echo
puts "Accept $sock from $addr $port"
set echo(addr, $sock) [list $addr $port]
fconfigure $sock -buffering line
fileevent $sock readable [list Echo $sock]
}
proc Echo {sock} \
{
global echo
if {[eof $sock]} || [catch {gets $sock line}]} \
{
;# end of file or abnormal connection drop
close $sock
puts "Close $echo(addr, $sock)"
unset echo(addr,$sock)
} \
else \
{
if {[string compare $line "quit"] == 0} \
{
;# Prevent new connections, Existing connections stay open
close $echo(main)
}
puts $sock $line
}
}
I've tried it without my escapes and still the same. Any ideas?
The problem is this line:
if {[eof $sock]} || [catch {gets $sock line}]} \
^^^
That extra } is terminating the if early, which makes the } at the end of the line terminate the body of the procedure early (and the rest of what you think the body is appears as extra arguments to proc, which doesn't like it).
You're recommended to avoid using backslashes to introduce newlines like that; it's just extra visual noise. You're also recommended to use an editor which can do auto-indentation and/or bracket matching, both of which would have helped you find your problem virtually immediately.
Don't try and make your Tcl code look like C. It is not the same language at all. Every statement in Tcl is made up of words terminated by a newline or semicolon. One way to group words is using the curly braces but these need to be on the same line as the earlier part of the phrase. You can escape the newline as it looks like you are attempting to do but this is fragile and hard to maintain because if you introduce any whitespace after your escape character you no longer escape the newline and you get the error you are seeing.
In Tcl code, put the opening braces on the same line as the code. eg:
proc Echo {sock} {
if {1 == 0} {
puts "something"
} else {
puts "do something else"
expr {
1 * 2 +
3
}
}
}
This is explained in detail in the Tcl(1) manual page but you have to read it rather carefully to glean the details.

Learning TCL with sockets setting up echo server on local machine

I am new at TCL and trying an example within a book called Practical Programming.
I am trying to connect to an echo server with a client within the same file. So I have made a file called EchoServer.tclsh. I then have written the echo server code within this and the echoclient code. When I run this I receive an error that shows:
couldn't open socket: connection refused
while executing
"socket $host $port"
(procedure "Echo_Client" line 2)
invoked from within
"Echo_Client localhost 2540"
invoked from within
"set s [Echo_Client localhost 2540]"
(file "echo_server.tcl" line 35)
The code for the server is:
proc Echo_server {port} {
global echo
set echo(main) {socket -server EchoAccept $port}
}
proc EchoAccept {sock addr port} {
global echo
puts "Accept $sock from $addr port $port"
set echo(addr,$sock) [list $addr $port]
f configure $sock -buffering line
fileevent $sock readable [list Echo $sock]
}
proc Echo {sock} {
global echo
if {[eof $sock] || [catch {gets $sock line}]} {
# end of file or abnormal connection drop
close $sock
puts "Close $echo(addr,$sock)"
unset echo(addr,$sock)
} else {
if {[string compare $line "quit"] ==0} {
# Prevent new connections.
# Existing connections stay open.
close $echo(main)
}
puts $sock $line
}
}
The code for the client is:
proc Echo_Client {host port} {
set s [socket $host $port]
fconfigure $s -buffering line
return $s
}
set s [Echo_Client localhost 2540]
puts $s "Hello!"
gets $s
the line
set echo(main) {socket -server EchoAccept $port}
should be
set echo(main) [socket -server EchoAccept $port]
And enter the event loop by doing a
vwait forever
at the end