When i am executing it it says can't read s0 not such variable. Please - global-variables

proc ok { } {
exec echo "LIST OF TOKENS REQUESTED TO BE KILLED BY THE USER" >killed_file; #the information will be dumped in your a file named killed_file
global s0 s1 s2 s3 s4 s5
if {$s0} {exec echo "$s0 choice a" >>killed_file}
if {$s1} {exec echo "$s1 choice b" >>killed_file}
if {$s2} {exec echo "$s2 choice c" >>killed_file}
if {$s3} {exec echo "$s3 choice d" >>killed_file}
if {$s4} {exec echo "$s4 choice e" >>killed_file}
if {$s5} {exec echo "$s5 choice f" >>killed_file}
destroy .top
}
executing it says can't read s0 no such variable and same for other variables.

Presumably the variables s0 to s5 have not been set.
If you want to skip them when they do not exists you can use the command info exists name to test this. Also you are using a very strange method to write to your file. A more straightforward alternative (not tested) would be:
proc ok { } {
#the information will be dumped in your a file named killed_file
set kf [open killed_file w]
puts $kf "LIST OF TOKENS REQUESTED TO BE KILLED BY THE USER"
global s0 s1 s2 s3 s4 s5
if {[info exists s0]} {puts $kf "$s0 choice a"}
if {[info exists s1]} {puts $kf "$s1 choice b"}
if {[info exists s2]} {puts $kf "$s2 choice c"}
if {[info exists s3]} {puts $kf "$s3 choice d"}
if {[info exists s4]} {puts $kf "$s4 choice e"}
if {[info exists s5]} {puts $kf "$s5 choice f"}
close $kf
destroy .top
}

You probably should pass these in as arguments, shouldn't use 'exec' to write to a file, etc...
Below is my approach, which is bad and I'd not code in a real project, but much better than 'globals' and system calls to write to files:
proc ok {s0 s1 s2 s3 s4 s5} {
set FH [open killed_file 'w']
puts $FH "LIST OF TOKENS REQUESTED TO BE KILLED BY THE USER"
if {$s0} {puts $FH "$s0 choice a"}
if {$s1} {puts $FH "$s1 choice b"}
if {$s2} {puts $FH "$s2 choice c"}
if {$s3} {puts $FH "$s3 choice d"}
if {$s4} {puts $FH "$s4 choice e"}
if {$s5} {puts $FH "$s5 choice f"}
close $FH
destroy .top
}
If you give more details on what you are trying to achieve, we may be able to give a bit more help.

Related

Error: can't read server: no such variable when using ltk remotely

I am tinkering around with ltk as it provides the option of running a remote GUI. However, when trying to use the remote GUI I run into issues I do not encounter when running ltk locally:
(in-package :ltk-user)
(defun add-current-investigation-frame (master)
(let* ((frame (make-instance 'frame :master master :width 100 :height 100))
(topic-label (make-instance 'label :text "Current Investigation" :master frame))
(project-label (make-instance 'entry :text "N/A" :master frame))
(action-button (make-instance 'button
:master frame
:text "new investigation")))
(setf (command action-button) (lambda ()
(format t "test~%")
(let ((next-project (nth (random 3) '("A" "B" "N/A"))))
(setf (text project-label) next-project))))
(pack frame)
(pack topic-label :side :top)
(pack project-label :side :top)
(pack action-button :side :top)))
(defun create-main-view ()
(let ((wrapper-frame (make-instance 'frame :master nil)))
(pack wrapper-frame)
(add-current-investigation-frame wrapper-frame)))
(defun create-remote-view (&optional (port 8888))
(Ltk:with-remote-ltk port ()
(create-main-view)))
(defun create-local-view ()
(with-ltk ()
(create-main-view)))
When running (create-local-view) everything works fine and the content of the entry widget changes randomly.
When running (create-remote-view) I get the error message can't read server: no such variable. Why does this error occur and how can I fix this?
I am using the remote.tcl deployed by quicklisp:
#!/usr/bin/wish
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
wm withdraw .
set host localhost
if {[llength $argv] == 2} {
set host [lindex $argv 0]
set port [lindex $argv 1]} else {
set port [lindex $argv 0]}
#puts "connecting to $host $port"
set server [socket $host $port]
set wi [open "|wish" RDWR]
fconfigure $server -blocking 0
fconfigure $wi -blocking 0
fileevent $server readable {set txt [read $server];puts $wi "$txt";flush $wi}
fileevent $wi readable {
if {[eof $wi]} {
close $wi
exit} else {
set txt [read $wi]; puts -nonewline $server $txt;flush $server}}
So I spent some time reading and testing the code, and it appears that it works better with remote-client.tcl than remote.tcl. When working with ltk-remote.lisp, the Lisp side creates a server that may accept multiple clients, each client being a tcl/tk interpreter.
lisp <=== socket stream ===> [ server socket ]
^
|
(wish interpreter)
The lisp side expects the interpreter to maintain a global variable named server. In the case of a local interpreter, this is done in init-wish, where there is set server stdout. In the case of a remote wish, it is expected that the wish interpreter sets this variable itself.
This is the case with remote-client.tcl, and the test applications works well (e.g. ltk-remote::lrtest), except that it adds a .status widget which is never removed. It should be possible to clean up a bit the remote-client.tcl script.
In the case of remote.tcl, the interpreter opens a pair of streams to another wish process:
set wi [open "|wish" RDWR]
It also connects to a server (variable server), and copies inputs from the server to the wish process. Unfortunately, the embedded wish process does not define a server variable:
lisp <=== socket stream ===> [ server socket ]
^
|
(wish interpreter 1)
"server" variable
|
"wi" variable
^
| pipe connection
v
(wish interpreter 2)
no "server" variable
If however you set server to stdout, as explained in the other answer, this assignment is evaluated in the second wish interpreter. The output is sent back to the first wish interpreter, which copies the answer back to the lisp server.
Instead of going through another wish interpreter, I tested locally by using a modified remote-client.tcl that doesn't add any widget:
package require Tk
set host localhost
set port 19790
set server ""
if {[llength $argv] > 0} {
set host [lindex $argv 0]
}
if {[llength $argv] > 1} {
set port [lindex $argv 1]
}
if {[catch {global server; global host; global port; set server [socket $host $port]}]} {
tk_messageBox -icon error -type ok -title "Connection failed!" -message "Cannot connect to server $host port $port."
exit
}
fconfigure $server -blocking 0 -translation binary -encoding utf-8
fileevent $server readable [list sread $server]
set buffer ""
proc getcount {s} {
if {[regexp {^\s*(\d+) } $s match num]} {
return $num
}
}
proc getstring {s} {
if {[regexp {^\s*(\d+) } $s match]} {
return [string range $s [string length $match] end]
}
}
proc process_buffer {} {
global buffer
global server
set count [getcount $buffer]
set tmp_buf [getstring $buffer]
while {($count > 0) && ([string length $tmp_buf] >= $count)} {
set cmd [string range $tmp_buf 0 $count]
set buffer [string range $tmp_buf [expr $count+1] end]
if {[catch $cmd result]>0} {
tk_messageBox -icon error -type ok -title "Error!" -message $result
puts $server "(error: \"$result\")"
flush $server
close $server
exit
}
set count [getcount $buffer]
set tmp_buf [getstring $buffer]
}
}
proc sread {server} {
global buffer
if {[eof $server]} {
tk_messageBox -icon info -type ok -title "Connection closed" -message "The connection has been closed by the server."
close $server
exit
} else {
set txt [read $server];
set buffer "$buffer$txt"
process_buffer
}
}
This is a preliminary answer as I am not entirely sure that this fix does not break anything. I will update this answer in the future to report back on encountered issues. But for now this fixes the issue.
In ltk.lisp there is a function called init-wish which requires an additional line (send-wish "set server stdout")
(defun init-wish ()
(send-lazy
;; print string readable, escaping all " and \
;; proc esc {s} {puts "\"[regsub {"} [regsub {\\} $s {\\\\}] {\"}]\""}
;(send-wish "proc esc {s} {puts \"\\\"[regsub -all {\"} [regsub -all {\\\\} $s {\\\\\\\\}] {\\\"}]\\\"\"} ")
;(send-wish "proc escape {s} {return [regsub -all {\"} [regsub -all {\\\\} $s {\\\\\\\\}] {\\\"}]} ")
(send-wish "package require Tk")
;;; PUT MISSING LINE HERE
(send-wish "set server stdout")
;;; PUT MISSING LINE HERE
(flush-wish)
#+:tk84
(send-wish "catch {package require Ttk}")
#-:tk84
(send-wish "if {[catch {package require Ttk} err]} {tk_messageBox -icon error -type ok -message \"$err\"}")
(send-wish "proc debug { msg } {
global server
puts $server \"(:debug \\\"[escape $msg]\\\")\"
flush $server
} ")
; more code ....
))
Explanation: The function seems to set up the wish interface and actions (confirmed by inserting prints in the remote.tcl). However, as one can see server is referenced in all procs yet it is never declared if we consider all those declarations to be in their own namespace. Consequently, the missing server has to be defined. As all the output is read by fileevent $wi ... and then passed on further, defining server as stdout seemed the most sensible.
It seems to work, however I have no clue if this breaks other stuff

How to make an If-Then-Else statement that accepts only Capitals

I have a UNIX script written in korn shell. I need to make it so that this statement:
while true
do
echo "What is the last name of the person you would like to modify:"
read last_name
if line=$(grep -i "^${last_name}:" "$2")
then
IFS=: read c1 c2 c3 c4 rest <<< "$line"
echo -e "Last Name: $c1\nFirst Name: $c2\nState: $c4"
while true
do
echo "What would you like to change the state to?:"
read state
if [[ $state -eq [A-Z] ]];then
echo "State: $state"
echo "This is a valid input"
break
else
echo "Not a valid input:"
fi
done
else
echo "ERROR: $last_name is not in database"
echo "Would you like to search again (y/n):"
read delete_choice
case $delete_choice in [Nn]) break;; esac
fi
done
;;
Specifically, I am having trouble with this code:
if [[ $state -eq [A-Z] ]];then
The point of this program is to modify a record in a text file but will only take the input of state abbreviations such as (MI, WA, KS, ....).
Try something like:
if echo $state | egrep -q '^[A-Z]{2}$'
then
...
fi
^[A-Z]{2}$ means your state starts and ends with CAPS alphabets of length two.

Trying to write "<<<" in ksh

I am having trouble with the code below:
IFS=: read c1 c2 c3 c4 rest <<< "$line"
Don't get me wrong this code works good but it doesn't seem to be used for ksh. I basically need to write the same code without the "<<<". There is not much info on the "<<<" online. If anybody has any ideas it would be much appreciated.
EDIT:
Ok code is as follows for the entire portion of programming:
m|M)
#Create Modify Message
clear
echo " Modify Record "
echo -en '\n'
echo -en '\n'
while true
do
echo "What is the last name of the person you would like to modify:"
read last_name
if line=$(grep -i "^${last_name}:" "$2")
then
oldIFS=$IFS
IFS=:
set -- $line
IFS=$oldIFS
c1=$1
c2=$2
c3=$3
c4=$4
shift; shift; shift; shift
rest="$*"
echo -e "Last Name: $1\nFirst Name: $2\nState: $4"
while true
do
echo "What would you like to change the state to?:"
read state
if echo $state | egrep -q '^[A-Z]{2}$'
then
echo "State: $state"
echo "This is a valid input"
break
else
echo "Not a valid input:"
fi
done
echo -e "Last Name: $c1\nFirst Name: $c2\nState: $state"
echo "State value changed"
break
else
echo "ERROR: $last_name is not in database"
echo "Would you like to search again (y/n):"
read modify_choice
case $modify_choice in [Nn]) break;; esac
fi
done
;;
Ok so everything works except for the
echo -e "Last Name: $c1\nFirst Name: $c2\nState: $state"
It will just show:
Last Name:
First Name:
State:
So I can see it is not adding it to my echo correctly.
FINAL EDIT
CODE:
#Case statement for modifying an entry
m|M)
#Create Modify Message
clear
echo " Modify Record "
echo -en '\n'
echo -en '\n'
while true
do
echo "What is the last name of the person you would like to modify:"
read last_name
if line=$(grep -i "^${last_name}:" "$2")
then
echo "$line" |
while IFS=: read c1 c2 c3 c4 rest; do
echo -e "Last Name: $c1\nFirst Name: $c2\nState: $c4"
last=$c1
first=$c2
done
while true
do
echo "What would you like to change the state to?:"
read state
if echo $state | egrep -q '^[A-Z]{2}$'
then
echo "State: $state"
echo "This is a valid input"
break
else
echo "Not a valid input:"
fi
done
echo -e "Last Name: $last\nFirst Name: $first\nState: $state"
echo "State value changed"
break
else
echo "ERROR: $last_name is not in database"
echo "Would you like to search again (y/n):"
read modify_choice
case $modify_choice in [Nn]) break;; esac
fi
done
;;
A here string in Bash
command <<<"string"
is basically equivalent to
echo "string" | command
with the obvious exception that the latter uses a pipeline, which means you cannot meaningfully use it with read in particular. A common workaround is to use the set builtin to capture tokens from a string or an external command:
oldIFS=$IFS
IFS=:
set -- $line # no quotes
IFS=$oldIFS
c1=$1
c2=$2
c3=$3
c4=$4
shift; shift; shift; shift
rest="$*" # loses spacing / quoting
Another workaround is to use a loop which iterates just once; this may seem elegant at first, but can lead to rather clunky code if the body of the pseudo-loop is long or complex.
echo "$line" |
while IFS=: read c1 c2 c3 c4 rest; do
: stuff which uses those variables
done
This works around the problem that echo stuff | read variable will run read in a child process and thus immediately forget the value of variable -- the body of the while loop is all the same process in which the read happened, and so the values of the variables it initialized are visible inside the loop.
Another, similar workaround is to delegate the reading and procesIng to a function;
process () {
IFS=: read c1 c2 c3 c4 rest
: stuff which uses those variables
}
echo "$line" | process
Whether this is clunky or elegant depends a lot on what happens in the function. If it's neatly encapsulated, it can be rather attractive; but if you end up passing in a bunch of unrelated variables (or worse, modifying globals inside the function!) it can be quite the opposite.

Keep getting error in if statement in UNIX Script

I have had to write a script that can execute 2 command line arguments to execute task. In addition to a while-loop. I am having trouble with the if statement on line 16. The shell produces the following:
./asg6s: line 16: syntax error near unexpected token fi'
'/asg6s: line 16:fi
My code is as follows:
#check if number of arguments are 2
if [ $# -ne 2 ]; then
echo "Does not equal two arguments"
echo "Usage $0 inputfile outputfile"
exit 1
fi
# check if input file exists
if [ ! -e $1 ]; then
echo "$1 not found!"
exit 1
fi
#check if input file is empty
if [ ! -s $FILE ] ; then
echo "$1 is empty"
exit 1
fi
# copy contents of first file to second
cat $1 > $2
while true
do
clear
# display the menu
echo "University of Maryland."
echo "purpose of using the app"
echo -en '\n'
echo "Choose one of the following:"
echo "1 Addition"
echo "2 Subtraction"
echo "3 Multiplication"
echo "4 Division"
echo "5 Modulo"
echo "0 Exit"
echo -en '\n'
#take input for operation
read N
case $N in
1) NAME="add";OP="+";;
2) NAME="subtract";OP="-";;
3) NAME="multiply";OP="*";;
4) NAME="divide";OP="/";;
5) NAME="modulo";OP="%";;
0) echo "The progam is ending" ; exit 0;;
*) echo “Not an Acceptable entry.” ;continue;
esac
#take input numbers
echo "Enter two numbers"
read A
read B
#display value on screen and also append in the output file
echo "The operation is to $NAME. The result of $NAME $A and $B is" `expr $A $OP $B`
echo "The operation is to $NAME. The result of $NAME $A and $B is" `expr $A $OP $B` > $2
done
Any help would be appreciated.
Edit:
In the same code above I have been getting a problem with the loop statement. Well I should say the fact that I cannot get the program to print the answer for me after I input to integers into the program. Specifically, it does nothing and goes back to the point where it ask me input for what operation I want to complete. Any help would be appreciated.
Your script is almost working.
After showing the result of the expr your script continues with the while loop and calls clear. If you want to see the result, you must show the result after the clear or read a dummy key input.
Another problem is the variable $OP, that could be a *. When * is evaluated to a set of files, your expr statement will not work.
The shortest changes is adding a read statement and quoting your $OP:
echo "The operation is to $NAME. The result of $NAME $A and $B is" `expr $A "$OP" $B`
echo "The operation is to $NAME. The result of $NAME $A and $B is" `expr $A "$OP" $B` > $2
read dummy
Of course the script can be changed. Do you really want to overwrite $2 with the results or append to the file?
The 2 echo lines can be put together with tee.
I would move the clear to above the while statement and replace the last part of your script with
read A
read B
clear
#display value on screen and also append in the output file
echo "The operation is to ${NAME}. The result of ${NAME} $A and $B is $(expr $A "${OP}" $B)" | tee -a $2
echo
done

Tcl - Recursive walking and FTP upload

How can I do a recursive walk through a local folder in order to upload everything it has inside it to the desired ftp folder? Here's what I have so far :
package require ftp
set host **
set user **
set pass **
set ftpdirectory **
set localdirectory **
proc upload {host user pass dir fileList} {
set handle [::ftp::Open $host $user $pass]
ftpGoToDir $handle $dir
# some counters for our feedback string
set j 1
set k [llength $fileList]
foreach i $fileList {
upload:status "uploading ($j/$k) $i"
::ftp::Put $handle $i
incr j
}
::ftp::Close $handle
}
#---------------
# feedback
#---------------
proc upload:status {msg} {
puts $msg
}
#---------------
# go to directory in server
#---------------
proc ftpGoToDir {handle path} {
::ftp::Cd $handle /
foreach dir [file split $path] {
if {![::ftp::Cd $handle $dir]} {
::ftp::MkDir $handle $dir
::ftp::Cd $handle $dir
}
}
}
proc watchDirChange {dir intv {script {}} {lastMTime {}}} {
set nowMTime [file mtime $dir]
if [string eq $lastMTime ""] {
set lastMTime $nowMTime
} elseif {$nowMTime != $lastMTime} {
# synchronous execution, so no other after event may fire in between
catch {uplevel #0 $script}
set lastMTime $nowMTime
}
after $intv [list watchDirChange $dir $intv $script $lastMTime]
}
watchDirChange $localdirectory 5000 {
puts stdout {Directory $localdirectory changed!}
upload $host $user $pass $ftpdirectory [glob -directory $localdirectory -nocomplain *]
}
vwait forever
Thanks in advance :)
You're already using the ftp package, so that means you've got tcllib installed. Good. That means in turn that you've got the fileutil package as well, and can do this:
package require fileutil
# How to do the testing; I'm assuming you only want to upload real files
proc isFile f {
return [file isfile $f]
}
set filesToUpload [fileutil::find $dirToSearchFrom isFile]
The fileutil::find command is very much like a recursive glob, except that you specify the filter as a command instead of via options.
You might prefer to use rsync instead though; it's not a Tcl command, but it is very good and it will minimize the amount of data actually transferred.

Resources