Remote SSH - executing cat and reading input - bash

I've got the following script snippit I've written and am using:
ssh -t root#$host bash -c "'
echo \"Connected to server $host\"
echo \"Paste in data and hit Ctrl + D\"
data=$(cat)
echo \"Success!\"
echo $data
'"
It works fine without the cat line and executes in order.
With the cat line, it hangs for input before any of the echos and then when you Ctrl + D it executes the rest.
How can I get this to run back in the intended order?
EDIT:
For clarity, I'm simply attempting to get data from the local console after making the SSH connection. If I was to use a read, this works fine and prompts. But if I use "$(cat)" (which works fine locally) I have issues.
I'm attempting to take multiple lines of text in after the SSH connection. I'm using $(cat) to do this as per link, which works fine locally but doesn't appear to work in remote commands via SSH.

The reason the cat happens at the start is because you are using double quotes. The $(cat) is evaluated and executed locally before the ssh command runs. To prevent that, use single quotes instead. (I took the liberty to simplify a bit more at the same time.)
ssh -t root#$host "echo 'Connected to server $host'"';
echo "Paste in data and hit Ctrl + D"
data=$(cat)
echo "Success!"
echo "$data"'
(The first echo is in double quotes in order to allow $host to be expanded locally; then we switch to single quotes to protect the rest of the command line from local expansion.)

1- Using variables in ssh connection would be something like this:
while read pass port user ip fileinput fileoutput filetemp; do
sshpass -p$pass ssh -o 'StrictHostKeyChecking no' -p $port $user#$ip fileinput=$fileinput fileoutput=$fileoutput filetemp=$filetemp 'bash -s'<<ENDSSH1
python /path/to/f.py $fileinput $fileoutput $filetemp
ENDSSH1
done <<____HERE1
PASS PORT USER IP FILE-INPUT FILE-OUTPUT FILE-TEMP
____HERE1
So you can change this script in the way you want.
2- Can't you change your script to this way?
ssh -t root#$host bash -c "'
echo \"Connected to server $host\"
echo \"Paste in data and hit Ctrl + D\"
data=`cat <input-file>`
echo \"Success!\"
echo $data
'"
3- If you want to run some commands on remote machine try this one:
#!/bin/bash
SCRIPT='
<put your commands here>
'
while read pass ip; do
sshpass -p$pass ssh -o 'StrictHostKeyChecking no' -p <port> root#$ip "$Script"
done <<HERE
pass1 ip1
pass2 ip2
pass3 ip3
. .
. .
. .
HERE

Related

bash while read loop stops after the first line (after corrections based on a StackExchange post) [duplicate]

The eventual goal is to have my bash script execute a command on multiple servers. I almost have it set up. My SSH authentication is working, but this simple while loop is killing me. When I execute the while loop, reading my file for host names, it works fine when I run a
ssh $HOST "uname -a"
but when I attempt to run another ssh command,
ssh $HOST "oslevel -s"
the while loop ends early! I can't figure it out. Why would the while read do loop run perfectly fine with the first command, but not when the second is added?
I have a simple text file called hosts.list that has 4 hostnames, one per line.
$ cat hosts.list
pcced1bip04
pcced1bit04
pcced1bo02
pcced1bo04
$ cat getinfo.bash
#!/bin/bash
set -x
while read HOST
do
echo $HOST
ssh $HOST "uname -a"
#ssh $HOST "oslevel -s"
echo ""
done < hosts.list`
When it runs, it works fine. It goes through the file, line by line and gets the results of "uname -a". So everything is fine, right? (Sorry, but I turned on set -x).
$ ./getinfo.bash
+ read HOST
+ echo pcced1bip04
pcced1bip04
+ ssh pcced1bip04 'uname -a'
AIX pcced1bip04 1 6 0001431BD400
+ echo ''
+ read HOST
+ echo pcced1bit04
pcced1bit04
+ ssh pcced1bit04 'uname -a'
AIX pcced1bit04 1 6 0001431BD400
+ echo ''
+ read HOST
+ echo pcced1bo02
pcced1bo02
+ ssh pcced1bo02 'uname -a'
AIX pcced1bo02 1 6 0009FE2AD400
+ echo ''
+ read HOST
+ echo pcced1bo04
pcced1bo04
+ ssh pcced1bo04 'uname -a'
AIX pcced1bo04 1 6 0009FE2AD400
+ echo ''
+ read HOST
$
The problem occurs when I enable the line [ssh $HOST "oslevel -s"]. When I do, the script only reads the first line of the file, and then stops. Why won't it go onto the other lines?
$ ./getinfo.bash
+ read HOST
+ echo pcced1bip04
pcced1bip04
+ ssh pcced1bip04 'uname -a'
AIX pcced1bip04 1 6 0001431BD400
+ ssh pcced1bip04 'oslevel -s'
6100-06-02-1044
+ echo ''
+ read HOST
$
If I had a problem with my script, why would it be working perfectly fine with just the [ssh $HOST "uname -a"] in the while loop?
If you run commands which read from stdin (such as ssh) inside a loop, you need to ensure that either:
Your loop isn't iterating over stdin
Your command has had its stdin redirected:
...otherwise, the command can consume input intended for the loop, causing it to end.
The former:
while read -u 5 -r hostname; do
ssh "$hostname" ...
done 5<file
...which, using bash 4.1 or newer, can be rewritten with automatic file descriptor assignment as so:
while read -u "$file_fd" -r hostname; do
ssh "$hostname" ...
done {file_fd}<file
The latter:
while read -r hostname; do
ssh "$hostname" ... </dev/null
done <file
...can also, for ssh alone, can also be approximated with the -n parameter (which also redirects stdin from /dev/null):
while read -r hostname; do
ssh -n "$hostname"
done <file
Assign to an array before the loop, so that you are not using stdin for your loop variables. The ssh inside the loop can then use stdin without interfering with your loop.
readarray a < hosts.list
for HOST in "${a[#]}"; do
ssh $HOST "uname -a"
#...other stuff in loop
done
As the solution specified here use -n option for ssh or open file with a different handle:
while read -u 4 HOST
do
echo $HOST
ssh $HOST "uname -a"
ssh $HOST "oslevel -s"
echo ""
done 4< hosts.list`
maybe with python XD
#!/usr/bin/python
import sys
import Queue
from subprocess import call
logfile = sys.argv[1]
q = Queue.Queue()
with open(logfile) as data:
datalines = (line.rstrip('\r\n') for line in data)
for line in datalines:
q.put(line)
while not q.empty() :
host = q.get()
print "++++++ " + host + " ++++++"
call(["ssh", host, "uname -a"])
call(["ssh", host, "oslevel -s"])
print "++++++++++++++++++++++++++"

ssh to remote server gives incorrect hostname

Below piece of code is part of my build script & I'm running it from Jenkins as a parameterized build option(node).
It is able to connect to server_b and does the tasks as expected, but the only command not working is the "hostname -f".
It still gives the server_a's hostname value instead of server_b's hostname value.
I'm not sure what exactly I'm doing incorrectly,thanks.
#!/bin/bash
server_b(){
folder="/home/mylogin/server_b"
ssh -tt myuser#server_b.com << EOF
echo "$(hostname -f)" ## tried echo `hostname -f` as well
cd $folder
echo -e "FOLDER: $folder"
<other commands that works fine>
exit
EOF
}
server_b
Try escaping the $ that you want interpreted on the remote machine, eg :
echo \$(hostname -f)

how to add ssh key to host via bash script

I've been trying to automate the creation of a user and configuration of the ssh access.
So far I created a script that access the host and creates the new user via expect, as follows:
expect -c '
spawn ssh '$user'#'$ip';
expect "assword: ";
send "'$passwd'\r";
expect "prompt\n";
send "adduser '$new_user'\r";
...
send "mkdir /home/'$new_user'/.ssh\r";
expect "prompt\n";
send "exit\r";
'
This works fine, after that I need to add the .pub key file to the authorized keys file in the host, there is where hell started.
I tried:
ssh_key='/home/.../key.pub'
content=$(cat $ssh_key)
expect -c '
spawn ssh '$user'#'$ip' "echo '$content' >> /home/'$new_user'/.ssh/authorized_keys;
expect "password:";
...
'
and got:
missing "
while executing
"spawn ssh root#000.00.00.00 ""
couldn't read file "<ssh .pub key content> ...
I tried also:
cat $ssh_key | ssh $user#$ip "cat >> /home/$new_user/.ssh/authorized_keys"
Without success, I only get the password query blinking, I can't connect the expect with this last method.
I'm going to ignore the larger problems here and focus specifically on your question. (There are larger problems: Don't use expect here -- if you rely on sshpass instead you can simplify this script immensely).
Right now, when you close your single quotes, you aren't starting any other kind of quotes. That means that when you substitute a variable with whitespace, you end the -c argument passed to expect.
Instead of doing this:
'foo'$bar'baz'
do this:
'foo'"$bar"'baz'
...so your script will look more like:
ssh_key='/home/.../key.pub'
content=$(<"$ssh_key")
expect -c '
spawn ssh '"$user"'#'"$ip"' "echo '"$content"' >> /home/'"$new_user"'/.ssh/authorized_keys;
expect "password:";
...
'
In terms of avoiding this altogether, though, consider something more like the following:
#!/bin/bash
# ^^^^- NOT /bin/sh
content=$(<"$ssh_key") # more efficient alternative to $(cat ...)
# generate shell-quoted versions of your variables
# these are safe to substitute into a script
# ...even if the original content contains evil things like $(rm -rf /*)
printf -v content_q '%q' "$content"
printf -v new_user_q '%q' "$new_user"
# use those shell-quoted versions remotely
sshpass -f"$password_file" ssh "$host" bash -s <<EOF
adduser ${new_user_q}
printf '%s\n' ${content_q} >>/home/${new_user_q}/.ssh/authorized_keys
EOF

ssh bash receive variable from a remote file

I need to read the variable from a remote file over SSH and compare it. But I get a variable in the wrong format. how to do it correctly?
#!/bin/bash
pass='dpassspass'
user='root#10.10.19.18'
IP="10.2.1.41"
path=/sys/variable/serv
#not work## No such file or directory# write=$(sshpass -p $ovhpass ssh -t $user echo "$IP" > $path)
sshpass -p $pass ssh -t $user << EOF
echo "$IP" > $path
EOF
my_var=$(sshpass -p $pass ssh -t $user "cd /sys_ovh; ./serv.bash")
echo mystart-"$my_var"-myend
read=$(sshpass -p $pass ssh -t $user cat $path)
echo start-"$read"-end
echo start-"$IP"-end
if [ "$read" == "$IP" ]; then
echo "run"
fi
output:
Connection to 10.10.19.18 closed.
-myendt-10.2.1.41
Connection to 10.10.19.18 closed.
-endt-10.2.1.41
start-10.2.1.41-end
Where I make a mistake? How to take data from the SSH?
The vars my_var and read are filled with a string ending with '\r', telling echo to go back to the first column. I think this is a problem with your local script. You can correct that with
tr -d "\r" < myfile > myfile2
Your fundamental problem comes from using unquoted here documents for the commands. You should properly understand in which order the shell interprets these contructs.
ssh remote cmd >file
executes cmd remotely, but first redirects the output from the ssh command to the local file.
ssh remote "cmd >’$file'"
The quotes cause the redirection to be part of the remote command line. The variable file is interpreted first, by the local shell, though.
ssh remote 'cmd >"$file"`
The single quotes prevent the local shell from modifying the command before sending it. Thus, he variable interpolation and the redirection are both handled by the remote shell, in this order.
So your commented-out "not work" command could easily be fixed with proper quoting. However, it will be much more elegant and efficient to use a single remote session, and execute all the commands in one go. Mixing the local variable IP with remote variables calls for some rather elaborate escaping, though. A major simplification would be to pass the value on standard input, so that the entire remote script can be single quoted.
#!/bin/bash
pass='dpassspass'
user='root#10.10.19.18'
IP="10.2.1.41"
result=$(echo "$IP" |
sshpass -p "$pass" ssh -t "$user" '
path=/sys/variable/serv
cat > "$path"
cd /sys_ovh
./serv.bash
cat "$path"')
echo mystart-"${result%$'\n'*}"-myend
echo start-"${result#*$'\n'}"-end
echo start-"$IP"-end
if [ "${result#*$'\n'}" == "$IP" ]; then
echo "run"
fi
The output from the remote shell is two lines; we pick it apart by using the shell's prefix and suffix substitution operators.

One password prompt for bash script including SCP and SSH

Printing documents from printer connected to internet is really slow at my university. Therefore I'm writing a script that sends a file to a remote computer with SCP, sends a series of commands over SSH to print the document from the remote computer (which has better connection with the printer) and then delete the file on the remote computer.
It works like a charm but the annoying part is that it prompts for the password two times, one time when it sends the file with SCP and one time when it sends commands over SSH. How can this be solved? I read that you can use a identity file? The thing is though that multiple users will use it and many has very limited experience with bash programming so the script must do everything including creating the file.
Users will mostly use Mac and the remote computer uses Red Hat. Here's the code so far:
#!/bin/sh
FILENAME="$1"
PRINTER="$2"
# checks if second argument is set, else prompt for it
if [ -z ${PRINTER:+x} ]; then
printf "Printer: ";
read PRINTER;
fi
# prompt for username
printf "CID: "
read CID
scp $FILENAME $CID#adress:$FILENAME
ssh -t $CID#adress bash -c "'
lpr -P $PRINTER $FILENAME
rm $FILENAME
exit
'"
You don't need to copy the file at all; you can simply send it to lpr via standard input.
ssh -t $CID#adress lpr -P "$PRINTER" < "$FILENAME"
(ssh reads from $FILENAME and forwards it to the remote command.)
start an ssh-agent and add your key to it:
eval $(ssh-agent -s)
ssh-add # here you will be prompted
scp "$FILENAME" "$CID#adress:$FILENAME"
ssh -t "$CID#adress" bash -c <<END
lpr -P "$PRINTER" "$FILENAME"
rm "$FILENAME"
END
ssh-agent -k # kill the agent

Resources