How to one-line this "expect" command? - linux

I would like to one-line this
#!/usr/bin/expect
spawn ssh-copy-id -i .ssh/id_dsa.pub root#testip
expect "Are you sure you want to continue connecting (yes/no)?"
send -- "yes\r"
expect eof
which I would assume should be
/usr/bin/expect -c 'expect "\n" { eval spawn ssh-copy-id -i .ssh/id_dsa.pub root#testip; expect "Are you sure you want to continue connecting (yes/no)?"; send -- "yes\r" }'
but it is not.
Can anyone see how it should be?

Maybe you will not need it anymore, but it should be like this:
/usr/bin/expect -c 'spawn ssh-copy-id -i .ssh/id_dsa.pub root#testip ; expect "Are you sure you want to continue connecting (yes/no)?" ; send -- "yes\r" ; expect eof'

Related

why this interact not exec? (expect)

expect -c "
log_user 0
set timeout 60
spawn ssh ${user}#${ip} -p ${port} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
log_user 1
expect {
\"password:\" {send \"${password}\r\";exp_continue}
default {}
eof {exit 1}
}
set timeout -1
interact
"
when password not ok, eof effect.
when password is ok, interact not effect, i can't put command by keyboard, it is blocking, i just don't want use "#" or "$" to check login status, because my shell use zsh and prefix is "🚀 ~ ❯", please help me, thinks.

Bash+Expect script not running properly in crontab

I have a bash script that is supposed to run periodically. the script should connect to a remote SFTP server and get a file from there.
Since this is a SFTP server I had to use expect with the bash script.
the script runs well when I run it manually but fails when running via crontab.
the problematic function is the get_JSON_file()
please advise...
this is the code:
#!/bin/bash
export xxxxx
export xxxxx
export PATH=xxxxx
check_if_file_is_open(){
while :
do
if ! [[ `lsof | grep file.txt` ]]
then
break
fi
sleep 1
done
}
get_JSON_file(){
/usr/bin/expect -f <(cat << EOF
spawn sftp -P port user#ip
expect "Password:"
send "password\r"
expect "$ "
send "get path/to/file/file.json\r"
send "exit\r"
interact
EOF
)
}
get_JSON_file
check_if_file_is_open
cp file.txt /path/to/destination/folder
Expect's interact works only when stdin is on a tty/pty but cron job is not running on tty/pty. So replace interact with expect eof (or expect -timeout 12345 eof if necessary).
That's a very awkward way to pass expect commands to the expect interpreter. Use a (quoted) heredoc instead, and you would drop the -f option for expect
get_JSON_file(){
/usr/bin/expect <<'EOF'
spawn sftp -P port user#ip
expect "Password:"
send "password\r"
expect "$ "
send "get path/to/file/file.json\r"
send "exit\r"
expect eof
EOF
}
The most important tip for debugging expect scripts is to invoke expect's debug output. While you're working out the kinks, use
expect -d <<'EOF'
and in the crontab, you'd want to redirect stderr to stdout so you get the debugging output
* * * * * /path/to/script.sh 2>&1
To run a function within a shell script, no parentheses should be used.
Your code then becomes:
#!/bin/bash
export xxxxx
export xxxxx
export PATH=xxxxx
function check_if_file_is_open(){
while :
do
if ! [[ `lsof | grep file.txt` ]]
then
break
fi
sleep 1
done
}
function get_JSON_file(){
/usr/bin/expect -f <(cat << EOF
spawn sftp -P port user#ip
expect "Password:"
send "password\r"
expect "$ "
send "get path/to/file/file.json\r"
send "exit\r"
interact
EOF
)
}
get_JSON_file
check_if_file_is_open
cp file.txt /path/to/destination/folder
create a new script with screen command and add it in crontab
new_script.sh
#!/bin/bash
cd script_path
screen -dm -S screen_name ./your_script.sh

TCL Expect kills child of child process

I have an expect script like this
#!/usr/bin/expect -f
set timeout 30
log_user 0
set PASSWORD $::env(PASSWORD)
set USERNAME $::env(USERNAME)
set TOKEN $::env(TOKEN)
puts stderr "Generating OTP"
spawn oathtool --totp $TOKEN
expect -re \\d+
set otp $expect_out(0,string)
puts stderr "Connecting to VPN server"
spawn -ignore HUP env openconnect -b https://vpn
expect "GROUP:"
send "Tech\n"
expect "Username:"
send "$USERNAME\n"
expect "Password:"
send "$PASSWORD\n"
expect "Password:"
send "$otp\n"
expect EOF
This simple script provides user and password to openconnect to make a new VPN connection in background, but it wont work because the children spawned processes are killed by expect. As you may know, expect will send SIGHUP signal before finish, I was trying to workaround it but even when I put the -ignore HUP flag, it is killing the underlying process, I would like to end my script but the underlying openconnect in background survive.
Do you know what is lacking here?
Take into account that openconnect -b will spawn other PID by its own.
The following method using 2 batch files worked for me:
The -b flag in openconnect is not used and kill command is used instead to send openconnect to background.
contents of file named vpn2:
#!/usr/bin/expect -f
set timeout -1
spawn -ignore HUP -noecho /root/bin/v2vpn2
expect "password"
sleep 3
send -- "my_password\r"
expect "SMS OTP"
interact
expect "Established"
expect eof
contents of file named v2vpn2:
rm /var/log/vpn2.log > /dev/null 2>&1
touch /var/log/vpn2.log
# the word password is printed twice and so filtering here
tail -f /var/log/vpn2.log | grep -m2 -wo "password" | sed '2q;d' &
tail -f /var/log/vpn2.log | grep --color=never -wo "SMS OTP" &
while /bin/true; do
grep -q "Established" /var/log/vpn2.log
if (( $? == 0 )); then
kill -STOP `pgrep openconnect`
kill -CONT `pgrep openconnect`
pkill vpn2
exit
fi
done &
openconnect -u "my_user_name" my_vpn_url >> /var/log/vpn2.log 2>&1
After spending too much time on this, I solved it by adding
expect -timeout -1 -ex "Client killed"
and calling script with &
./vpn.exp &

Unable to SSH a script of bash commands via expect

I'm attempting to push a single set of commands to multiple remote hosts. I can use the following on an individual basis:
ssh user#remoteHost "bash -s" <./commands.sh
I can put this into a loop, but then I'm stuck typing in the password n number of times. Disabling the password prompts in the SSH config files is not an option for me.
I've attempted to use expect within a loop, but I'm unable to get it working.
#!/bin/bash
HOSTS="host1 host2"
read -sp "Password: " PASSWORD
for HOST in $HOSTS; do
expect -c "
spawn /usr/bin/ssh user#$HOST "bash -s" <./commands.sh
expect_before {
"*yes/no*" {send "yes"\r;exp_continue}}
expect {
"*Password*" {send $PASSWORD\r;interact}}
exit"
done
I get the following error:
spawn /usr/bin/ssh root#host1 bash
expect: invalid option -- 's'
usage: expect [-div] [-c cmds] [[-f] cmdfile] [args]
spawn /usr/bin/ssh root#host2 bash
expect: invalid option -- 's'
usage: expect [-div] [-c cmds] [[-f] cmdfile] [args]
Any ideas? It appears as though expect is trying to interpret the bash commands. I'm unsure how to stop this.
Solution:
replace
spawn /usr/bin/ssh user#$HOST "bash -s" <./commands.sh
with
spawn sh -c {ssh root#$HOST 'bash -ls' < /tmp/commands.sh}
Final Code:
#!/bin/bash
HOSTS="host1 host2"
read -sp "Password: " PASSWORD
for HOST in $HOSTS; do
expect -c "
spawn sh -c {ssh root#$HOST 'bash -ls' < /tmp/commands.sh}
expect_before {
"*yes/no*" {send "yes"\r;exp_continue}}
expect {
"*assword*" {send $PASSWORD\r;interact}}
exit"
done
I'd suggest this:
#!/bin/bash
hosts=(host1 host2)
read -sp "Password: " password
for host in "${hosts[#]}"; do
env h="$host" p="$password" expect <<'END_EXPECT'
spawn sh -c "/usr/bin/ssh user#$env(h) 'bash -s' <./commands.sh"
expect {
"*yes/no*" {send "yes\r"; exp_continue}
"*Password*" {send "$env(p)\r"}
}
interact
END_EXPECT
done
notes
uses lower case variable names: leave upper case varnames for the shell's use
uses a quoted heredoc to contain the expect code
that lets you use single and double quotes within expect without having to worry about quoting hell in the shell
uses env to pass shell variables to expect via the environment
simplifies your expect statement
The danger with using
expect -c " ...; expect "*Password*" ..."
is that the inner double quotes get matched with the outer quotes, and are removed by the shell. That leaves *Password* as a bare glob that the shell can expand based on the files in your current directory and the shell settings. For example, create a file named "The Password" (with a space) and you'll get an
error.

I cant get my expect script to run remote bash script as "expected"

I am trying to run an expect script, which should run a bash script on a remote linux server. The bash script is meant to restart a strongswan IPSec tunnel based on the argument sent via the expect script.
Here's my expect script:
#log_user 0
set prompt {\$ >}
expect -re $prompt
set ip 192.168.0.1
set user user
set password 4p4ssw0rd
set target [lindex argv 0]
spawn ssh -p 2228 "$user\#$ip"
expect "password:"
send "$password\r";
expect ">"
send "su -\r"
expect "Password:"
send "$password\r"
expect "#"
send "sh /usr/local/bin/ipsec_fixer.sh $target; exit\r"
expect "#"
Here's the remote script:
target=$1
if [[ -n "$target" ]]; then
value=`grep -i $target /etc/ipsec.conf -A1 |awk '{print $2}'| tail -n 2 | sed 's/0$//'`
declare -a args
args=( $(grep "$value"* /etc/ipsec.conf | awk '{print $2}') )
for each in ${args[#]}; do ipsec down $each; sleep 3; ipsec up $each ; done
else
exit 2
fi
The expect script runs, but I get the following output:
/usr/local/bin/fix_ipsec avpnconnection
spawn ssh -p 2228 user#192.168.0.1
user#192.168.0.1's password:
Last login: Sun Dec 11 05:40:55 2016 from 192.168.0.232
sh /usr/local/bin/ipsec_fixer.sh argv; exit
021 no connection named "-c"
021 no connection named "-c"
021 no connection named "-c"
021 no connection named "-c"
logout
user#remote:/home/login >
I have no idea what is going on. Please what am I doing wrong?
I think I've figured it out: the odd looking
021 no connection named "-c"
021 no connection named "-c"
021 no connection named "-c"
021 no connection named "-c"
lines are from the remote system's shell.
My expect script now looks like this:
set script /usr/local/bin/ipsecfixer.sh
spawn ssh -p 2228 $user#$ip
expect "password:"
send "$password\r";
expect ">"
send "su -\r"
expect "Password:"
send "$password\r"
expect "#"
send "/bin/bash $script $argv\r"
expect "#"
And it does the job, so far..
You could just use
ssh -t -p 2228 user#192.168.0.1 "sudo /bin/sh /usr/local/bin/ipsec_fixer.sh $target"
and use SSH Key-Based Authentication.
As for the messages you get, is it possible that the script you're launching has an influence on the current SSH connections?

Resources