Thanks in advance for any help
-Al
You would wrap the contortions done in shell
with more contortions to make tcl behave like shell.
Determine what it ( the script ) is supposed to do
and write it anew.
What about using this shellscript from tcl by way of [exec]
uwe
Hi Uwe,
Thanks for responding!
It's a shell script that is interactive in nature in the form of
prompts. It takes user input of disk drives in a RAID in a single line
ie. sda1 sdb1 sdc1... and writes a large file to each then compares the
file written to the reference. It then deletes the file from each
drive.
If I were to "exec" that in TCL I would have to capture the stdout to a
text box - I'm using wish8.4.
bash script is as follows:
BEGIN BASH SCRIPT
**************************
#!/bin/sh
# Script to test file on raid or jbod storage device
yes="y"
declare -a devices
testscript="RAID/JBOD SCSI file test script - rev A"
echo $testscript
echo "WOULD YOU LIKE TO INSTALL NAC DRIVER (enter y for yes n for no)
THEN ENTER"
read insdriver
if [ $insdriver = $yes ]; then
./install
fi
# ****** GATHER INFORMATION FOR TEST ******
echo "WOULD YOU LIKE TO DO FILE TESTING (enter y for yes n for no) THEN
ENTER"
read filetest
if [ $filetest = $yes ]; then
echo "SPECIFY FILE SIZE TO TEST (100 = 100Mbyte) THEN ENTER"
read filesize
echo "files size to create is $filesize Mbyte"
echo "WOULD YOU LIKE TO COPY A FILE FROM HOST TO STORAGE (enter y for
yes n for no) THEN ENTER"
read filecopy
echo "WOULD YOU LIKE TO DELETE THE FILE FROM STORAGE WHEN TESTING IS
COMPLETE (enter y for yes n for no) THEN ENTER"
read filedelete
# ****** GET STORAGE DEVICES TO WRITE FILE ******
echo "ENTER STORAGE DEVICE NAMES TO RUN THE FILE TEST ON ONE LINE
(ex:sda1 sdb5 sdd6) THEN ENTER"
read -a devices
devicecount=${#devices[@]}
index=0
while [ "$index" -lt "$devicecount" ]
do
echo ${devices[$index]}
let "index = $index +1"
done
# **** clear out test1.txt file ****
cp zero.txt test2.txt
# **** create file of size filesize use testbase.txt to generate
test1.txt
echo "creating test file... this takes a couple of minutes"
while [ $filesize -gt 0 ]
do
cat
testbase2.txt>>/home/root/DDC/latest/FC7901xS1/ddk/applications/test2.txt
filesize=$((filesize-1))
done
echo "test file test2.txt is created"
# **** copy file to RAID or JBOD? ********
if [ $filecopy = $yes ];then
# **** mount storage copy file then sync and compare files
index=0
while [ "$index" -lt "$devicecount" ]
do
echo "mounting ${devices[$index]}"
echo "mounting
${devices[$index]}">>/home/root/DDC/latest/FC7901xS1/ddk/applications/filetest.txt
mount -t ext2 -v /dev/${devices[$index]}
/new_root>>/home/root/DDC/latest/FC7901xS1/ddk/applications/filetest.txt
echo "copying test file to ${devices[$index]}"
cp test2.txt /new_root
echo "sync disks"
sync
echo "comparing files now (no output if files are the same)"
cmp -l test2.txt /new_root/test2.txt
echo "COMPARE COMPLETE for ${devices[$index]}"
echo "unmount ${devices[$index]}"
umount -v /new_root
let "index = $index + 1"
done
fi
echo "***** FILE TEST COMPLETE *****"
# **** delete file from RAID or JBOD? *******
if [ $filedelete = $yes ];then
index=0
while [ "$index" -lt "$devicecount" ]
do
echo "mounting ${devices[$index]}"
echo "mounting
${devices[$index]}">>/home/root/DDC/latest/FC7901xS1/ddk/applications/filetest.txt
mount -t ext2 -v /dev/${devices[$index]}
/new_root>>/home/root/DDC/latest/FC7901xS1/ddk/applications/filetest.txt
echo "deleting file test2.txt from ${devices[$index]}"
rm /new_root/test2.txt
echo "test2.txt is now removed from ${devices[$index]}"
echo "unmount ${devices[$index]}"
umount -v /new_root
let "index = $index + 1"
done
fi
fi
END BASH SCRIPT
*************************
That's the whole of it. This is why I don't want to start from
scratch. If you think it's better to start from scratch upon seeing
this, let me know,
> That's the whole of it. This is why I don't want to start from
> scratch. If you think it's better to start from scratch upon seeing
> this, let me know,
>
Interesting. When someone says "large" to me I think of 10's of
thousands of lines of code. It looks like that script could be rewritten
in under an hour, easily (assuming you know Tcl to begin with). If I
were you, I'd start from scratch.
Besides, if you're creating a GUI there's no reason to prompt the user
sequentially -- give them some widgets and an "OK" or "Execute" button.
--
Bryan Oakley
http://www.tclscripting.com
> It's a shell script that is interactive in nature in the form of
> prompts. It takes user input of disk drives in a RAID in a single line
> ie. sda1 sdb1 sdc1... and writes a large file to each then compares the
> file written to the reference. It then deletes the file from each
> drive.
>
> If I were to "exec" that in TCL I would have to capture the stdout to a
> text box - I'm using wish8.4.
>
A question first: do you just aim for having the output
viewable in some gui widget?
and does not look too bad;-)
1. rewrite in tcl:
you will have to [exec]
the system stuff "mount .."
and be root for that
the yes/no and name stuff
would be handled by entry and checkbox widgets
about 1/3 of your script is putting info
to the user and getting an answer.
The rest seems to be simple repeat loops.
2. wrap the script with expect
try "man expect"
expect is a chat programm on steroids
based on tcl. Actually it is a standard
tclinterpreter with a binary extension
facilitating driving other processes through
a pty
[spawn] your shellscript
and then loop through
[expect] "what the script says"
and answer by [exp_send] "what the script expects"
you can handle all output with tcl and place it
in widgets of your liking.
depending on how you mind meshes with how expect works
the first or the second might seem easier to you.
uwe
--
Uwe Klein [mailto:uwe-...@foni.net]
KLEIN MESSGERAETE Habertwedt 1
D-24376 Groedersby b. Kappeln, GERMANY
phone: +49 4642 920 123 FAX: +49 4642 920 125
Like I said, "assuming you know Tcl to begin with". If you don't know
Tcl, why do you want to rewrite it?
However, as simple as that script is, I'd wager that even if you don't
know Tcl and Tk, you could probably re-implement that script in less
than a day if you are a professional programmer.
there are enough eyes in comp.lang.tcl to lead
you into tcl-proficiency.
uwe
set yes "y"
exec declare -a devices
set testscript "RAID/JBOD SCSI file test script - rev A"
puts $testscript
puts "WOULD YOU LIKE TO INSTALL NAC DRIVER (enter y for yes n for no)
THEN ENTER"
set insdriver [gets stdin]
if {$insdriver == $yes } {
exec ./install
}
# ****** GATHER INFORMATION FOR TEST ******
puts "WOULD YOU LIKE TO DO FILE TESTING (enter y for yes n for no) THEN
ENTER"
set filetest [gets stdin]
if { $filetest == $yes } {
puts "SPECIFY FILE SIZE TO TEST (100 = 100Mbyte) THEN ENTER"
set filesize [gets stdin]
puts "files size to create is $filesize Mbyte"
puts "WOULD YOU LIKE TO COPY A FILE FROM HOST TO STORAGE (enter y for
yes n for no) THEN ENTER"
set filecopy [gets stdin]
puts "WOULD YOU LIKE TO DELETE THE FILE FROM STORAGE WHEN TESTING IS
COMPLETE (enter y for yes n for no) THEN ENTER"
set filedelete [gets stdin]
# ****** GET STORAGE DEVICES TO WRITE FILE ******
puts "ENTER STORAGE DEVICE NAMES TO RUN THE FILE TEST ON ONE LINE
(ex:sda1 sdb5 sdd6) THEN ENTER"
set devices [gets stdin]
set devicecount [llength $devices]
for { set i 0} { $i <= $devicecount} {incr i } {
puts [lindex $devicecount $i]
}
# **** clear out test1.txt file ****
file copy zero.txt test2.txt
# **** create file of size filesize use testbase.txt to generate
test1.txt
set fileId [open
"/home/root/DDC/latest/FC7901xS1/ddk/applications/test2.txt" "a"]
puts "creating test file... this takes a couple of minutes"
while { $filesize > 0} {
puts $fileId testbase2.txt
set filesize [expr {$filesize-1}]
}
puts "test file test2.txt is created"
# **** copy file to RAID or JBOD? ********
if {$filecopy == $yes} {
# **** mount storage copy file then sync and compare files
for { set i 0} { $i <= $devicecount} {incr i } {
set devicename [lindex $devices $i]
puts "mounting $devicename"
eval exec { echo "mounting
$devicename">>/home/root/DDC/latest/FC7901xS1/ddk/applications/filetÂest.txt
mount -t ext2 -v /dev/${devices[$index]}
/new_root>>/home/root/DDC/latest/FC7901xS1/ddk/applications/filetest.txt
}
puts "copying test file to $devicename"
file copy test2.txt /new_root
puts "sync disks"
exec sync
puts "comparing files now (no output if files are the same)"
exec cmp -l test2.txt /new_root/test2.txt
puts "COMPARE COMPLETE for $devicename"
puts "unmount $devicename"
exec umount -v /new_root
}
}
puts "***** FILE TEST COMPLETE *****"
# **** delete file from RAID or JBOD? *******
if {$filedelete == $yes} {
for { set i 0} { $i <= $devicecount} {incr i } {
set devicename [lindex $devices $i]
puts "mounting $devicename"
puts "mounting $devicename
>>/home/root/DDC/latest/FC7901xS1/ddk/applications/filetÂest.txt "
eval exec { echo "mounting
$devicename">>/home/root/DDC/latest/FC7901xS1/ddk/applications/filetÂest.txt
mount -t ext2 -v /dev/${devices[$index]}
/new_root>>/home/root/DDC/latest/FC7901xS1/ddk/applications/filetest.txt
}
puts "deleting file test2.txt from $devicename"
file delete /new_root/test2.txt
puts "test2.txt is now removed from $devicename"
puts "unmount $devicename"
exec umount -v /new_root
devices is later used as list (and inside of tcl only).
>
>
uwe
If the user enters anything which is not a proper tcl list, this line
will trigger an TCL error. Better use something along the line of:
set devicelist [list]
foreach item [split [gets stdin]] {
set dev [string trim $dev]
if {[string length $dev] > 0} {
lappend devicelist $dev
}
}
puts [join $devicelist "\n"]
| puts "creating test file... this takes a couple of minutes"
| while { $filesize > 0} {
| puts $fileId testbase2.txt
| set filesize [expr {$filesize-1}]
| }
This writes the literal string "testbase2.txt" $filesize times into
the fileid. It does not even look at the contents of the
testbase2.txt file. Use
exec cat testbase2.txt >> destination
instead of the 'puts' to get identical functionality of your bash
script.
| eval exec { echo "mounting
|
| $devicename">>/home/root/DDC/latest/FC7901xS1/ddk/applications/filetÂest.txt
|
| mount -t ext2 -v /dev/${devices[$index]}
|
| /new_root>>/home/root/DDC/latest/FC7901xS1/ddk/applications/filetest.txt
| }
- avoid 'eval exec' unless you know what you're doing
- indexing a list is done via lindex, not via [].
set fd [open /home/root/DDC/latest/FC7901xS1/ddk/applications/filetÂest.txt a+]
puts $fd "mounting $devicename"
close $fd
exec mount -t ext2 -v /dev/[lindex $devicelist $i]
As an alternative to
for { set i 0} { $i <= $devicecount} {incr i } {
... your code ... [lindex $devicelist $index]
}
you could simply use
foreach dev $devicelist {
... your code ... $dev
}
| puts "comparing files now (no output if files are the same)"
| exec cmp -l test2.txt /new_root/test2.txt
You need to 'catch' this since cmp will exit non-zero when the files
differ, otherwise your script stops here.
if {[catch {exec cmp -l test2.txt /new_root/test2.txt} msg]} {
puts stderr $msg
}
| eval exec { echo "mounting
|
| $devicename">>/home/root/DDC/latest/FC7901xS1/ddk/applications/filetÂest.txt
|
| mount -t ext2 -v /dev/${devices[$index]}
|
| /new_root>>/home/root/DDC/latest/FC7901xS1/ddk/applications/filetest.txt
| }
See above.
HTH
R'
-Al
I tried the mod you suggested and get the following from the error log
can't read "dev": no such variable
can't read "dev": no such variable
while executing
"string trim $dev"
("foreach" body line 2)
invoked from within
"foreach item [split [gets stdin]] {
set dev [string trim $dev]
if {[string length $dev] > 0} {
lappend devices $dev
}
}"
Looks like dev is not in scope. Is this intedded to work with a STDIN
entry like "sda1 sdb1 sdc1" ? This is how the devices would be
entered. The space will have to be trimmed and each device will have
to be an entry that's accessible in code. Would an array work or is
this better?
Thanks,
-Al
Juan Carlos---
set devicelist [list]
foreach item [split [gets stdin]] {
set dev [string trim $dev]
if {[string length $dev] > 0} {
lappend devicelist $dev
}
}
puts [join $devicelist "\n"]
I still get a problem with cant read dev: no such variable. I don't
get it. This is so very basic. I'm sure I just need fresh (and more
experienced) eyes on this
uwe
Sorry, that line should not have been there any more. The 'string
trim' is unneccessary here, since after 'split' the individual
elements should not have leading or trailing whitespace... after all,
that's what 'split' is there for :-)
| Is this intedded to work with a STDIN entry like "sda1 sdb1 sdc1" ?
Yes. This splits the line by whitespace and checks each entry. Note
that tcl 'split' returns an empty list element when successive spaces
are entered, so you have to handle those.
Corrected loop would be
set devices [list]
foreach dev [split [gets stdin]] {
# ignore empty list elements
if {[string length $dev] > 0} {
lappend devices $dev
}
}
Probably you would have to check after that loop whether any devices
were entered:
if {[llength $devices] == 0} {
# no devices entered
}
R'