LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - Newbie (http://www.linuxquestions.org/questions/linux-newbie-8/)
-   -   Passing commands through an SSH shell in a bash script (http://www.linuxquestions.org/questions/linux-newbie-8/passing-commands-through-an-ssh-shell-in-a-bash-script-817072/)

buee 06-29-2010 06:52 PM

Passing commands through an SSH shell in a bash script
 
I have a txt file with a bunch of commands. Here is the file"

Code:

'/ip firewall nat add chain=dstnat action=dst-nat to-addresses=10.0.0.25 to-ports=80 protocol=tcp src-address=192.168.10.44 dst-port=1-65535 comment="Baer NONPAYER"'
'/ip firewall nat add chain=dstnat action=dst-nat to-addresses=10.0.0.25 to-ports=80 protocol=tcp src-address=192.168.10.45 dst-port=1-65535 comment="Watson NONPAYER"'
'/ip firewall nat add chain=dstnat action=dst-nat to-addresses=10.0.0.25 to-ports=80 protocol=tcp src-address=192.168.10.46 dst-port=1-65535 comment="Watson NONPAYER"'
'/ip firewall nat add chain=dstnat action=dst-nat to-addresses=10.0.0.25 to-ports=80 protocol=tcp src-address=192.168.10.49 dst-port=1-65535 comment="Smith NONPAYER"'
'/ip firewall nat add chain=dstnat action=dst-nat to-addresses=10.0.0.25 to-ports=80 protocol=tcp src-address=192.168.10.50 dst-port=1-65535 comment="Johnson NONPAYER"'
'/ip firewall nat add chain=dstnat action=dst-nat to-addresses=10.0.0.25 to-ports=80 protocol=tcp src-address=192.168.10.51 dst-port=1-65535 comment="Johnson NONPAYER"'
'/ip firewall nat add chain=dstnat action=dst-nat to-addresses=10.0.0.25 to-ports=80 protocol=tcp src-address=192.168.10.55 dst-port=1-65535 comment="Smith NONPAYER"'
'/ip firewall nat add chain=dstnat action=dst-nat to-addresses=10.0.0.25 to-ports=80 protocol=tcp src-address=192.168.10.56 dst-port=1-65535 comment="Smith NONPAYER"'
'/ip firewall nat add chain=dstnat action=dst-nat to-addresses=10.0.0.25 to-ports=80 protocol=tcp src-address=192.168.10.58 dst-port=1-65535 comment="Smith NONPAYER"'
'/ip firewall nat add chain=dstnat action=dst-nat to-addresses=10.0.0.25 to-ports=81 protocol=tcp src-address=192.168.10.47 dst-port=1-65535 comment="Watson TORRENTER"'
'/ip firewall nat add chain=dstnat action=dst-nat to-addresses=10.0.0.25 to-ports=81 protocol=tcp src-address=192.168.10.48 dst-port=1-65535 comment="Watson TORRENTER"'
'/ip firewall nat add chain=dstnat action=dst-nat to-addresses=10.0.0.25 to-ports=81 protocol=tcp src-address=192.168.10.52 dst-port=1-65535 comment="Johnson TORRENTER"'
'/ip firewall nat add chain=dstnat action=dst-nat to-addresses=10.0.0.25 to-ports=82 protocol=tcp src-address=192.168.10.53 dst-port=1-65535 comment="Johnson SPEED JUNKIE"'
'/ip firewall nat add chain=dstnat action=dst-nat to-addresses=10.0.0.25 to-ports=82 protocol=tcp src-address=192.168.10.54 dst-port=1-65535 comment="Smith SPEED JUNKIE"'
'/ip firewall nat add chain=dstnat action=dst-nat to-addresses=10.0.0.25 to-ports=82 protocol=tcp src-address=192.168.10.57 dst-port=1-65535 comment="Smith SPEED JUNKIE"'

So, the portion of the script that throws these out there is:

Code:

cat /media/32GB/MikroTik/firewall_rules.txt | while read line; do
(ssh admin@192.254.1.1 $line)
done

I've tried wrapping the ssh command in ", `, ', ( & ), I always get:

Code:

expected command name (line 1 column 1)
What's the proper way to do this do while loop so it will pass these commands?

I have already got the key authentication set up and have tested it by passing

Code:

ssh admin@192.254.1.1 '/ip firewall nat add chain=dstnat action=dst-nat to-addresses=10.0.0.25 to-ports=81 protocol=tcp src-address=192.168.10.52 dst-port=1-65535 comment="Johnson TORRENTER"'
And that works fine.

adamben 06-29-2010 08:20 PM

How about this?

Code:

cat commands.txt | while read x; do echo $( ssh regularuser@remotehostnameorip.domain "sudo $x" & )  ; done

buee 06-29-2010 08:42 PM

Quote:

Originally Posted by adamben (Post 4018847)
How about this?

Code:

cat commands.txt | while read x; do echo $( ssh regularuser@remotehostnameorip.domain "sudo $x" & )  ; done

I had to modify it a little bit because I'm not going to a linux box with a conventional superuser. I'm going to a MikroTik router. The PITA here is not with the router though because executing the commands works fine.

Here what I ended up using, still getting the same error:

Code:

cat /media/32GB/MikroTik/firewall_rules.txt | while read line; do
echo $( ssh admin@192.254.1.1 $line & ) ;
done

I tried putting $line in quotes too.

The really confusing part is that I call in another script before this that does essentially the same thing, it works, but uses:

Code:

`ssh admin@192.254.1.1 '/ip firewall nat remove 6'`
The only difference I'm seeing is there is no variable in the shorter version.

chrism01 06-29-2010 11:17 PM

I suspect that the first level of parsing by the shell strips off the leading/trailing single quotes from the file recs. Try escaping them thus
Code:

\'/ip firewall nat add chain=dstnat action=dst-nat to-addresses=10.0.0.25 to-ports=81 protocol=tcp src-address=192.168.10.52 dst-port=1-65535 comment="Johnson TORRENTER"\'
Try one line manually first....
Also, to see what's really happening, try

set -vx

at the top of the script.

geoff_f 06-30-2010 06:18 AM

Try:
Code:

ssh admin@192.254.1.1 < /media/32GB/MikroTik/firewall_rules.txt
[edit] With 'firewall_rules.txt' formatted as you originally had it (without backslashes). It might even work without the single quotes.

adamben 06-30-2010 07:04 AM

I agree with the guru, if you can mod the input file and put in escapes/etc. might work ok for you.

Can also try an eval version:

cat cmds.txt | while read i; do echo $( eval "ssh remoteuser@host.domain ""$i" & ); done

gsal 06-30-2010 07:26 AM

if you are going to be explicitly typing all those commands, why don't you simply add the ssh part to them too?

ssh admin@192.254.1.1 '/ip firewall nat add chain=dstnat action=dst-nat to-addresses=10.0.0.25 to-ports=80 protocol=tcp src-address=192.168.10.44 dst-port=1-65535 comment="Baer NONPAYER"'
ssh ad,om@192.254.1.1 '/ip firewall nat add chain=dstnat action=dst-nat to-addresses=10.0.0.25 to-ports=80 protocol=tcp src-address=192.168.10.45 dst-port=1-65535 comment="Watson NONPAYER"'

That way you don't have to worry about 'cat' or re-direction or one too many parsing/evaluations by previous shell interpretation, etc.

You would of course have to make the file executable and add the '#!/bin/bash' at the beginning of the file and that's it. Then, you simply run such file from the command line.

pheonix7117 06-30-2010 08:28 AM

Or you could scp the file to the remote server, and just run it with bash. This also negates the need to make multiple ssh connections within a short span of time. Just two: one to copy the file with the list of commands, and a second to ssh in to run the file.

dannybpng 06-30-2010 08:57 AM

The single quotes are messing it up, because it thinks the whole line is the name of the command. Remove the single quotes from each line and type the following command.

ssh admin@192.254.1.1 < /media/32GB/MikroTik/firewall_rules.txt

Swynndla2 06-30-2010 07:02 PM

I agree that the single quotes need to be removed from the txt file.

Then, as suggested, this should work:
Code:

ssh admin@192.254.1.1 < /media/32GB/MikroTik/firewall_rules.txt
and then also this should work:
Code:

cat /media/32GB/MikroTik/firewall_rules.txt | while read line; do
echo $( ssh admin@192.254.1.1 $line & )
done

or nicer:
Code:

cat /media/32GB/MikroTik/firewall_rules.txt | while read line; do
ssh -n admin@192.254.1.1 $line
done


buee 06-30-2010 07:17 PM

Well, I spent a lot of time redoing this script. I've taken out all references to awk altogether and combined all of it in to one enormous script. A few bugs worked out thanks to the replies I've received here. Using ssh admin@192.254.1.1 < /media/32GB/MikroTik/firewall_scripts is awesome because it literally cut the run time of this script down to 1/100 of what it was. I've tested it, and this appears to be working just fine:

Code:

#!/bin/bash

path=/media/32GB/MikroTik
webaddress=10.0.0.11
bport=81
tport=82
sport=83

#Removing Old Firewall Rules
number=$(wc -l $path/firewall_rules.txt | cut -f1 -d' ')

number=$(($number+4))

rm -f $path/firewall_rules.txt

i=5

while [ $i -lt $number ]; do
echo -ne "$i," >> $path/temporary.txt && ((i++))
done

echo -ne "$i" >> $path/temporary.txt

rules=$(cat $path/temporary.txt)

ssh admin@192.254.1.1 "/ip firewall nat remove $rules"

rm -f $path/temporary.txt

#Making new rules
#Billing
zero=0
h=$(wc -l $path/CustomerUnpaid.txt | cut -f1 -d' ')

while [ $zero -lt $h ]; do
((zero++))
name=$(cat $path/CustomerUnpaid.txt | head -$zero | tail -1 | cut -f1)
ip=$(cat $path/CustomerUnpaid.txt | head -$zero | tail -1 | cut -f2)
echo "/ip firewall nat add chain=dstnat action=dst-nat to-addresses="$webaddress" to-ports="$bport" protocol=tcp src-address="$ip" dst-port=1-65535 comment=\""$name" NONPAYER\"" >> $path/firewall_rules.txt
done

rm -f $path/Name.txt $path/IP.txt


#Torrent
zero=0
h=$(wc -l $path/Torrenters.txt | cut -f1 -d' ')

while [ $zero -lt $h ]; do
((zero++))
name=$(cat $path/Torrenters.txt | head -$zero | tail -1 | cut -f1)
ip=$(cat $path/Torrenters.txt | head -$zero | tail -1 | cut -f2)
echo "/ip firewall nat add chain=dstnat action=dst-nat to-addresses="$webaddress" to-ports="$tport" protocol=tcp src-address="$ip" dst-port=1-65535 comment=\""$name" TORRENT HOUND\"" >> $path/firewall_rules.txt
done

rm -f $path/Name.txt $path/IP.txt

#Speed Junkies
zero=0
h=$(wc -l $path/SpeedTests.txt | cut -f1 -d' ')

while [ $zero -lt $h ]; do
((zero++))
name=$(cat $path/SpeedTests.txt | head -$zero | tail -1 | cut -f1)
ip=$(cat $path/SpeedTests.txt | head -$zero | tail -1 | cut -f2)
echo "/ip firewall nat add chain=dstnat action=dst-nat to-addresses="$webaddress" to-ports="$sport" protocol=tcp src-address="$ip" dst-port=1-65535 comment=\""$name" SPEED JUNKIE\"" >> $path/firewall_rules.txt
done

rm -f $path/Name.txt $path/IP.txt

#Applying new rules
ssh admin@192.254.1.1 < $path/firewall_rules.txt

exit 0

Thanks to everyone who replied.

simon.sweetman 06-30-2010 10:23 PM

You could consider some further improvements below (Uses IFS= and read to remove cat, head and tail usage, removes usage of temp files, etc.)

Are Name.txt and ip.txt just leftover temp files?

Code:

#!/bin/bash
path=/media/32GB/MikroTik

webaddress=10.0.0.11
bport=81
tport=82
sport=83

TAB=$(printf '\t')

#Removing Old Firewall Rules
number=$(wc -l $path/firewall_rules.txt)
number=${number%% *}
number=$(($number+4))

for((i=5; i<$number; i++))
do
    rules=$rules$i,
done
rules=$rules$i

ssh admin@192.254.1.1 "/ip firewall nat remove $rules"

rm -f $path/firewall_rules.txt

#Making new rules
#Billing

OLDIFS=$IFS
IFS=$TAB
while read name ip ignore
do
    echo "/ip firewall nat add chain=dstnat action=dst-nat to-addresses="$webaddress" to-ports="$bport" protocol=tcp src-address="$ip" dst-port=1-65535 comment=\""$name" NONPAYER\"" >> $path/firewall_rules.txt
done < $path/CustomerUnpaid.txt
IFS=$OLDIFS

#Torrent
IFS=$TAB
while read name ip ignore
do
    echo "/ip firewall nat add chain=dstnat action=dst-nat to-addresses="$webaddress" to-ports="$tport" protocol=tcp src-address="$ip" dst-port=1-65535 comment=\""$name" TORRENT HOUND\"" >> $path/firewall_rules.txt
done < $path/Torrenters.txt
IFS=$OLDIFS

#Speed Junkies
IFS=$TAB
while read name ip ignore
do
    echo "/ip firewall nat add chain=dstnat action=dst-nat to-addresses="$webaddress" to-ports="$sport" protocol=tcp src-address="$ip" dst-port=1-65535 comment=\""$name" SPEED JUNKIE\"" >> $path/firewall_rules.txt
done < $path/SpeedTests.txt
IFS=$OLDIFS

rm -f $path/Name.txt $path/IP.txt

#Applying new rules
ssh admin@192.254.1.1 < $path/firewall_rules.txt

exit 0


geoff_f 07-01-2010 01:28 AM

Just a comment for the benefit of those suggesting while..do..done loops in this situation.

The firewall_rules.txt file is a list of commands needed to be executed in the remote environment, separated by Linefeed characters. When issued by this command:
Code:

ssh admin@192.254.1.1 < /media/32GB/MikroTik/firewall_rules.txt
it's the same as sitting at a terminal in the remote environment, typing in each line, and hitting Enter after each one. Therefore, that one command - in the local environment - issues many commands in the remote environment, without any need to loop through variables, etc. That's the reason this method is so efficient. Program response time across a network is many times slower than in the local environment (with RAM, hard disks, etc), which makes it so much faster (in time) when using the more efficient (in programming) method when communicating across a network.

Think upon that, and marvel at the awesome power of the shell.


All times are GMT -5. The time now is 06:55 AM.