how to escape unusual/uniq characters from expect scripts? - linux

in expect script I can set any command or character to run it on remote machine
but the sad thing is that expect cant send the same character as they defined in the expect script
for example
I want to run this line from expect script in order to change IP address from 10.10.10.10
to 1.1.1.1
expect # {send "perl -i -pe 's/\Q10.10.10.10\E/1.1.1.1/' /etc/hosts\r"}
but when I run the expect screen actually I see this line runing on the console:
[root#localhost ~]# perl -i -pe 's/Q10.10.10.10E/1.1.1.1/' /etc/hosts
pay attention that the backslash before Q and before E was Disappeared
so I wonder hoW to escape those characters from expect script?
so expect will run the same line on the console as following
[root#localhost ~]# perl -i -pe 's/\Q10.10.10.10\E/1.1.1.1/' /etc/hosts
REMARK set a backslash "\" before backslash doesn’t help!!!
my script:
#!/bin/ksh
#
expect=`cat << EOF
set timeout -1
spawn ssh 192.9.200.10
expect {
")?" { send "yes\r" ; exp_continue }
word: {send secret1\r}
}
expect # {send "perl -i -pe 's/\\Q10.10.10.10\\E/1.1.1.1/' /etc/hosts\r"}
expect # {send exit\r}
expect eof
EOF`
expect -c "$expect"
RESULTS ( after I run my script: )
spawn ssh 192.9.200.10
root#'192.9.200.10 s password:
Last login: Sun Aug 4 22:46:53 2013 from 192.9.200.10
[root#localhost ~]# perl -i -pe 's/Q10.10.10.10E/1.1.1.1/' /etc/hosts
[root#localhost ~]# exit
logout
Connection to 192.9.200.10 closed.

Using different Tcl quotes will work
expect # {
# send text verbatim here
send {perl -i -pe 's/\Q10.10.10.10\E/1.1.1.1/' /etc/hosts}
# interpret backslash sequence as carriage return here
send "\r"
}

Either escape it with \ or enclose the whole thing in {}
expect # {send "perl -i -pe 's/\\Q10.10.10.10\\E/1.1.1.1/' /etc/hosts\r"}
(Enclosing the entire thing with {} would send \r as this 2 characters, not as line terminator, so not appropriate here.)
See the the manual page about the Tcl Syntax
And an other note:
You could do the same thing with Tcl, as long as you don't send the commands over SSH

Related

Bash shell: command not found

I am using the shell module to execute the following command
tasks:
- name: Command
shell: "sshpass -p 123 ssh -o 'StrictHostKeyChecking no' root#10.67.13.50 shell << EOF \n whoami\nEOF | cat"
I am getting the following error
"stderr_lines": [
"/bin/sh: line 2: warning: here-document at line 0 delimited by end-of-file (wanted `EOF')",
"Warning: Permanently added '10.67.13.50' (ECDSA) to the list of known hosts.",
"bash: shell: command not found"
]
What is wrong with my command?
tl;dr you can either a) replace shell with sh or bash, or b) replace shell with whoami and drop the heredoc.
Let's decompose the shell command:
sshpass -p 123 ssh -o 'StrictHostKeyChecking no' root#10.67.13.50 shell << EOF \n whoami\nEOF | cat
There are several processes happening here.
A sh pipeline with two subprocesses:
sshpass which then runs as a subprocess…
ssh which connects to 10.67.13.50 and runs…
shell with \n whoami\n as its standard input
… and cat, which takes the output from the sshpass process hierarchy
There are a couple potential bugs:
You can safely remove cat from the pipeline.
As #KamilCuk mentioned, cat reads from its input and writes it out. It isn't doing anything here; it's neither useful nor harmless.
shell is not a command on the remote server (10.67.13.50). If you want to run a shell, typically sh or bash is used.
Moreover, you can replace the entire shell … EOF sequence with whoami.
The << EOF \n whoami \nEOF is a heredoc to tell the shell on the remote server what commands to execute. However, there is only one command executed.
In summary, the shell: line could be rewritten as:
sshpass -p 123 ssh -o 'StrictHostKeyChecking no' root#10.67.13.50 whoami
… an odd command, since we know the remote user root.

Run a command on remote machine and store its output in variable on remote machine

I want to capture number of rules of iptables that start with specific pattern in comment and then delete them. This is what I want to achieve. Here is my bash script
ssh -o "StrictHostKeyChecking no" root#$ip_address << EOF
echo "Now Removing your IPTables";
#storing output in input variable
input=$(iptables -nL INPUT --line-number | grep ip.* | cut -d " " -f1 | xargs)
#converting variable into an array
arr1=($input);
#loop through each element of array
echo "length:${#arr1[#]}";
for (( i="${#arr1[#]}"-1;i >=0; i-- ));
do
echo "$i:${arr1[$i]}"
iptables -D INPUT $i;
done;
EOF
Problem is the iptables command is not being executed on the remote machine and the output shows the length of arr1 is 0. But I am sure iptables has rules with my desired pattern.
Error being shown in terminal:
-bash: line 9: 3: command not found
Adding 2>&1 in the end of command also not working:
input=$(iptables -nL INPUT --line-number | grep ip.* | cut -d " " -f1 | xargs 2>&1)
TL;DR: Use <<"EOF" instead of <<EOF.
Your Here-Document will expand all variables and evaluate all subshells before the script is even sent to your ssh server.
Consider the following script:
ssh user#servername <<EOF
echo "$(hostname)"
EOF
This will not print servername (the name of the computer you are connecting to) but the name of your localhost instead (the name of the computer you working on).
Before ssh is executed, the subshell $(hostname) is executed. The resulting string "echo localhostname" is then passed to ssh and executed on the remote server.
To fix the problem you have to escape the $ inside the Here-Document or use a literal Here-Document:
ssh user#servername <<"EOF"
echo "$(hostname)"
EOF

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.

Run bash script inside the expect script

Im trying to run my bash script inside an expect script but getting errors.
/usr/bin/expect <<EOD
spawn ssh nginubud#10.123.25.83 $(< try1.sh)
expect "assword:"
send "$reg\r"
expect eof
EOD
im trying to do this in expect ssh nginubud#10.123.25.83 "$(< try1.sh)", this one is working but i need to find a way to run it in automated way. I dont want to use RSA keys.
error that in encountered:
spawn ssh nginubud#10.123.25.83 #tats script
invalid command name "echo"
while executing
"echo "Enter Year:""
Also i can run my expect ssh script but when i include and try to run my $(< try1.sh) im getting "no variable errors"
You can use ssh user#host bash -c .... For example:
[bash] % cat foo.sh
export CMD=$( printf '%q' "$(< try.sh)" )
expect << EOF
spawn ssh foo#localhost bash -c \$::env(CMD)
expect -nocase password:
send bar\r
expect eof
EOF
[bash] % cat try.sh
echo hello world | tr a-z A-Z
[bash] % bash foo.sh
spawn ssh foo#localhost bash -c echo\ hello\ world\ \|\ tr\ a-z\ A-Z
foo#localhost's password:
HELLO WORLD
[bash] %

How to display newline in ssh

I'm trying to do the following:
#!/bin/sh
ssh user#server "echo \"Test \n for newline\""
This displays:
test \n for newline
How do I get the shell to interpret \n as an actual newline?
Try using the -e option, e.g., echo -e "Test \n for newline".
If your echo doesn't have a -e option, then I'd use printf. It's widely available and it does not have nearly as many variations in it's implementations.
For greater portability, use printf instead of echo.
#!/bin/sh
ssh user#server 'printf "Test \n for newline"'
According to the POSIX standard, echo should process \n as a newline character. The bash built-in echo does not, unless you supply the -e option.
Just use one of
#!/bin/sh
ssh user#server "echo -e \"Test \n for newline\""
or
#!/bin/sh
ssh user#server 'echo -e "Test \n for newline"'
or
#!/bin/sh
ssh user#server "echo -e 'Test \n for newline'"
or even
#!/bin/sh
ssh user#server "echo 'Test
for newline'"
All of those will display
Test
for newline
(note the trailing space after the first line and the leading space before the second one - I just copied your code)
Before exectuning ssh command update the IFS environment variable with new line character.
IFS='
'
Store the ssh command output to a varaible
CMD_OUTPUT=$(ssh userName#127.0.0.1 'cat /proc/meminfo')
iterate the output per line
for s in $CMD_OUTPUT; do echo "$s"; done

Resources