0

I wrote a short script to compare the amount of available RAM as reported by the free -m command with the amount of swap used and, if there is enough room for the swap + 128M (to compensate for momentary increases in swap use at the exact time the command is issued), move the swapped data back into RAM. The problem is that no matter how much RAM I'm using, the script returns "Not enough room in memory to unswap". I put echo commands in and the math works, so I thin it's something to do with the if ((diff > 128)); then line. I'd appreciate it if someone could take a look.

Note: I want to use the script with a cron job on a virtualization server to return KVM machines that have been swapped out to memory if there's room. Since my server is also where I try out new distros, my RAM usage tends to have periodic spikes. Consequently, seldomly used VMs like ownCloud get swapped out occasionally but don't come back in until I or someone else needs them for something, and then I get complaints that they're broken or painfully slow.

#!/bin/bash
# This script moves data from swap back into RAM

meminfo=$(mktemp -d -t meminfo.XXXXXX)
cd $meminfo
info=$(mktemp info.XXXXXX)
swapline=$(mktemp swapline.XXXXXX)
freeline=$(mktemp freeline.XXXXXX)
free -m > info
exec 0< info

sed -n '2p' info > freeline
less freeline | while read -a values; 
do 
  free=${values[3]} 
  cached=${values[6]}
  avail=$[$free+$cached]
done

sed -n '4p' info > swapline
less swapline | while read -a bytes;
do
  swap=${bytes[2]};
done

diff=$((avail - swap))

if (( diff > 128 )); then
  echo "unswapping"
  swapoff -a
  swapon /dev/sda2
else
  echo "Not enough room in memory to unswap"
fi

rm -r $meminfo
#

Here's the current output of free -m; the script still doesn't work.

             total       used       free     shared    buffers     cached
Mem:          3019       2930         88         12         49        851
-/+ buffers/cache:       2029        989
Swap:         1928        165       1763
  • Did you try `echo $diff`, before the `if (( diff > 128 )); then`. What does that say – MSameer Apr 26 '16 at 20:53
  • check for trailing spaces ... bash does not tolerate that. – z atef Apr 26 '16 at 20:55
  • @MSameer just tried that and it gave me `0 Not enough room in memory to unswap`. So the problem is with the subtraction. Maybe a syntax error? – Parker Wedge Apr 26 '16 at 20:56
  • @NullSoulException, there was one trailing space but it didn't change the output Edit: I got rid of it anyway – Parker Wedge Apr 26 '16 at 20:59
  • On my system (swap enabled), the sed -n '4p' line does produces nothing, so swap is never set. Check for these error cases. Also, less is an interactive command, you should probably use cat to read the freeline and swapline files. – Chaos Apr 26 '16 at 21:00
  • 6
    [Shellcheck](http://www.shellcheck.net) helpfully points out that your [variables are being defined in a subshell](https://github.com/koalaman/shellcheck/wiki/SC2031) caused by piping to the while read loop, so they'll appear to be unset after. – that other guy Apr 26 '16 at 21:01
  • @ParkerWedge, I will first try echo each of the variables avail and swap i.e. `echo $avail` and `echo $swap`. Make sure those values are as expected. We can then look into `$((avail - swap))`. – MSameer Apr 26 '16 at 21:02
  • @MSameer `avail 933` `swap 164` `diff 0` `Not enough room in memory to unswap` – Parker Wedge Apr 26 '16 at 21:05
  • 2
    This appears to be http://stackoverflow.com/questions/16854280/modifying-variable-inside-while-loop-is-not-remembered (as caught by shellcheck and indicated by that other guy) and which is linked from the bash tag info wiki. – Etan Reisner Apr 26 '16 at 21:09
  • @ParkerWedge, Looks like the issue is around the variables in scope. Your syntax looks fine. I tried the following and its working as expected i.e. it enters into if block. `#!/bin/bash avail=933 swap=164 diff=$((avail - swap)) if (( diff > 128 )); then echo "Inside if, diff val $diff" else echo "Inside else, diff val $diff" fi` – MSameer Apr 26 '16 at 21:14
  • Thanks for all the help; the answer below worked great but it's the same as the posts you guys linked to above. I didn't realize that variables used after a pipe didn't stay set. I'm coming to Bash from Python, and everyone raves about pipes, so I guess I was just trying to shoehorn them in. Thanks again! – Parker Wedge Apr 26 '16 at 21:26

1 Answers1

1

As @thatotherguy pointed out, the avail and swap variables are not set in the same scope, due to the pipe to the while loops. Change the loops like so:

while read -a bytes
do
    swap=${bytes[2]}
done < swapline
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Chaos
  • 335
  • 1
  • 6