ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
I periodically Run a command line program that returns stdout text to the terminal. I would like to exec this program from a tcl/tk script and have the stdout display in a textbox widget.
Although I did this several years ago I don't remember how it was done and now have no idea how.
Any comments, advice or suggestions would be welcome. Hope I'm clear on what I need.
Last edited by sharky; 06-11-2008 at 07:15 PM.
Reason: spelling
You just need to create a pipe and use that as the commands stdout, capture the output and stuff it into the textwidget. See pipe(2). You'll need to close stdout of the child process after you fork, and dup it to the writing end of the pipe before exec'ing. You should close the ends you don't need (i.e. the write end in the parent and the read end in the child). The pipe man page has a brief example.
Alternatively, you could use popen, which does the hard work for you, but works slightly differently (it passes the command line to /bin/sh). So popen is less work setting it up, but pipe gives you more flexbility (e.g. you could use two pipes if you want to capture stderr too).
.txtBox delete 1.0 end
set myvar [exec mycmd]
.txtBox insert 1.0 end $myvar
This is not what I suggested in my original question because this is not necessarily a capture of STDOUT. The tricky part will be capture STDOUT and STDERR if 'mycmd' barfs.
You just need to create a pipe and use that as the commands stdout, capture the output and stuff it into the textwidget. See pipe(2). You'll need to close stdout of the child process after you fork, and dup it to the writing end of the pipe before exec'ing. You should close the ends you don't need (i.e. the write end in the parent and the read end in the child). The pipe man page has a brief example.
Alternatively, you could use popen, which does the hard work for you, but works slightly differently (it passes the command line to /bin/sh). So popen is less work setting it up, but pipe gives you more flexbility (e.g. you could use two pipes if you want to capture stderr too).
"create a pipe"? Sorry, that just went right over my head.
Sorry, went off on a bit of a tangent there, after I opened the thread my brain blocked out the "tcl/tk", I was leaning towards C for some reason. ?
There is also the open command. You can redirect stderr to a file as in the shell, not sure if you can capture it, my tcl is extremely rusty (and was never particularly shiny to begin with...), but you can always read the file afterwards.
Last edited by smoked kipper; 06-13-2008 at 02:03 PM.
It turned out to be very easy to capture stderr and write it to my text widget.
.txtbox delete 1.0 end
catch {exec $myCmd} result
.txtbox insert 1.0 $result
Anything returned by 'exec $myCmd' gets written to $result.
Now I need to convert $result into an array. That will be another thread.
Regards all,
This actually doesn't work like I thought. I'm doing another tcltk script and this actually doesn't write to the text widget until $myCmd is completed.
I've done some googling and found some hints on how to 'pipe' stdout and stderr to the widget but haven't gotten anything to work.
Any suggestions would be appreciated. I'll post the 'non-working' code I have later today after I get to work.
This is the code I have so far that does NOT work.
Code:
if [catch {open "| ~/projects/$sel/${sel}.csh"} result] {
.txtCreateEnv insert end $result
} else {
fileevent $result readable "Log {$result}"
}
proc Log {result} {
if [eof $result] {
catch {close $result}
} else {
gets $result line
.txtCreateEnv insert end $line
.txtCreateEnv see end
}
}
sel is defined earlier. The command after the open statement runs because I can see the generated results. However, the text widget is empty.
I put a statement after the 'gets' statement, "puts $line", and I see the expected output returned to the terminal. I have no idea why it's not getting written to the text widget.
This is the code I have so far that does NOT work.
Code:
if [catch {open "| ~/projects/$sel/${sel}.csh"} result] {
.txtCreateEnv insert end $result
} else {
fileevent $result readable "Log {$result}"
}
proc Log {result} {
if [eof $result] {
catch {close $result}
} else {
gets $result line
.txtCreateEnv insert end $line
.txtCreateEnv see end
}
}
sel is defined earlier. The command after the open statement runs because I can see the generated results. However, the text widget is empty.
It's working now and I didn't change any of the code posted above. Immediately following the first if statement I had ".txtCreateEnv configure -state disabled". After removing that line the stdout and stderr start streaming to the widget. To disable the widget ( I don't want it to be editable ) I added the configure statement after the catch statement in the Log proc.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.