0

I have 4 servers that I am SSHing to in a while loop to fetch the free huge page information.

cat /home/oracle/dbs_group
server1
server2
server3
server4

If I run on the current host (server1), it works fine

export hpfgb=$((freehugepages=`cat /proc/meminfo|grep "HugePages_Free"|awk '{print $NF}'` * 2 / 1024 )); echo "Free Huge Pages in GB: $hpfgb"
Free Huge Pages in GB: 190

But when I try to ssh-loop through the file /home/oracle/dbs_group, its returning a NULL for $hpfgb as follows and on top of that its running only on server1 apparently as seen below.

while read -u10 HOST; do ssh -q "export hpfgb=$((freehugepages=`cat /proc/meminfo|grep "HugePages_Free"|awk '{print $NF}'` * 2 / 1024 )); echo "`hostname`: Free Huge Pages in GB: $hpfgb""; done 10< /home/oracle/dbs_group
server1: Free Huge Pages in GB:
server1: Free Huge Pages in GB:
server1: Free Huge Pages in GB:
server1: Free Huge Pages in GB:

Any idea why this would happen? What am I missing?

1 Answer 1

1

First, $((var=expr)) where you never use var is useless and misleading clutter.

Second, you don't need cat+grep+awk PLUS shell-expr for this, awk can do the whole thing:

 awk </proc/meminfo '/HugePages_free/{print "Free Huge Pages in GB: " int($NF*2/1024)}' 

Third, your loop can't work; you don't specify where ssh is to connect. You need something like ... do ssh -q $HOST "command"; done.

Fourth, your problem isn't actually with the loop at all, only with the ssh (remoting).

But to your question, when you give a command argument in doublequotes it is expanded for $-substitutions and backticks by your (local) shell before being given to the local program you run (here ssh, which sends that argument to the remote system to be executed there) -- and singlequotes inside the doublequotes don't alter this, while they would stop expansion if they were 'outside'. Moreover you can't put doublequotes inside doublequotes without backslashing them. Thus what you did (trimmed to be less unreadable) is:

ssh -q "export hpfgb=$((`cat-grep-awk` * 2 / 1024 )); echo "`hostname`: Free Huge Pages in GB: $hpfgb""
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
that part is executed on your _local_ system, and presumably returns 190
                                                            ^^^^^^^^^^                         ^^^^^^
those parts aren't even quoted, and are executed on your local system; hostname is apparently server1, and hpfgb has no value

thus what ssh gets from local shell and sends to each remote to execute is effectively 
ssh -q "export hpfgb=190; echo" server1: Free Huge Pages in GB:
which you should expect to produce the result you get

Sending commands like this awk as an ssh argument is difficult because you need at least the $NF and the internal "string" if used (it could be avoided) to be quoted both in the local shell and the remote shell. It can be done, at least usually, but gets really ugly. A much simpler approach in many cases is to send the command as input to the remote shell rather than an ssh argument, such as a heredoc:

while read -u10 HOST; do ssh -q $HOST <<EOF
awk -vhost=`hostname` </proc/meminfo '/HugePages_Free/{print host ": Free Huge Pages in GB: " int($NF*2/1024)}'
EOF
done 10<list

Although something of a hack, you only need to remotely doublequote (not singlequote) the awk script if you move the literal onto the variable and put a space in the field reference:

while read -u10 HOST; do ssh -q $HOST \
'awk -vhostx=`hostname`": Free Huge Pages in GB: " </proc/meminfo "/HugePages_Free/{print hostx int($ NF *2/1024)}" '
EOF
done 10<list

Two more points:

Is it really necessary to run hostname on the remote? Its name usually should be the same as the name you connected to, and if it's different wouldn't you like to see the name you used rather than an unexpected one? I would just use $HOST instead (although in my hack method you now need to not singlequote the whole command, so this expands locally).

The while read line; do ... done <list structure (and often -r as well, which you don't have) is needed (and recommended) to correctly handle arbitrary data, but in this case we know the contents of list must be hostnames which cannot contain whitespace or glob chars. This special/limited case can be handled by the simpler:

# data limited to valid hostnames
for HOST in $(cat list); do ssh -q $HOST <EOF
as above
EOF
done

Alternatively, another general solution as long as you only need one small-enough file (i.e. don't use hostname, see above) is to do the processing locally:

while read -u10 HOST; do ssh -q $HOST cat /proc/meminfo \
| awk -vhost=$HOST '/HugePages_Free/{print host ": Free Huge Pages in GB: "int($NF *2/1024)}'
done 10<list
# only the cat runs remotely; the | awk ... is processed only by the local shell, with normal, familiar quoting 
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much for the detailed explanation. I am reviewing and trying to absord your response. Sure, I will try your suggestions.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.