testing this script (parameters are not real, of course):
package require tdbc::mysql
tdbc::mysql::connection create test_conn -user test_admin -passwd test_passw -db test -host www.test.com -port 3306
set consulta "SELECT * FROM entes"
set sentencia [test_conn prepare $consulta ]
$sentencia foreach row {
puts $row
}
$sentencia close
test_conn close
I get this
Exit code: 3221225477
However, using this
puts [test_conn evaldirect $consulta ]
i get the right lists.
So, is something wrong with "prepare" or have i made a mistake ?
Thanks,
Alejandro
Related
I have expect (tcl) script for automated task working properly - configuring network devices via telnet/ssh. Most of the cases there is 1,2 or 3 command lines to execute, BUT now I have more then 100 command lines to send via expect. How can I achieved this in smart and good scripting way :)
Because I can join all command lines over 100 to a variable "commandAll" with "\n" and "send" them one after another, but I think it's pretty ugly :) Is there a way without stacking them together to be readable in code or external file ?
#!/usr/bin/expect -f
set timeout 20
set ip_address "[lrange $argv 0 0]"
set hostname "[lrange $argv 1 1]"
set is_ok ""
# Commands
set command1 "configure snmp info 1"
set command2 "configure ntp info 2"
set command3 "configure cdp info 3"
#... more then 100 dif commands like this !
#... more then 100 dif commands like this !
#... more then 100 dif commands like this !
spawn telnet $ip_address
# login & Password & Get enable prompt
#-- snnipped--#
# Commands execution
# command1
expect "$enableprompt" { send "$command1\r# endCmd1\r" ; set is_ok "command1" }
if {$is_ok != "command1"} {
send_user "\n### 9 Exit before executing command1\n" ; exit
}
# command2
expect "#endCmd1" { send "$command2\r# endCmd2\r" ; set is_ok "command2" }
if {$is_ok != "command2"} {
send_user "\n### 9 Exit before executing command2\n" ; exit
}
# command3
expect "#endCmd2" { send "$command3\r\r\r# endCmd3\r" ; set is_ok "command3" }
if {$is_ok != "command3"} {
send_user "\n### 9 Exit before executing command3\n" ; exit
}
p.s. I'm using one approach for cheeking is given cmd line is executed successfully but I'm not certain that is perfect way :D
don't use numbered variables, use a list
set commands {
"configure snmp info 1"
"configure ntp info 2"
"configure cdp info 3"
...
}
If the commands are already in a file, you can read them into a list:
set fh [open commands.file]
set commands [split [read $fh] \n]
close $fh
Then, iterate over them:
expect $prompt
set n 0
foreach cmd $commands {
send "$cmd\r"
expect {
"some error string" {
send_user "command failed: ($n) $cmd"
exit 1
}
timeout {
send_user "command timed out: ($n) $cmd"
exit 1
}
$prompt
}
incr n
}
While yes, you can send long sequences of commands that way, it's usually a bad idea as it makes the overall script very brittle; if anything unexpected happens, the script just keeps on forcing the rest of the script over. Instead, it is better to have a sequence of sends interspersed with expects to check that what you've sent has been accepted. The only real case for sending a very long string over is when you're creating a function or file on the other side that will act as a subprogram that you call; in that case, there's no really meaningful place to stop and check for a prompt half way. But that's the exception.
Note that you can expect two things at once; that's often very helpful as it lets you check for errors directly. I mention this because it is a technique often neglected, yet it allows you to make your script far more robust.
...
send "please run step 41\r"
expect {
-re {error: (.*)} {
puts stderr "a problem happened: $expect_out(1,string)"
exit 1
}
"# " {
# Got the prompt; continue with the next step below
}
}
send "please run step 42\n"
...
i've coded an ActiveDirectory logging system a couple of years ago...
it never become a status greater than beta but its still in use...
i got an issue reported and found out what happening...
they are serveral filds in such an ActiveDirectory Event witch are UserInputs, so i've to validate them! -- of course i didnt...
so after the first user got the brilliant idea to use singlequotes in a specific foldername it crashed my scripts - easy injection possible...
so id like to make an update using prepared statements like im using in PHP and others.
Now this is a Powershell Script.. id like to do something like this:
$MySQL-OBJ.CommandText = "INSERT INTO `table-name` (i1,i2,i3) VALUES (#k1,#k2,#k3)"
$MySQL-OBJ.Parameters.AddWithValue("#k1","value 1")
$MySQL-OBJ.Parameters.AddWithValue("#k2","value 2")
$MySQL-OBJ.Parameters.AddWithValue("#k3","value 3")
$MySQL-OBJ.ExecuteNonQuery()
This would work fine - 1 times.
My Script runs endless as a Service and loops all within a while($true) loop.
Powershell clams about the param is already set...
Exception calling "AddWithValue" with "2" argument(s): "Parameter
'#k1' has already been defined."
how i can reset this "bind" without closing the database connection?
id like the leave the connection open because the script is faster without closing and opening the connections when a event is fired (10+ / sec)
Example Code
(shortend and not tested)
##start
function db_prepare(){
$MySqlConnection = New-Object MySql.Data.MySqlClient.MySqlConnection
$MySqlConnection.ConnectionString = "server=$MySQLServerName;user id=$Username;password=$Password;database=$MySQLDatenbankName;pooling=false"
$MySqlConnection.Open()
$MySqlCommand = New-Object MySql.Data.MySqlClient.MySqlCommand
$MySqlCommand.Connection = $MySqlConnection
$MySqlCommand.CommandText = "INSERT INTO `whatever` (col1,col2...) VALUES (#va1,#va2...)"
}
while($true){
if($MySqlConnection.State -eq 'closed'){ db_prepare() }
## do the event reading and data formating stuff
## bild some variables to set as sql param values
$MySQLCommand.Parameters.AddWithValue("#va1",$variable_for_1)
$MySQLCommand.Parameters.AddWithValue("#va2",$variable_for_2)
.
.
.
Try{ $MySqlCommand.ExecuteNonQuery() | Out-Null }
Catch{ <# error handling #> }
}
Change your logic so that the db_prepare() method initializes a MySql connection and a MySql command with parameters. Set the parameter values for pre-declared parameter names in loop. Like so,
function db_prepare(){
# ...
# Add named parameters
$MySQLCommand.Parameters.Add("#val1", <datatype>)
$MySQLCommand.Parameters.Add("#val2", <datatype>)
}
while($true) {
# ...
# Set values for the named parameters
$MySQLCommand.Parameters.SetParameter("#val1", <value>)
$MySQLCommand.Parameters.SetParameter("#val2", <value>)
$MySqlCommand.ExecuteNonQuery()
# ...
}
I've defined a function hello in fishshell:
function hello
echo Hello
end
And save it:
funcsave hello
If I want to delete it, I can delete the file ~/.config/fish/functions/hello.fish.
Is there any other way to do it? (like built-in funcdel or funcrm)
No, there isn't any builtin to remove the file, but you can use:
functions --erase hello
or
functions -e hello
to erase the function definition from the current session.
See also
Documentation
I created another fish function for that
function funcdel
if test -e ~/.config/fish/functions/$argv[1].fish
rm ~/.config/fish/functions/$argv[1].fish
echo 'Deleted function ' $argv[1]
else
echo 'Not found function ' $argv[1]
end
end
The above solution of functions -e hello only deletes hello in the current session. Open another terminal, and the function is still there.
To delete the function in a persistent way, I had to resort to deleting the file ~/.config/fish/functions/hello.fish directly. Up till now, I do not know of another way that does deleting in a persistent way.
a more complete, self defined (and quiet) self-crafted solution, inspired by #Kanzee's answer (copy to file ~/.config/fish/functions/funcdel.fish):
function funcdel --description 'Deletes a fish function both permanently and from memory'
set -l fun_name $argv[1]
set -l fun_file ~/.config/fish/functions/$fun_name.fish
# Delete the in-memory function, if it exists
functions --erase $fun_name
# Delete the function permanently,
# if it exists as a file in the regular location
if test -e $fun_file
rm $fun_file
end
end
I combined the answer from #hoijui with some code from the function funcsave, so you can delete more than one function at once:
function funcdel --description 'Deletes a fish function both permanently and from memory'
set cf (status function)
set -l options 'h/help'
argparse -n funcdel $options -- $argv
or return
# should create a manpage
if set -q _flag_help
__fish_print_help cf
return 0
end
if not set -q argv[1]
printf (_ "%ls: Expected at least %d args, got only %d\n") $cf 1 0
return 1
end
set -l retval 0
for funcname in $argv
set -l funcfile $__fish_config_dir/functions/$funcname.fish
# Delete the in-memory function, if it exists
functions --query -- $funcname
if test $status -eq 0
functions --erase -- $funcname
printf (_ "%s: function %s removed from session\n") $cf $funcname
else
printf (_ "%s: Unknown function '%s'\n") $cf $funcname
end
# Delete the function permanently,
# if it exists as a file in the regular location
if test -e $funcfile
rm $funcfile
printf (_ "%s: %s deleted\n") $cf $funcfile
else
printf (_ "%s: %s not found\n") $cf $funcfile
end
end
return $retval
end
I am trying to create an expect script which will grep a file and return the line which contains the string I am looking for, in this case the string will be a terminal ID. For example I have a file called terminal_list.txt with the following contents:
0x400 192.168.62.133 10006
0x420 192.168.62.133 10021
0x440 192.168.62.133 10022
and I want the line returned which starts with 0x420
My code is as follows:
#!/usr/bin/expect -f
set terminal_list "/home/linkway/terminal_list.txt"
set termid "0x400"
spawn /bin/bash
expect "] "
# Get ip and port of terminal
# Check if termid exists in terminal_list file
set command "grep -q '$termid' '$terminal_list' && echo 'true' || echo 'false'"
send "$command\r"
expect "$command\r\n"
expect -re "(.*)\r\n.*] "
set val $expect_out(1,string)
# If terminal_list does not exist print error and exit
if { [string first "No such file or directory" $val] >= 0 } {
puts "$terminal_list does not exist. Script Failed"
exit
# If terminal_list does not contain termid print error and continue
} elseif { [string match "false" $val] } {
puts "\nTerminal $termid does not exist in ${terminal_list}. Cannot update bootfile.\n"
# If termid is found in terminal_list, get the ip and port of the terminal
} else {
set command "grep '$termid' '$terminal_list'"
send "$command\r"
expect "$command\r\n"
expect -re "(.*)\r\n.*] "
set val $expect_out(1,string)
set ip_port [string range $val 6 end]
}
This works perfectly when I ssh to the RHEL server via putty and run the script in a maximized putty window. HOWEVER when I shrink the window length so that the grep command no longer fits on a single line my code breaks! Can anyone please help me come up with a solution to this? I have been struggling with the processing of expect_out and could really use some guidance.
EDIT: I found what was causing the bug. It turns out that when the grep command is split over multiple lines, a \r is added to the command where the line break is. Here is some debugging info from exp_internal 1. You can see how the \r is added into the grep command where the command ran onto the next line:
expect: does "grep -q '0x400' '/home/linkway/term \rinal_list.txt'
&& echo 'true' || echo 'false'\r\n" (spawn_id exp6)
match glob pattern "grep -q '0x400' '/home/linkway/terminal_list.txt'
&& echo 'true' || echo 'false'\r\n"? no
Why is this happening and what is the proper way to get just the output of the grep command? I find it very strange that expect would behave differently base on how the output of a command is displayed on screen. Any help is greatly appreciated.
I was able to find a cleaner solution to my problem by making my script more expect-like. Here's what it looks like:
set command "grep -q '$termid' '$terminal_list' && echo 'true' || echo 'false'"
send "$command\r"
expect {
-re ".*\r\ntrue\r\n.*] " {
send "grep '$termid' '$terminal_list'\r"
expect ".*\r\n"
expect -re "(.*)\r\n.*] "
set val $expect_out(1,string)
set ip_port [string range $val 6 end]
puts "\nUpdating $termid bootfile"
updatebootfile $ip_port $boot_data $termid
}
-re ".*\r\nfalse\r\n.*] " {
puts "\nTerminal $termid does not exist in ${terminal_list}. Cannot update bootfile.\n"
}
-re "No such file or directory.*] " {
puts "$terminal_list does not exist. Script Failed"
exit 1
}
timeout {
puts "expect timeout when searching for $termid in $terminal_list"
}
}
I have a function that is similar to this:
function A-Function{
[CmdletBinding(SupportsShouldProcess=$True)]
param (
[Parameter(Position=0, HelpMessage="A Test string", Mandatory=$true)]
[string]$Path,
[Parameter(Position=1, HelpMessage="The list of file names to download.", ValueFromPipeline=$True)]
[string[]]$testVar,
[Parameter(HelpMessage= "The username")]
[string]$User,
[Parameter(HelpMessage= "The password")]
[string]$Password,
[Parameter(HelpMessage= "The credentials used.")]
[Net.NetworkCredential]$Credential = (New-Object Net.NetworkCredential("Anonymous", ""))
)
Begin {
$Path = "TEST_" + $Path
if ($User) {
if ($Password) {
$Credential = New-Object Net.NetworkCredential($User, $Password)
}
}
}
Process {
$Path
$Credential
}
End {
}
}
If I run "A-Function test -User test -Password -test", I get the output :
TEST_test
UserName Password SecurePassword Domain
-------- -------- -------------- ------
test test System.Security.SecureString
This is what I expected the output to be. However, if I run this command instead:
"Test" | A-Function test -User test -Password -test
I get this output instead:
TEST_test
UserName Password SecurePassword Domain
-------- -------- -------------- ------
Anonymous System.Security.SecureString
In other words, in the second scenario, it hasn't changed the value of the Credential argument in the Begin section, but in the first, it has. I don't understand why this is, can someone explain it?
Regards
If you do a
trace-command parameterbinding {"Test" | A-Function test -User test -Password test} -pshost
you will see that $credential, since it is not being passed as argument to the function, it is bound with the default value each time during process. Path, on the other hand, is bound only once since you pass it to the function and hence the change that you do in begin is available in process.
This is definitely a bug / something not really optimal, as this is not reproduced in Powershell v3. In v3, you get the desired output of credential being test rather than anonymous.
You can fix this for now, of course, by using a local variable in begin or scoping the existing ones with $script:Path etc:
function A-Function{
[CmdletBinding(SupportsShouldProcess=$True)]
param (
[Parameter(Position=0, HelpMessage="A Test string", Mandatory=$true)]
[string]$Path,
[Parameter(Position=1, HelpMessage="The list of file names to download.", ValueFromPipeline=$True)]
[string[]]$testVar,
[Parameter(HelpMessage= "The username")]
[string]$User,
[Parameter(HelpMessage= "The password")]
[string]$Password,
[Parameter(HelpMessage= "The credentials used.")]
[Net.NetworkCredential]$Credential = (New-Object Net.NetworkCredential("Anonymous", ""))
)
Begin {
$script:Path = "TEST_" + $Path
if ($User) {
if ($Password) {
$script:Credential = New-Object Net.NetworkCredential($User, $Password)
}
}
}
Process {
$script:Path
$script:Credential
}
End {
}
}
"Test" | A-Function test -User test -Password test
Yeah, I've been bitten by this. The function parameters get reset every iteration of the Process block. Just set a local variable in your Begin block and then use that local variable in the Process block instead of using the parameter variable.
Edit:
I have to confess that I didn't look very hard at your sample code as I immediately recognized a problem from the combination of the title of your question and the fact that you were setting $Path in the Begin block. Looking at your function a little harder, makes me wonder why you have a Process block when you don't process any pipeline input.
My recollection is that changes you make to the function parameters in the Begin block will survive the first iteration of the Process block. They will only get reset on subsequent iterations (or, perhaps, at the end of the first iteration).