Open file in Tcl with variables inside - shell

I want to open a file called filelist.txt which contains only the string ${PATHFILE}/test.txt, read the line and open the file test.txt. The file test.txt is present inside the folder ~/testfile.
Consider this sample code:
#!/usr/bin/env tclsh
set PATHFILE "~/testfile"
set fp [open "filelist.txt" r]
set lines [split [read $fp] "\n"]
close $fp
foreach line $lines {
set fp1 [open $line r]
close $fp1
}
The problem is that it seems that the "open" command cannot find the PATHFILE variable and i get this error:
couldn't open "${PATHFILE}/test.txt": no such file or directory
If i try to open the file with set fp1 [open "${PATHFILE}/test.txt" r] i don't have any errors.

Yes, you can use the TCL subst command to evaluate the PATHFILE variable. Note that you might still have an issue with the tilde ~ - it may be better to use full path names.
#!/usr/bin/env tclsh
set PATHFILE "~/testfile"
set fp [open "filelist.txt" r]
set lines [split [read $fp] "\n"]
close $fp
foreach line $lines {
set fp1 [open [subst $line] r]
close $fp1
}

Related

tcl insert string at certain line in file

I want to insert one string at the certain line ,and I know the line number.
ex.
#Aa_version = Aa/45.21-a32_1
#Aa_version = Aa/47.21-a33_1
Aa_version = Aa/45.27-a57_2 ->I can get this line number n
and I want to insert one line Aa/49.27-a54_1 at line n+1
and put Aa_version = Aa/45.27-a57_2 -> #Aa_version = Aa/45.27-a57_2
output is like
#Aa_version = Aa/45.21-a32_1
#Aa_version = Aa/47.21-a33_1
#Aa_version = Aa/45.27-a57_2
Aa_version = Aa/49.27-a54_1
and my code is
set Aa ""
set fp [open $file "r+"]
set lines [split [read $fp] \n]
set idx [lsearch -regexp $lines {^Aa_version} ]
regexp {Aa(.+)} [lindex $lines $idx] Aa_version
set old_version "#$Aa_version"
set newAa [gets stdin]
set new_version "Aa_version =$newAa "
puts $old_version ->replace $Aa_version
puts $new_version
close $fp
How can I put them at the correct line
thanks
I think it's easier and cleaner to handle the input file a line at a time and look at each one in turn instead of reading it all in one single go and then finding, altering and inserting elements in a list:
set fp [open $file]
while {[gets $fp line] >= 0} {
# If the line starts with Aa_version...
if {[string match "Aa_version*" $line]} {
# Comment it out
puts "#$line"
# And read the new version and write it out
set newAa [gets stdin]
puts "Aa_version = $newAa"
# Copy the rest of the file to standard output and exit the loop
chan copy $fp stdout
break
} else {
puts $line
}
}
close $fp
But if you want to keep the current list based approach, lreplace is your friend:
set fp [open $file]
set lines [split [read $fp] \n]
close $fp
set idx [lsearch -glob $lines "Aa_version*"]
# If a match was found...
if {$idx >= 0} {
# Read the new verson
set newAa [gets stdin]
# Replace the version element with two new elements:
# Commented out previous version, and new version
set lines [lreplace $lines $idx $idx \
"#[lindex $lines $idx]" "Aa_version = $newAa"]
}
puts [join $lines \n]

wrong # args: should be "foreach varList list ?varList list ...? command"

this is the .txt file
desk-12
desk-123
desk-auto-1234
this is the .expect file
#!/usr/bin/expect
set f [open "listOfIps.txt"]
set ips [split [read $f] "\n"]
close $f
set PASSWORD "test#123"
puts "$ips"
foreach HOST $ips{
expect -> "
puts $HOST
#spawn scp -r /usr/bin/scp /Users/test-123/1.png admin#$HOST:/home/testFolder
expect {
"*password:*"
{ send $PASSWORD\r}
}
}
puts "completed"
can anyone help me how to solve this "wrong # args: should be "foreach varList list ?varList list ...? command"" error
On the line
foreach HOST $ips{
you need to add a space between $ips and { for Tcl to parse this correctly.

How to store a line from expect_out(buffer) to a variable in tcl

For example, if the output ( in expect_out(buffer) )is
blah
blh blah
asdjsudfsdf
how can I store the 2nd line to a variable? so far I have this:
foreach line [split $expect_out(buffer) "\n"] {
if [lindex $line 1] {
set variable $line
}
}
But this does not work, it says the variable variable is undefined. I tried adding a counter, but that didn't work either. There has to be an easier way!
yes there is an easier way:
set lines [split $expect_out(buffer) \n]
set variable [lindex $lines 1]
or in one line
set variable [lindex [split $expect_out(buffer) \n] 1]
Keep mindful you know what Tcl commands return: split returns a list. You then use lindex to find the 2nd element of the list.

Read file into String and do a loop in Expect Script

What I am trying to do is to:
Create a .exp file, which will read from the *.txt file from the same directory and parse all the content in the text file into a string variable in the expect script.
Loop the string, which contains a series of host names, and excecute a series of command until the string is enumerated.
So what the script does, is read a series of hostname from a txt file in the same directory, and then read them into a string, and the .exp file will auto log into each of them and excecute a series of commands.
I have the following code written but it's not working:
#!/usr/bin/expect
set timeout 20
set user test
set password test
set fp [open ./*.txt r]
set scp [read -nonewline $fp]
close $fp
spawn ssh $user#$host
expect "password"
send "$password\r"
expect "host1"
send "$scp\r"
expect "host1"
send "exit\r"
Any help is greatly appreciated....
The code should read the contents of the two files into lists of lines, then iterate over them. It ends up like this:
# Set up various other variables here ($user, $password)
# Get the list of hosts, one per line #####
set f [open "host.txt"]
set hosts [split [read $f] "\n"]
close $f
# Get the commands to run, one per line
set f [open "commands.txt"]
set commands [split [read $f] "\n"]
close $f
# Iterate over the hosts
foreach host $hosts {
spawn ssh $user#host
expect "password:"
send "$password\r"
# Iterate over the commands
foreach cmd $commands {
expect "% "
send "$cmd\r"
}
# Tidy up
expect "% "
send "exit\r"
expect eof
close
}
You could refactor this a bit with a worker procedure or two, but that's the basic idea.
I'd refactor a bit:
#!/usr/bin/expect
set timeout 20
set user test
set password test
proc check_host {hostname} {
global user passwordt
spawn ssh $user#$hostname
expect "password"
send "$password\r"
expect "% " ;# adjust to suit the prompt accordingly
send "some command\r"
expect "% " ;# adjust to suit the prompt accordingly
send "exit\r"
expect eof
}
set fp [open commands.txt r]
while {[gets $fp line] != -1} {
check_host $line
}
close $fp
Using any of the two solutions here, I would also create a logfile that you can view at a later time. Makes it easy to troubleshoot any problems after the script is run, especially if you're configuring several hundred hosts.
Add:
log_file -a [log file name]
Before your loop.
Cheers,
K

How to get the home directory from a variable in expect?

I'd like to set the home directory as a variable.
How can I do that?
This is my code:
set fp [open "$HOME/temp.sh" r]
set data [read $fp]
close $fp
set ::logfile [open "$HOME/log.sh" a]
Since Expect is an extension of Tcl, you use Tcl's env array:
set fp [open "$::env(HOME)/temp.sh" r]
set data [read $fp]
close $fp
set ::logfile [open "$::env(HOME)/log.sh" a]
See http://tcl.tk/man/tcl8.5/TclCmd/tclvars.htm and the Tcl tutorial.

Resources