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 { }
Related
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
I am trying to run the following:
exec tail CRON_GBOI_INC_AVG_COMPRESS_20140425_18* | grep -i "status of" | awk -F" " '{ print $NF }'
What it does is it tails the file, grep for the line containing the text status of, which will return a string and then return the last character in the string.
However, Tcl always throws the following error:
missing close-bracket or close-brace while compiling that line.
How can I change the code to do what I need to achieve? Is it at all possible with Tcl?
Tcl's syntax is not the shell's syntax. The conversion of that line would be:
exec tail {*}[glob CRON_GBOI_INC_AVG_COMPRESS_20140425_18*] | \
grep -i "status of" | awk "-F " {{ print $NF }}
That's to say, the globbing is explicit, the double quotes are round whole words, and the single quotes are changed to braces. (It's also broken over 2 lines with a backslash-newline sequence for clarity.)
Sirs,
As it happens, Tcl string handling may make this easier:-
set stringBack [exec tail [lindex $argv 0] | grep -i "[lindex $argv 1]" ]
set wanted [string index $stringBack end]
puts "stat chr is $wanted"
.
We run as, say,
./charget /path/to/file 'text hook we choose'
with file and text hook as parameters (quoting the text hook string for handoff to grep insensitive).
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.
I would like to get the line number using grep command, but I am getting the error message when search pattern is not a single word:
couldn't read file "Pattern": no such file or directory
How should be the proper usage of the grep? The code is here:
set status [catch {eval exec grep -n '$textToGrep' $fileName} lineNumber]
if { $status != 0 } {
#error
} else {
puts "lineNumber = $lineNumber"
}
Also if the search pattern is not matched at all, the returned value is : "child process exited abnormally"
Here is the simple test case:
set textToGrep "<BBB name=\"BBBRM\""
file contents:
<?xml version="1.0"?>
<!DOCTYPE AAA>
<AAA>
<BBB name="BBBRM" />
</AAA>
Well, I also get problems with your code and a single word pattern!
First of all, I don't think you need the eval command, because catch itself does an evaluation of its first argument.
Then, the problem is that you put the $textToGrep variable in exec inside single quotes ', which have no meaning to Tcl.
Therefore, if the content of textToGrep is foo, you are asking grep to search for the string 'foo'. If that string, including the single quotes, is not found in the file, you get the error.
Try to rewrite your first line with
set status [catch {exec grep -n $textToGrep $fileName} lineNumber]
and see if it works. Also, read the exec man page, which explains well these problems.
If your system has tcllib install, you can use the fileutil::grep command from the fileutil package:
package require fileutil
set fileName data.xml
set textToGrep {<BBB +name="BBBRM"}; # Update: Add + for multi-space match
set grepResult [::fileutil::grep $textToGrep $fileName]
foreach result $grepResult {
# Example result:
# data.xml:4: <BBB name="BBBRM" />
set lineNumber [lindex [split $result ":"] 1]
puts $lineNumber
# Update: Get the line, squeeze the spaces before name=
set line [lindex [split $result ":"] 2]
regsub { +name=} $line " name=" line
puts $line
}
Discussion
When assigning value to textToGrep, I used the curly braces, thus allowing double quote inside without having to escape them.
the result of the ::fileutil::grep command is a lits of strings. Each string contains the file name, line number, and the line itself; separated by colon.
One way to extract the line number is to first split the string (result) into pieces, using the colon as a separator. Next, I use lindex to grab the second item (index=1, since list is zero-base).
I have updated the code to account for case where there are multiple spaces before name=
There are two problems here:
Pattern matching does not work.
grep exits with error child process
exited abnormally when pattern is not found
The first problem is because you are not enclosing the textToGrep within double quotes(instead of single quotes). So your code should be:
[catch {exec grep -n "$textToGrep" $fileName} lineNumber]
Second problem is because of the exit status of grep command. grep exits with error when the pattern is not found. Here is the try on a shell:
# cat file
pattern
pattern with multiple spaces
# grep pattern file
pattern
pattern with multiple spaces
# echo $?
0
# grep nopattern file
# echo $?
1
EDIT:
In your case you have special characters such as < and > (which have special meaning on a shell).
set textToGrep "<BBB name=\"BBBRM\""
regsub -all -- {<} "$textToGrep" "\\\<" textToGrep
regsub -all -- {>} "$textToGrep" "\\\>" textToGrep
set textToGrep {\<BBB name="BBBRM"}
catch {exec grep -n $textToGrep $fileName} status
if {![regexp "child process" $status]} {
puts $status
} else {
puts "no word found"
}
I think you should do regular expression with child process. Just check above code if it works. In if statement you can process the status command as you like.
With the given example (in your post) the above code works only you need to use backslash for the "<" in the textToGrep variable
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