I am trying to write a scrip that will collect the hostname, IP and total memory installed on remote hosts from a list I feed into the script. The script will collect information from Redhat and Solaris machines.Below is what my script looks like:

echo > ip_info.output
echo -e "\n"
for host in `cat ip_adds`
echo "Hostname:" $host
sudo ssh -o BatchMode=yes -o ConnectTimeout=5 $host "echo IP Address:; ip route get 1 | awk '{print $NF;exit}'; free -m | grep Mem | awk '{print $1,$2}'"
echo -e "\n"

When I run the script I get error below:

awk: {print ,}
awk:        ^ syntax error
awk: {print ,}
awk:         ^ syntax error
awk: cmd. line:1: {print ,}
awk: cmd. line:1:          ^ unexpected newline or end of string

I think the problem is with free -m | grep Mem | awk '{print $1,$2} but not sure how to rectify it. If I run free -m | grep Mem | awk '{print $1,$2} directly into the shell I have no issues. Just inside the script.

  • What is the echo > ip_info.output doing? – terdon Oct 12 '18 at 8:55
  • The echo > ip_info.output is writing nothing to the output file so that if I have to run the script again for whatever reason I dont end up with duplicate information. – user315468 Oct 12 '18 at 9:04
  • Yes, but you don't seem to be using the output file anywhere, so I assume you're running the script as script.sh > ip_info.output, right? – terdon Oct 12 '18 at 9:07
  • no Im running it as ./script.sh | tee -a ip_info.output. echo > ip_info.output clear the file for when ./script.sh | tee -a ip_info.output is run. – user315468 Oct 12 '18 at 9:14
  • 1
    OK, then don't use -a. The -a tells tee to append to the file instead of overwriting it. If you use ./script.sh | tee ip_info.output, then you'll still see the output in the terminal and you'll also save it to ip_ifo.output, overwriting anything that was already in the file. – terdon Oct 12 '18 at 9:50

This is considerably more complicated than it needs to be. Also, why would you run ssh with sudo? If you need to log into the remote as root, then you can do that (ssh root@$host) but it's very unlikely you would need to run ssh with sudo unless your ssh keys all belonged to root. Which is a pretty bad idea.

Also, on my Arch system, the command you seem to be using to get the IP returns the UID of my user:

$ ip route get 1 via dev enp0s31f6 src uid 1000 
$ ip route get 1 | awk '{print $NF;exit}'; 

It does work as expected on an Ubuntu I tested:

$ ip route get 1 via 123.456.7.8 dev eth0  src 123.456.7.9
$ ip route get 1 | awk '{print $NF;exit}'; 

So perhaps a more portable version of this is to print the field after src:

$ ip route get 1 | sed -nE 's/.* src ([0-9.]+).*/\1/p'

The errors you saw were indeed because of quoting. Since you were running ssh $host "command", the double quotes around command cause the shell to expand any variables found inside the command (so things like awk's $2 etc). To avoid this and pass the symbols unexpanded to awk, you need to escape the $.

A simpler version of your script:

sshOpts="BatchMode=yes -o ConnectTimeout=5"
echo > ip_info.output
echo ""                
while read host; do
    printf "Hostname:%s" $host
    ssh -o $sshOpts $host "printf 'Hostname: %s\nIP: %s\nMem: %s\n' $host "$(ip route get 1 | sed -nE 's/.* src ([0-9.]+).*/\1/p')" "$(free -m | awk '/Mem/{print $2}')""
done < ip_adds
  • Thanks for this terdon - Your script looks simpler however when I run it I am getting: Hostname:eXX-XX-XX-XXXXX-01Hostname: eXX-XX-XX-XXXXX-01 IP: eth0 Mem: 3832 – user315468 Oct 12 '18 at 9:33
  • Seems that the IP isnt being collected properly. – user315468 Oct 12 '18 at 9:36
  • @user315468 yes, the output of ip route get 1 can change on different systems. Try the updated answer, sorry! – terdon Oct 12 '18 at 9:54
  • thanks for your input. Much appreciated. With your help I have modified my script and now I get a really nice and neat output. My script looks like: #!/bin/bash echo > ip_info.output echo -e "\n" for host in cat ip_adds1` do sudo ssh -o BatchMode=yes -o ConnectTimeout=5 $host "printf 'Hostname: %s\nIP: %s\nMem: %s\n' $host "$(ip route get 1 | sed -nE 's/.* src ([0-9.]+).*/\1/p')" "$(free -m | awk '/Mem/{print $2}')"" echo -e "\n" done` Out put is now: Hostname: eXX-XX-XX-XXXXX-XX IP: XX.XX.XX.XXX Mem: 3832 – user315468 Oct 12 '18 at 10:44
  • 1
    @user315468 please consider incorporating the other changes I've made. The while read ... < file loop is safer than the for and a better way to parse files in general and the echo -e "\n" is just a more complicated way of writing echo "", there's no reason to use it. In fact, it would be even simpler to not use echo at all and just add a \n to the beginning of the printf command, but that's up to you. – terdon Oct 12 '18 at 10:51

I have found an answer for this. It was to do with escaping so I added \ before $1 and $2 as per below:

free -m | grep Mem | awk '{print \$1,\$2}

This is what the final script looks like. I know it will come useful to some of you out there.

for host in `cat ip_adds2`
echo "Hostname:" $host
sudo ssh -t -o BatchMode=yes -o ConnectTimeout=5 $host 'echo IP: `hostname -i`;read junk total used free shared buffers cached junk < <(free -g  | grep ^Mem);echo Memory: $total GiB'
echo -e "\n"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.