Failed to validate arguments being parsed to Expect Script - expect

I am writing an Expect script for first time, and I would like to have a checker to validate user arguments. But I am getting errors as following, please do let me know if you know what has went wrong. Thanks
#!/usr/bin/expect
if {$argc != 1}
{
puts 'Insert IP address of tftp server'
exit 1
}
This is the erro
jeff#mymachine:/home/jeff~$ ./test.sh
wrong # args: no script following "$argc != 1" argument
while executing
"if {$argc != 1}"
(file "./test.sh" line 3)

Expect is based on Tcl. As your code is written, the Tcl parser does not know that the if statement is supposed to continue on the next line. You can fix this by writing either
if {$argc != 1} \
{
puts 'Insert IP address of tftp server'
exit 1
}
or
if {$argc != 1} {
puts 'Insert IP address of tftp server'
exit 1
}

Related

Check environment variable in Chef only-if guard

I am trying to add a conditional guard to a Chef recipe action, so that it only runs if a certain environment variable is set (CLEAN_DB).
execute "create databases" do
Chef::Log.info("Cleaning/creating the database")
command "psql --file #{node['sql-directory']}/sql/db_create.sql"
cwd node['sql-directory']
action :run
user node['postgresql-user']
only-if (ENV['CLEAN_DB'] == "create")
end
The line right before the end is giving me trouble. It gives the following syntax error:
...
SyntaxError
-----------
/home/centos/.jenkins/workspace/test1__Deploy/cache/cookbooks/recipes/app_linux.rb:157: syntax error, unexpected end-of-input, expecting keyword_end
...
I've tried all the following:
only-if ENV['CLEAN_DB'] == "create"
only-if { ENV['CLEAN_DB'] == "create" }
only-if '("#{ENV[\'CLEAN_DB\']}" == "create")'
only-if 'if [[ "$CLEAN_DB" == "create" ]] ; then echo 0 ; else echo 1; fi;'
only-if 'if [[ "$CLEAN_DB" == "create" ]] ; then echo 0 ; else echo 1; fi'
And none of them seem to work. The documentation (https://docs.chef.io/resource_common.html) seems to suggest that all I need is a shell script that outputs 0 (in the shell case, it gives an error about a string literal in the condition) or a Ruby block (shown in the examples between {}). I am out of ideas at this point.
If you would have correctly spelled it only_if, then you should at least have succeeded with the second try (of the last block).
The correct syntax would be:
execute "create databases" do
# Logging here makes IIRC no sense because it's emitted at compile time
# Chef::Log.info("Cleaning/creating the database")
command "psql --file #{node['sql-directory']}/sql/db_create.sql"
cwd node['sql-directory']
action :run
user node['postgresql-user']
only_if { ENV['CLEAN_DB'] == "create" }
end
Alternatively, you can use
only_if do ENV['CLEAN_DB'] == "create" end

Expect script return value

I'm including simple Expect commands within a Bash script (I know I could be just writing a pure Expect script, but I would like to get it to work from within Bash).
The script is below:
#!/bin/bash
OUTPUT=$(expect -c '
spawn ssh mihail911#blah.org
expect "password:"
send "dog\r"
')
Upon ssh'ing to the above address, it will return something of the form mihail911's password: on the prompt, so I think my expect line is valid.
When I run this my script does not print anything. It does not even show the password: prompt. In general, even if I manually provide an incorrect password, I will receive a Incorrect password-type response prompt. Why is nothing printing and how can I get my script to execute properly?
I have tried debugging by using the -d flag and it seems to show that at least the first expect prompt is being matched properly.
In addition, what values should I expect in the OUTPUT variable? When I echo this variable, it simply prints the first the first command of the expect portion of the script and then mihail911's password:. Is this what it's supposed to be printing?
Use:
#!/bin/bash
OUTPUT=$(expect -c '
# To suppress any other form of output generated by spawned process
log_user 0
spawn ssh dinesh#xxx.xxx.xx.xxx
# To match some common prompts. Update it as per your needs.
# To match literal dollar, it is escaped with backslash
set prompt "#|>|\\$"
expect {
eof {puts "Connection rejected by the host"; exit 0}
timeout {puts "Unable to access the host"; exit 0;}
"password:"
}
send "root\r"
expect {
timeout {puts "Unable to access the host"; exit 0;}
-re $prompt
}
send "date\r"
# Matching only the date cmd output alone
expect {
timeout { puts "Unable to access the host";exit 0}
-re "\n(\[^\r]*)\r"
}
send_user "$expect_out(1,string)\n"
exit 1
')
echo "Expect's return value: $?"; # Printing value returned from 'Expect'
echo "Expect Output: $OUTPUT"
Output:
dinesh#MyPC:~/stackoverflow$ ./Meric
Expect's return value: 1
Expect Output: Wed Sep 2 09:35:14 IST 2015
dinesh#MyPC:~/stackoverflow$

ruby system command check exit code

I have a bunch of system calls in ruby such as the following and I want to check their exit codes simultaneously so that my script exits out if that command fails.
system("VBoxManage createvm --name test1")
system("ruby test.rb")
I want something like
system("VBoxManage createvm --name test1", 0) <-- where the second parameter checks the exit code and confirms that that system call was successful, and if not, it'll raise an error or do something of that sort.
Is that possible at all?
I've tried something along the lines of this and that didn't work either.
system("ruby test.rb")
system("echo $?")
or
`ruby test.rb`
exit_code = `echo $?`
if exit_code != 0
raise 'Exit code is not zero'
end
From the documentation:
system returns true if the command gives zero exit status, false for
non zero exit status. Returns nil if command execution fails.
system("unknown command") #=> nil
system("echo foo") #=> true
system("echo foo | grep bar") #=> false
Furthermore
An error status is available in $?.
system("VBoxManage createvm --invalid-option")
$? #=> #<Process::Status: pid 9926 exit 2>
$?.exitstatus #=> 2
For me, I preferred use `` to call the shell commands and check $? to get process status. The $? is a process status object, you can get the command's process information from this object, including: status code, execution status, pid, etc.
Some useful methods of the $? object:
$?.exitstatus => return error code
$?.success? => return true if error code is 0, otherwise false
$?.pid => created process pid
system returns false if the command has an non-zero exit code, or nil if there is no command.
Therefore
system( "foo" ) or exit
or
system( "foo" ) or raise "Something went wrong with foo"
should work, and are reasonably concise.
You're not capturing the result of your system call, which is where the result code is returned:
exit_code = system("ruby test.rb")
Remember each system call or equivalent, which includes the backtick-method, spawns a new shell, so it's not possible to capture the result of a previous shell's environment. In this case exit_code is true if everything worked out, nil otherwise.
The popen3 command provides more low-level detail.
One way to do this is to chain them using and or &&:
system("VBoxManage createvm --name test1") and system("ruby test.rb")
The second call won't be run if the first fails.
You can wrap those in an if () to give you some flow-control:
if (
system("VBoxManage createvm --name test1") &&
system("ruby test.rb")
)
# do something
else
# do something with $?
end
Ruby 2.6 added option to raise exception in Kernel#system:
system("command", exception: true)
I want something like
system("VBoxManage createvm --name test1", 0) <-- where the second parameter checks the exit code and confirms that that system call was successful, and if not, it'll raise an error or do something of that sort.
You can add exception: true to your system call to have an error raised on non 0 exit codes.
For example, consider this small wrapper around system which prints the command (similar to bash -x, fails if there's a non 0 exit code (like bash -e) and returns the actual exit code:
def sys(cmd, *args, **kwargs)
puts("\e[1m\e[33m#{cmd} #{args}\e[0m\e[22m")
system(cmd, *args, exception: true, **kwargs)
return $?.exitstatus
end
To be called like: sys("hg", "update")
If you want to call a program that uses a different convention for exit codes, you can suppress raising the exception:
sys("robocopy", src, dst, "/COPYALL", "/E", "/R:0", "/DCOPY:T", exception: false)
You can also suppress stdout and stderr for noisy programs:
sys("hg", "update", "default", :out => File::NULL, :err => File::NULL)

Calling a bash script from a tcl script and returning and exit status

I am attempting to call a bash script from a TCL script and need to get exit status from the bash script or at least pass something back into the TCL script so that I can tell if my script executed successfully. Any suggestions?
See http://wiki.tcl.tk/exec -- click the "Show discussion" button -- there's a very detailed example of how to do exactly what you're asking. What you need though is catch
set status [catch {exec script.bash} output]
if {$status == 0} {
puts "script exited normally (exit status 0) and wrote nothing to stderr"
} elseif {$::errorCode eq "NONE"} {
puts "script exited normally (exit status 0) but wrote something to stderr which is in $output"
} elseif {[lindex $::errorCode 0] eq "CHILDSTATUS"} {
puts "script exited with status [lindex $::errorCode end]."
} else ...
What you want is exec the result of which will be in the return value, be warned however there are lots of gotchas using exec, particularly if you need to do any complex quoting
My experience in tcl is limited to occasional dabbling. However, following links starting with the one in #jk's answer led me to this page which discusses the errorCode variable and related things that might be useful this circumstance. Here's a quick example demonstrating the use of errorCode:
tcl:
set ret_val [catch { exec /bin/bash /path/to/bash_script }]
set errc $errorCode
set ret_val [lindex [split $errc " " ] 2]
puts $ret_val
bash_script, as referenced above:
#!/bin/bash
exit 42
which led to output of:
42

while loops within expect

I am using expect within bash. I want my script to telnet into a box, expect a prompt, send a command. If there is a different prompt now, it has to proceed or else it has to send that command again.
My script goes like this:
\#!bin/bash
//I am filling up IP and PORT1 here
expect -c "
set timeout -1
spawn telnet $IP $PORT1
sleep 1
send \"\r\"
send \"\r\"
set temp 1
while( $temp == 1){
expect {
Prompt1 { send \"command\" }
Prompt2 {send \"Yes\"; set done 0}
}
}
"
Output:
invalid command name "while("
while executing
"while( == 1){"
Kindly help me.
I tried to change it to while [ $temp == 1] {
I am still facing the error below:
Output:
invalid command name "=="
while executing
"== 1"
invoked from within
"while [ == 1] {
expect {
This is how I'd implement this:
expect -c '
set timeout -1
spawn telnet [lindex $argv 0] [lindex $argv 1]
send "\r"
send "\r"
expect {
Prompt1 {
send "command"
exp_continue
}
Prompt2 {
send "Yes\r"
}
}
}
' $IP $PORT1
use single quotes around the expect script to protect expect variables
pass the shell variables as arguments to the script.
use "exp_continue" to loop instead of an explicit while loop (you had the wrong terminating variable name anyway)
The syntax for while is "while test body". There must be a spce between each of those parts which is why you get the error "no such command while)"
Also, because of tcl quoting rules, 99.99% of the time the test needs to be in curly braces. So, the syntax is:
while {$temp == 1} {
For more information see http://tcl.tk/man/tcl8.5/TclCmd/while.htm
(you probably have other problems related to your choice of shell quotes; this answer addresses your specific question about the while statement)

Resources