Running an external program from a Tcl script - tcl

I would like to export a variable depending on result of a binary command. My TCL script is this:
set A ""
exec sh -c "export A=\"`/usr/local/cuda/samples/1_Utilities/deviceQuery/deviceQuery -noprompt | grep ^Device | wc -l`\""
puts $A
if { $A == "1" } {
set CUDA_VISIBLES_DEVICES 0
} else {
set CUDA_VISIBLES_DEVICES 1
}
With this script, when I execute puts $A I don't get anything in terminal... so in if command I don't know what I evaluating...
My "export" must return ONLY 1 or 0...
Sorry about my poor TCL level.
Thanks.

I guess what you want is something like this:
set a [exec /usr/local/cuda/samples/1_Utilities/deviceQuery/deviceQuery -noprompt | grep ^Device | wc -l]
You set variable a in TCL context and assign the command's return value (i.e. the output text) to it.

The problem is that your exec'd command runs in its own process, so when you set a variable A, that A only exists as a shell variable for the life of that process. When the process exits, A goes away.
The exec command returns the stdout of the command that was exec'd. If you want the result of the command to be in a Tcl variable, you need to set the variable to be the result of the exec:
set A [exec ...]
For more information on the exec command, see the exec man page.

Related

Tcl: how does this proc return a value?

I'm modifying the code below, but I have no idea how it works - enlightenment welcome. The issue is that there is a proc in it (cygwin_prefix) which is meant to create a command, by either
leaving a filename unmodified, or
prepending a string to the filename
The problem is that the proc returns nothing, but the script magically still works. How? Specifically, how does the line set command [cygwin_prefix filter_g] actually manage to correctly set command?
For background, the script simply execs filter_g < foo.txt > foo.txt.temp. However, historically (this no longer seems to be the case) this didn't work on Cygwin, so it instead ran /usr/bin/env tclsh filter_g < foo.txt > foo.txt.temp. The script as shown 'works' on both Linux (Tcl 8.5) and Cygwin (Tcl 8.6).
Thanks.
#!/usr/bin/env tclsh
proc cygwin_prefix { file } {
global cygwin
if {$cygwin} {
set status [catch { set fpath [eval exec which $file] } result ]
if { $status != 0 } {
puts "which error: '$result'"
exit 1
}
set file "/usr/bin/env tclsh $fpath"
}
set file
}
set cygwin 1
set filein foo.txt
set command [cygwin_prefix filter_g]
set command "$command < $filein > $filein.temp"
set status [catch { eval exec $command } result ]
if { $status != 0 } {
puts "filter error: '$result'"
exit 1
}
exit 0
The key to your question is two-fold.
If a procedure doesn't finish with return (or error, of course) the result of the procedure is the result of the last command executed in that procedure's body.
(Or the empty string, if no commands were executed. Doesn't apply in this case.)
This is useful for things like procedures that just wrap commands:
proc randomPick {list} {
lindex $list [expr { int(rand() * [llength $list]) }]
}
Yes, you could add in return […] but it just adds clutter for something so short.
The set command, with one argument, reads the named variable and produces the value inside the var as its result.
A very long time ago (around 30 years now) this was how all variables were read. Fortunately for us, the $… syntax was added which is much more convenient in 99.99% of all cases. The only place left where it's sometimes sensible is with computed variable names, but most of the time there's a better option even there too.
The form you see with set file at the end of a procedure instead of return $file had currency for a while because it produced slightly shorter bytecode. By one unreachable opcode. The difference in bytecode is gone now. There's also no performance difference, and never was (especially by comparison with the weight of exec which launches subprocesses and generally does a lot of system calls!)
It's not required to use eval for exec. Building up a command as a list will protect you from, for example, path items that contain a space. Here's a quick rewrite to demonstrate:
proc cygwin_prefix { file } {
if {$::cygwin} {
set status [catch { set fpath [exec which $file] } result]
if { $status != 0 } {
error "which error: '$result'"
}
set file [list /usr/bin/env tclsh $fpath]
}
return $file
}
set cygwin 1
set filein foo.txt
set command [cygwin_prefix filter_g]
lappend command "<" $filein ">" $filein.temp
set status [catch { exec {*}$command } result]
if { $status != 0 } {
error "filter error: '$result'"
}
This uses {*} to explode the list into individual words to pass to exec.

Difference for eval exec functions in TCL

I am still confuse about eval and exac scenario as below;
1st scenario: exec ping "stackoverflow.com" -n 1
2nd scenario: eval exec [list ping //nologo "stackoverflow.com" -n 1]
3rd scenario: [list eval exec [list ping //nologo "stackoverflow.com" -n 1]]
The questions as below;
1. Difference tree above?
2. what is value number 1?
3. which one is good to use it?
Thanks in advance.
Starting with Tcl 8.5 (current is 8.6.8), the expansion
operator {*} (which breaks a list
into its component words) was added, and eval is rarely needed except
when evaluating scripts and script fragments.
With older versions of Tcl, eval is used instead of the expansion operator.
With the use of the expansion operator, #2 would become:
exec {*}[list ping /nologo "stackoverflow.com" -n 1]
There's nothing wrong with your #1, but there are a couple of common
patterns with the usage of exec where #2 is more useful.
a) Saving the command to be executed allows you to reuse it for a retry
or for debugging.
b) Commands can be built in a dynamic fashion.
foreach {host} [list stackoverflow.com stack_typo_exchange.com superuser.com] {
set cmd [list ping /nologo $host -n 1]
try {
exec {*}$cmd
} on error {err res} {
puts "ERROR: exec: $cmd"
puts " result: $res"
}
}
Older versions of Tcl would use the catch command:
if { [catch {eval exec $cmd}] } {
puts "ERROR: exec: $cmd"
}
Your #3 is (usually) not correct code. It is creating a list out of the return value from eval exec.
References: Tcl / argument expansion, try, catch, exec

TCL exec with special characters in list

There is tcl procedure which executes command stored in tcl list.
For example:
catch { exec $list}
List looks something like:
--option1 op1 --option2 op2 --option3 op3 ...
One of options is regexp that looks like:
(.*[/\])?(sh|bash)(\.exe)?
After substitution by exec option looks like:
{(.*[/\])?(sh|bash)(\.exe)?}
But what I need is:
"(.*[/\])?(sh|bash)(\.exe)?"
What can I do in such situation?
When a list is converted to a string, it is converted to a canonical form that will convert back to the same list.
What you are seeing are the quoting characters that are used to ensure that the canonical form converts back correctly.
So the value is correct.
exec $list only passes a single argument to exec. exec takes a series of words as arguments, not a list which contains words.
The exec command should be:
catch { exec {*}$list }
The {*} syntax converts the list into its component words.
In older versions of tcl, the eval statement must be used:
catch { eval exec $list }
References: exec, eval, {*} (section 5 of Tcl)
exec is going to execute commnad as subprocess
See semples :
works :
exec ps aux | grep tclsh
not working :
exec "ps aux | grep tclsh"
exec [list ps aux | grep tclsh]
but works fine :
eval exec "ps aux | grep tclsh"
eval exec [list ps aux | grep tclsh]
So we have got evaluating before executing - creation command(exec ps aux | grep tclsh) and invoking it. So eval doesn't care about type.
For current situation with options: exec someExecutable --option1 op1 --option2 op2 --option3 (.*[/\])?(sh|bash)(\.exe)?
I would recoment such solution:
set op3 {"(.*[/\])?(sh|bash)(\.exe)?"}
lappend cmd [someExecutable --option1 op1 --option2 op2 --option3 $op3]
set cmd [join $cmd]
eval exec $cmd
where [join $cmd] creates string from list - and there is not any { }

unix function return if any error occurs

I have a unix script in which I am calling functions.
I want the function should return immediately if any of the command failed in between.
But checking $? after every command I can not do. Is there any other way to do this.
Maybe running the script from a file line by line (as long of course as each of your functions are one line long).
Maybe the following script can be a starting point:
#!/bin/sh
while read l
do
eval "$l || break"
done <<EOF
echo test | grep e
echo test2 | grep r
echo test3 grep 3
EOF
This is another idea after my previous answer. It works with bash script and requires your functions to be quite simple (pipes may cause some issues):
#!/bin/bash
set -o monitor
check() {
[ $? -eq 0 ] && exit
}
trap check SIGCHLD
/bin/echo $(( 1+1 ))
/bin/echo $(( 1/0 ))
/bin/echo $(( 2+2 ))
Furthermore: functions need to be external command (this is why I use /bin/echo rather than echo). Regards.

how do i make a variable unique

How do I make a variable unique in TCL?
Example:
exec echo $msgBody - /tmp/Alert_Notify_Work.$$
exec cat /home/hci/Alert.txt -- /tmp/Alert_Notify_Work.$$
This does not work; I am trying to make the variable Alert_Notify_Work unique.
It's best to use a pre-existing library for this. Tcllib has a fileutil package that implements tempfiles:
set filename [fileutil::tempfile Alert_Notify_Work.]
$$ is not valid Tcl syntax, and Tcl will parse that line before the shell sees it. But there is a Tcl command to retrieve the pid: pid. I usually rely on the current time and the pid for uniqueness.
I assume msgBody is a Tcl variable, and the - and -- in your commands should be > and >> respectively.
option 1
set filename /tmp/Alert_Notify_Work.[clock seconds].[pid]
exec echo $msgBody > $filename
exec cat /home/hci/Alert.txt >> $filename
or, Tcl only with just a few more lines:
set f_out [open /tmp/Alert_Notify_Work.[clock seconds].[pid] w]
puts $f_out $msgBody
set f_in [open /home/hci/Alert.txt r]
fcopy $f_in $f_out
close $f_in
close $f_out