Using write access in Open command in TCL - tcl

How can i use write ('w') and read ('r') access while using command pipeline in open command in TCL.
when i do something like :
set f1 [open "| ls -l" w]
it returns a file descriptor to write to , say file1.
Now I am confused how can I put this file descriptor to my use.
PS : My example might be wrong, and in that case it'd be ideal if answer includes a programming example so that it'll be more clear.
Thanks

In general, the key things you can do with a channel are write to it (using puts), read from it (using gets and read), and close it. Obviously, you can only write to it if it is writable, and only read from it if it is readable.
When you write to a channel that is implemented as a pipeline, you send data to the program on the other end of the pipe; that's usually consuming it as its standard input. Not all programs do that; ls is one of the ones that completely ignores its standard input.
But the other thing you can do, as I said above, is close the channel. When you close a pipeline, Tcl waits for all the subprocesses to terminate (if they haven't already) and collects their standard error output, which becomes an error message from close if there is anything. (The errors are just like those you can get from calling exec; the underlying machinery is shared.)
There's no real point in running ls in a pure writable pipeline, at least not unless you redirect its output. Its whole purpose is to produce output (the sorted list of files, together with extra details with the -l option). If you want to get the output, you'll need a readable channel (readable from the perspective of Tcl): open "| ls -l" r. Then you'll be able to use gets $f1 to read a line from the subprocess.
But since ls is entirely non-interactive and almost always has a very quick running time (unless your directories are huge or you pass the options to enable recursion), you might as well just use exec. This does not apply to other programs. Not necessarily anyway; you need to understand what's going on.
If you want to experiment with pipelines, try using sort -u as the subprocess. That takes input and produces output, and exhibits all sorts of annoying behaviour along the way! Understanding how to work with it will teach you a lot about how program automation can be tricky despite it really being very simple.

Related

Use of command pipeline with open command

I was trying to understand the usage of command pipeline with the open command in TCL.
I read the following paragraph from the documentation :
" If write-only access is used (e.g. access is w), then standard output for the pipeline is directed to the current standard output unless overridden by the command. If read-only access is used (e.g. access is r), standard input for the pipeline is taken from the current standard input unless overridden by the command. "
I was unable to understand what it means , so i tried some code which was :
set f1 [open "| touch testFile.txt" w+]
set a {USA UK AUS IND JAP}
foreach country $a {
puts $f1 "Member of democratic alliance : $country"
}
close $f1
But when i checked the contents of file, there was nothing present.
Can somebody please explain that paragraph from the TCL documentation (with some exaples) and also point where am I doing mistake in my own
Thanks
The touch program does not read its standard input or normally write to its standard output. It just updates the access time of the file, creating it (as a zero length file) if necessary. If you provide standard input to it, it'll just ignore it.
For the purposes of learning about working with pipelines, you might instead prefer to use the tee program.
Although you haven't got there yet, with a bidirectional pipe (such as those made when you open them with the w+ mode) you need to be careful because of the potential for deadlock when both sides fill up their OS-level write buffers in the pipe and aren't reading from them. It's usually wisest to switch bidirectional pipes to non-blocking in Tcl. It probably is also a good idea to set them as no stricter than line buffered, or maybe even unbuffered. Or to use flush at the right times, but that's harder to get right.
fconfigure $f1 -blocking false -buffering none
These are fundamental issues caused by needing to have several programs work together, not by pipes themselves or the programming languages used; it's just that bidirectional pipelines are the place where programmers usually first observe them. They very much also apply to working with TCP sockets.

How to get immediate output in my IDE from a Tcl script?

I have just started to use the tcl language and I need to create a script with several functions triggered every 2 seconds. I have been searching an answer on the internet and found several topics about it. For instance, I found this code on StackOverlow (How do I use "after ms script" in TCL?):
#!/usr/bin/tclsh
proc async {countdown} {
puts $countdown
incr countdown -1
if {$countdown > 0} {
after 1000 "async $countdown"
} else {
after 1000 {puts Blastoff!; exit}
}
}
async 5
# Don't exit the program and let async requests
# finish.
vwait forever
This code could very easily be adapted to what I want to do but it doesn't work on my computer. When I copy paste it on my IDE, the code waits several second before giving all the outputs in one go.
I had the same problem with the other code I found on the internet.
It would be great if someone could help me.
Thanks a lot in advance.
I've just pasted the exact script that you gave into a tclsh (specifically 8.6) running in a terminal on macOS, and it works. I would anticipate that your script will work on any version from about Tcl 7.6 onwards, which is going back nearly 25 years.
It sounds instead like your IDE is somehow causing the output to be buffered. Within your Tcl script, you can probably fix that by either putting flush stdout after each puts call, or by the (much easier) option of putting this at the start of your script:
fconfigure stdout -buffering line
# Or do this if you're using partial line writes:
# fconfigure stdout -buffering none
The issue is that Tcl (in common with many other programs) detects whether its standard output is going to a terminal or some other destination (file or pipe or socket or …). When output is to a terminal, it sets the buffering mode to line and otherwise it is set to full. (By contrast, stderr always has none buffering by default so that whatever errors that occur make it out before a crash; there's nothing worse than losing debugging info by default.) When lots of output is being sent, it doesn't matter — the buffer is only a few kB long and this is a very good performance booster — but it's not what you want when only writing a very small amount at time. It sounds like the IDE is doing something (probably using a pipe) that's causing the guess to be wrong.
(The tcl_interactive global variable is formally unrelated; that's set when there's no script argument. The buffering rule applies even when you give a script as an argument.)
The truly correct way for the IDE to fix this, at least on POSIX systems, is for it to use virtual terminal to run scripts instead of a pipeline. But that's a much more complex topic!

How to use printf to debug TCL source v8.4

I am using TCL as an embedded control in my system and I need to modify its core source a bit, I mean the code under generic/, such as tclInterp.c. I am adding printf to the source code to trace my modification, but for some reason I cannot see the output. I see the code is using fprintf, I used that and tried both stdout and stderr, still not working.
I already added "--enable-symbols=all" to run configure and re-build the packages. Is there anything else I need to do?
You should use a debugger instead. Adding printf statements to the core code will result in your output appearing on stdout which your Tcl scripts may redirect. using fprintf(stderr, ...) might be less likely to clash with the scripts you run. --enable-symbols just results in a debuggable build - it will not affect the ability to write to stdout but will result in a debugger being able to produce meaningful output.
You don't say - but if you are on Windows and are embedded in a graphical program then you probably don't have stdout anyway. On Windows, you will be best to use OutputDebugString and watch the messages in Visual Studio's output window or sysinternals DbgView.
On unix, the console you launch the application should show the output. However, actually tracing your mods with a debugger will be the best route.
Are you sure you really need to modify the core? Seems unlikely to me. Normally you just add additional commands to the interpreter to provide the interface to your hosting application. The Tcl API offers access to pretty much everything you might reasonably want to fiddle with.

Exec-ing a program within tcl with redirection

Within a tcl program I am trying to exec a program which takes input using <
puts [exec "./program < inputfile"]
However, this produces the error
couldn't execute "./program < inputfile": no such file or directory
Is there a way of doing this in tcl?
Tcl can process redirections itself, so you would write:
puts [exec ./program <inputfile]
Otherwise, it tries to interpret the whole thing as a single (somewhat strange) filename; legal (on Unix, not on Windows) but not what you wanted.
Alternatively, you can fire the whole thing off through the system shell:
puts [exec /bin/sh -c "./program < inputfile"]
That works, but has many caveats. In particular, quoting things for the shell is a non-trivial problem, and you're not portable to Windows (where the incantation for running things through the command-line processor is a bit different). You also have an extra process used, but that's not really a big problem in practice unless you're really stretching the limits of system resources.
The plus side is that you can use full shell syntax in there, which can do a few things that are downright awkward otherwise. It's also more widely known, so something I'd surface to users with a medium level of expertise. (New users should stick to canned stuff; real experts can write some Tcl scripts.)
I found out the answer, the command has to be written as
puts [exec "./program" < inputfile]

How to make Tcl interpreter source a file and open a pipe from shell command simultaneously?

Is there a way to make Tcl interpreter source a file and open a pipe from shell command parallel?
In more details, I have a GUI built from tcl/tk. I want my tcl script to source a setting file for GUI variables, and at the same time, open a pipe from [tclsh setting_file] to redirect the output to my GUI stdout.
Thank you very much!
I'm not convinced that running the processing of the settings command in a subprocess is a good idea. Maybe a safe interpreter would be better?
Re trapping the output, you could pick a technique for doing stdout capture and then show the contents of the captured buffer in the GUI (after using encoding convertfrom to get the characters back if you're using my solution to that problem) but you've got a general issue that it is possible for user code to block things up if it takes a long time to run. You could work around that by using threads, but I suspect it is easier to avoid the complexity and to just let badly-written setup code cause problems that the user will have to fix. (The catch command can help you recover from any outright errors during the sourcing of the settings file.)