Tcl: can catch { exec } know whether a final newline was output? - tcl

Consider the following:
% catch { exec echo "test" } result
0
% catch { exec echo -n "test" } resultnonl
0
% if { $result == $resultnonl } { echo "true" }
true
Question: Is there a way for the two resulting variables to be different?
Use case: I'm retrieving the contents of the clipboard and cannot differentiate between these two cases. In Emacs, it is very common for me to kill (cut) a line without its final newline, and also very common to kill a whole line. The clipboard only differs by the newline.

Check out the -keepnewline flag to exec. Watch:
catch { exec -keepnewline -- echo "test" } result
string length $result

Related

Read a CSV file inside awk script (CLOSED)

I want to use an AWK script without typing in the terminal the CSV file and instead, call that file from inside my code.
Current Input terminal:
./script.awk file.csv
Desired Input Terminal:
./script.awk
On the other hand, here is the script I have done so far:
#!/usr/bin/awk -f
BEGIN{print"Filtered Elements:"}
BEGIN{FS=","}
{ if ($8~/.*5.*/ && $2~/.*Sh.*/ && ($3~/.*i.*/ || $4~/.*s.*/)) { print } }
{ if ($3~/.*ra.*/ && $7~/.*18.*/ && $13~/.*r.*/) { print } }
{ if ($5~/.*7.*/ && $2~/.*l.*/ && ($4~/.*Fi.*/ || $12~/20.*/)) { print } }
} **file.csv**
I aslo tried to do this:
#!/usr/bin/awk -f
BEGIN{print"Filtered Elements:"}
BEGIN{FS=","}
BEGIN{
while (getline < file.csv > 0) {
{ if ($8~/.*5.*/ && $2~/.*Sh.*/ && ($3~/.*i.*/ || $4~/.*s.*/)) { print } }
{ if ($3~/.*ra.*/ && $7~/.*18.*/ && $13~/.*r.*/) { print } }
{ if ($5~/.*7.*/ && $2~/.*l.*/ && ($4~/.*Fi.*/ || $12~/20.*/)) { print } }
}
But either ways an error occurred.
Thank you in advance!
Your second example is a correct getline loop, except that the file path should be quoted to be treated as a string (and not a variable): while (getline < "file.csv" > 0) #....
Alternatively, you can set the script arguments (including input files and variables) by manipulating ARGV and ARGC in a BEGIN block:
BEGIN {
ARGV[1] = "file.csv"
ARGC = 2
}
{
# commands here process file.csv as normal
}
Running this as ./script is the same as if you set the argument with the shell (like ./script file.csv).
An awk script isn't a command you call, it's a set of instructions interpreted by awk where awk IS a command you call. What you're trying to do apparently is write a Unix command that's implemented as a shell script which includes a call to awk, e.g.:
#!/usr/bin/env bash
awk '
{ print "foo", $0 }
' 'file.csv'
Store that in a file named stuff (not stuff.awk or stuff.sh or anything else with a suffix), and then call it as ./stuff or just stuff if the current directory is in your PATH.
Though you technically can use a shebang to call awk directly, don't do it - see https://stackoverflow.com/a/61002754/1745001.

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.

How to suppress a proc's return value in tcl prompt

I'm relatively new in TCL, in TCL prompt, when we invoke a proc with some return value, the proc's return value is echoed back by tcl. Is there a way to stop it (without affecting puts or similar functionality) as an example
bash$ tclsh
% proc a {} { puts "hello"; return 34; }
% a
hello
34
%
Now how do i suppress the 34 coming to the screen? Any help is appreciated.
Update:
Actually the proc is a part of another tool, earlier it did not have any return value, but now conditionally it can return a value.
it can be called from a script and there won't be any problem (as Bryan pointed out). and it can be called from interactive prompt, then after all the necessary outputs, the return value is getting printed unnecessarily.
So 1) I don't have the facility of changing a user's tclshrc 2) existing scripts should continue to work.
And it seems strange that every time the proc is called, after all the necessary outputs, a number gets printed. To a user, this is a needless information unless he has caught the value and wants to do something. So i wanted the value to be delivered to user, but without getting printed to prompt/UI (hope i'm clear )
The interactive shell code in tclsh and wish will print any non-empty result. To get nothing printed, you have to have the last command on the “line” produce an empty result. But which command to use?
Many commands will produce an empty result:
if 1 {}
subst ""
format ""
However, the shortest is probably:
list
Thus, you could write your code like:
a;list
Of course, this only really becomes useful when your command actually produces a large result that you don't want to see. In those cases, I often find that it is most useful to use something that measures the size of the result, such as:
set tmp [something_which_produces a_gigantic result]; string length $tmp
The most useful commands I find for that are string length, llength and dict size.
If you absolutely must not print the result of the command, you have to write your own interactive loop. There are two ways to do this, depending on whether you are running inside the event loop or not:
Without the event loop
This simplistic version just checks to see if the command name is in what the user typed. It's probably not a good idea to arbitrarily throw away results otherwise!
set accum ""
while {[gets stdin line] >= 0} {
append accum $line "\n"
if {[info complete $accum]} {
if {[catch $accum msg]} {
puts stderr $msg
} elseif {$msg ne "" && ![string match *TheSpecialCommand* $accum]} {
puts $msg
}
set accum ""
}
}
With the event loop
This is just handling the blocking IO case; that's the correct thing when input is from a cooked terminal (i.e., the default)
fileevent stdin readable handleInput
set accum ""
proc handleInput {} {
global accum
if {[gets stdin line] < 0} {
exit; # Or whatever
}
append accum $line "\n"
if {[info complete $accum]} {
if {[catch {uplevel "#0" $accum} msg]} {
puts stderr $msg
} elseif {$msg ne "" && ![string match *TheSpecialCommand* $accum]} {
puts $msg
}
set accum ""
}
}
vwait forever; # Assuming you're not in wish or have some other event loop...
How to detect the command is being executed
The code above uses ![string match *TheSpecialCommand* $accum] to decide whether to throw away the command results, but this is very ugly. A more elegant approach that leverages Tcl's own built-in hooks is to use an execution trace to detect whether the command has been called (I'll just show the non-event-loop version here, for brevity). The other advantage of this is that it is simple to extend to suppressing the output from multiple commands: just add the trace to each of them.
trace add execution TheSpecialCommand enter SuppressOutput
proc SuppressOutput args {
# Important; do not suppress when it is called inside another command
if {[info level] == 1} {
set ::SuppressTheOutput 1
}
}
# Mostly very similar from here on
set accum ""
while {[gets stdin line] >= 0} {
append accum $line "\n"
if {[info complete $accum]} {
set SuppressTheOutput 0; # <<<<<< Note this!
if {[catch $accum msg]} {
puts stderr $msg
} elseif {$msg ne "" && !$SuppressTheOutput} { # <<<<<< Note this!
puts $msg
}
set accum ""
}
}
To be clear, I wouldn't ever do this in my own code! I'd just suppress the output manually if it mattered.
You could make an empty procedure in .tclshrc...
proc void {} {}
...and when you don't need a return value, end the line with ;void.
Use tcl_interactive variable to enable the return of of the value, although I'm not sure where this would be useful...
proc a {} {
puts "hello"
if { [info exist tcl_interactive] } {
return {};
} else {
return 34;
}
}

Expect script, neither branch of if gets executed

Can anybody take a look and advice why the following snippet does not execute neither branch of the if statement? Script does not throw any errors. It simply reaches the expect line and waits until it times out.
arg1 is just an argument from command line.
set arg1 [lindex $argv 0]
set strname "teststring"
expect {
"prompt#" {
if { [string compare $arg1 $strname] != 0 } {
send "strings different\r"
} else {
send "strings same\r"
}
}
default abort
}
Thanks in advance.
EDIT:
Edited to show the correct structure.
Expect is an extension of Tcl, and Tcl is very particular about whitespace:
...
# need a space between the end of the condition and the beginning of the true block
if { [string compare $arg1 $strname] != 0 } {
...
# the else keyword needs to be on the same line as the closing bracket of the true block
} else {
...
}
Is your complete script more like this?:
#!/usr/bin/expect -d
set arg1 [lindex $argv 0]
set strname teststring
expect {
"prompt#"
if { [string compare $arg1 $strname] != 0 }{
send "strings different\r";
}
else{
send "strings same\r";
}
default abort
}
Or are you running expect some other way? How exactly are you running expect, your script, with what args?

Expect: how to print every output of a spawned process to the user

I'm trying to write an expect script, part of the commands will be executed as a different user. So i need to spawn an kuu process, then send commands to it after user provided password. But how do I collect the output of those commands and print it to the user who is running the expect scripts?
Thanks
This can be done by using something like the following:
proc outputUntilPrompt {} {
global expect_out
set prompt "ACT:*>*"
set output ""
while 1 {
expect {
-re "(\[^\r]*\)\r\n" {
append output $expect_out(buffer)
}
$prompt {
append output $expect_out(buffer)
break
}
}
}
return $output
}
send_user "$output"