My remote server cannot recognize the variable using sftp in shell scripting - linux

my script
#!/bin/ksh
ysdt=`date -d '-1 day' '+%Y%m%d'`
expect << 'EOS'
spawn sftp id#192.168.61.240:/data01/msc/huawei
expect "Password:"
send "password\n"
expect "sftp>"
send "mkdir $ysdt\n"
expect "sftp>"
send "cd /data01/msc/huawei/$ysdt\n"
expect "sftp>"
send "put /home/med/cdr/bak/hw/mss/prm/$ysdt/b*"
expect "sftp>"
send "bye\n"
EOS
-----error show-----------
can't read "ysdt": no such variable
while executing
"send "mkdir $ysdt\n" "

Your shell isn't expanding the variable ysdt.
If any part of the word passed as delimiter for a here-document is quoted, no expansion occurs.
remove the quoting to have expansion occur.
i.e.
expect << EOS

Related

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

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.

How do I run a command remotely as root ?

I`m trying to login to a server as userA then switch to the root account and run some command. The steps should be like:
ssh userA#10.0.0.1
su - root
whoami
I implemented step 1 with the code below but don't know how to implement steps 2 and 3.
#!/usr/bin/expect -f
set timeout 12
set password_root 12345678
set password_A 12345678
spawn ssh -t sflow#10.0.0.1
expect -re ".*password:"
send "$password_sflow\r"
expect eof
#!/usr/bin/expect
set timeout 12
set password_root 12345678
set password_A 12345678
set prompt "#|>|\\\$ $"
spawn ssh -t user1#xxx.xxx.x.xx
expect {
timeout {puts TIMEOUT}
"yes/no" {send "yes\r";exp_continue}
"password:" {send "user1password\r";exp_continue}
-re $prompt
}
send "su - root\r"
expect "Password:"
send "rootpassword\r"
expect -re $prompt
send "whoami\r"
expect -re $prompt
# and to exit
send "exit\r" ;# exit su
expect -re $prompt
send "exit\r" ;# exit ssh
expect eof

Loop exit while calling the sftp script inside the loop

I have written a shell script with name process.sh which calls the sftp script del.sh inside the loop. But once the sftp script(del.sh) is called, it is not coming back to process.sh
Process.sh
#!/bin/bash
for i in $(ls *.csv)
do
fbname=$(basename "$i" .csv)
echo "$fbname"
if [ -f $fbname.done ]
then
gzip $fbname.csv
exec /home/amithsa/del.sh $fbname.csv
else
echo "File Not Found"
fi
done;
del.sh
#!/usr/bin/expect
set arg1 [lindex $argv 0]
spawn sftp xyz#10.10.10.10 22
expect "password:"
send "xxxxxxxxx\n"
expect "sftp>"
send "cd sftp\n"
expect "sftp>"
send "rm $arg1\n"
expect "sftp>"
send "rm *.html\n"
expect "sftp>"
send "exit\n"
interact
Don't use exec if you want the script to continue. exec will completely replace the current process with the command you give it. It never returns1.
Just call your script like you call gzip and echo and the other commands, i.e. directly.
gzip $fbname.csv
/home/amithsa/del.sh $fbname.csv
1 Unless the command cannot be executed and, and you have the execfail option enabled. Or you're running an interactive shell.

How to one-line this "expect" command?

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'

Resources