How would I add imformation to an "if" statement? - tcl

if { $chan == $chan_(1) || $chan == $chan_(2) && $nick == $ircbotnick } { .....more code
The chan is defined with set chan_(1) "#1" etc
but when I add additional fields ie $chan == $chan_(3) that third or 4th channel fails to work as the first two do. It's supposed to pick up irc chat announces but it works as I posted but when I add more channels to it it fails on the added channels and only works with the 2 original channels 1 and 2.
Any help would be appreciated please.

Instead of adding more and more $chan == ... conditions, try creating a list and then check for list inclusion with in.
set valid_chans [list $chan_(1) $chan_(2) $chan_(3)]
if {$chan in $valid_chans && $nick == $ircbotnick } {
...
}
Adding additional channels to the valid_chans list is easy to do and the if statement does not need to change.

Related

how to set if capture search message is empty

bind pub - "!find" pub:cari
proc pub:cari { nick host hand chan text } {
set judul [lindex $text 0]
if { $judul == ""} {
puthelp "notice $nick :ketik !find <penyanyi/artis>"
return
} else {
putquick "notice $nick :being processed $judul"
catch [list exec find /home/gusman/mp3 -name "*$judul*" -type f -printf "%f\n"] data
putserv "notice $nick :!putar $data"
putserv "notice $nick :copy paste di channel !putar $data"
}
}
putlog "find.tcl"
if sought is in the data
it works well to post to the target.
if the searched in data is empty, it does not work, please indicate whether this scripts is incomplete or wrong.
I've got the solution on this line:
if { $judul == ""}
Does something like this work?
proc pub:cari { args } {
lassign $args nick host hand chan text
...
Normally in TCL if you don't pass enough arguments to a function then you'll get an error. If you don't know how many arguments your function will take, you can name the last argument "args", and any extra arguments will be put in this variable as a list.

Tcl Output redirection from stdout to a file

I know this question has been asked several times here. I have looked at the responses, but couldn't figure out the way it worked.Can you please help me understand.
Here it goes:
I'm trying to source a tcl script on the tclsh command line, and I want to redirect the output of that script into a file.
$ source my_script.tcl
The script my_script.tcl is something like this:
set output_file final_result
set OUT [open $output_file w]
proc calculate{} {
<commands>
return $result
}
foreach value [calculate] {
puts $output_file "$value"
}
This script still throws out the output onto the stdout, while I expected it to redirect the output into a file specified as "final_result"
Can you please help me understand where I went wrong ?
As you've described here, your program looks OK (apart from minor obvious issues). Your problem is that calculate must not write to stdout, but rather needs to return a value. Or list of values in your case, actually (since you're stuffing them through foreach).
Thus, if you're doing:
proc calculate {} {
set result {}
puts [expr {1 + 2 * 3}]
return $result
}
Then you're going to get output written stdout and an empty final_result (since it's an empty list). If you change that to:
proc calculate {} {
set result {}
lappend result [expr {1 + 2 * 3}]
return $result
}
then your code will do as expected. That is, from puts to lappend result. This is what I recommend you do.
You can capture “stdout” by overriding puts. This is a hack!
rename puts _puts
proc puts {args} {
# Detect where we're running. IMPORTANT!
if {[info level] > 1 && [lindex [info level -1] 0] eq "calculate"} {
upvar 1 result r
lappend r [lindex $args end]
} else {
_puts {*}$args
}
return
}
I'm not convinced that the code to detect whether to capture the value is what it ought to be, but it works in informal testing. (It's also possible to capture stdout itself by a few tricks, but the least horrible — a stacked “transformation” that intercepts the channel — takes a lot more code… and the other alternatives are worse in terms of subtleties.)
Assuming that calculate doesn't write to stdout, and all the other good stuff pointed out and suggested by #DonalFellows has been done...
You need to change the puts in the main script to
puts $OUT "$value"
The script as posted writes to a channel named final_result which almost certainly doesn't exist. I'd expect an error from the puts statement inside the foreach loop.
Don't forget to close the output file - either by exiting from the tclsh interpreter, or preferrably by executing
close $OUT
before you check for anything in it,

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;
}
}

Is there a way to use wildcards in '==' test in tcl?

This might not be possible but is there a way to pass a regular expression in tcl.
I have this function that i cannot change which you pass in a string if finds something and compares them to see if they are equal.
proc check {a } {
// find b in the database
if {$a == $b} {
puts "equals"
} {
puts "Not equals"
}
}
The problem is that the function uses '=='. this only matches if they are exact, but i need to have wild cards in 'a' so that 'a' and 'b' are equal if 'a' and 'b' start with the same words.
I have this function that i cannot change
Why? In tcl, you could easily redefine it with
proc check {a } {
# find b in the database
if {[string match -nocase $a $b]} {
puts "equals"
} {
puts "Not equals"
}
}
Or you could redefine if, although not recommended.
You could even search and replace the if line at runtime with
proc check [info args check] [string map {
{if {$a == $b}} {if {[string match -nocase $a $b]}}
} [info body check]]
So: Why can't you change the function?
The behavior of the == operator is fixed; it always does an equality test. Indeed it does an equality test that prefers to do numeric equality and only falls back to string equality if it has no other way.
Therefore, to change the behavior of check you have to get really tricky.
I'd look at using execution traces to intercept something inside of check so that you can then put a read trace on the local a variable so that when you read it you get actually whether its value matches something according to complex rules. (b can probably just hold a 1 for boolean truth.) The code to do this is sufficiently mind-bendingly complex that I'd really try to avoid doing it!
Much easier, if you can, is to redefine proc so that you can put a prefix on the body of check so you can apply the trace there. Or even massage the test itself.
# Rename the command to something that nothing else knows about (tricky!)
rename proc my_real_proc
my_real_proc proc {name arguments body} {
# Replace a simple equality with a basic no-case pattern match
if {$name eq "check"} {
set body [string map {{$a == $b} {[string match -nocase $b $a]}} $body]
}
# Delegate to the original definition of [proc] for everything else
uplevel my_real_proc $name $arguments $body
}
So long as you run that code before the definition of check, you'll change the code (without “changing the code”) and get the sort of capabilities you want.

error: [eof $FILE_NAME]

I'm trying to write a script in TCL,
I get an error in the line: ![eof $logfile_fd]
The error is: invalid command name "!0"
What may cause this, and how can I fix it?
if {[file exists $logfile] != 1} {
puts "Error existence of $logfile"
return -1
}
if {[catch {set logfile_fd [open "$logfile" r]} err]} {
puts "Error opening \"$logfile\" $err"
return -1
}
seek $logfile_fd 0
![eof $logfile_fd]
I tried to use another solution:
while {[gets $logfile_fd line] >= 0} {
...do something with $line...
}
But I got an other error:
list element in quotes followed by ")\nindexRecordsRead:" instead of space
whilst
)\nindexRecordsRead:
is some text inside $logfile_fd ... I think TCL tries to executes it or something... It works fine for each other line till this line...
Thanks!
I'm not sure what you are trying to do. eof is testing for an end of file condition - using it "bare" like that doesn't do anything. Tcl is evaluating the [eof $logfile_fd] to 0, and then trying to execute the command !0, which doesn't exist.
It does work if you have something like:
if {![eof $logfile_fd]} {
//do something
}
or, if you want to store the results for later, you can do:
set isEndOfFile [expr {![eof $logfile_fd]}]
But, executing like you are, I'm not aware of any side effects you might be wanting to get without using the return value (other than throwing an error if the file descriptor is invalid).
Just need to put this, before working with $line
set bugfree_line [regexp -all -inline {\S+} $line]