Backstory
Recently, we've changed the PCs at my workplace to un Linux Mint.
It works amazing, is blazing fast and the adjustment time was very short.
However, I noticed my PC would freeze completely, sometimes.
I've determined that it is low RAM memory.
My working habits require me to open 10+ tabs (sometimes 100, in 5-8 Google Chrome windows).
This makes it so the swap is stuffed, as well as the RAM (Physical Memory).
Sometimes I have to use a VM running off of my PC, which takes 4GB for itself.
The code
Since old habits are hard to kill and isn't fun, I've decided to write code (which is a lot more fun).
Since Linux Mint doesn't warn about low available RAM, I've scattered around and put together a script to run every minute, and warn me when the memory is running low.
This script can also be executed from the console, displaying a message on it, if needed.
A detection method had to be added, since the output sent to cron
is emailed by default.
#!/usr/bin/env bash
# based from https://askubuntu.com/questions/234292/warning-when-available-ram-approaches-zero
LANG=en_US.UTF-8
# gets available and total ram
RAM=$(free -m)
total=$(echo "$RAM"|awk '/^[mM]em\.?:/{print 2ドル}')
available=$(echo "$RAM"|awk '/^[mM]em\.?:/{print 7ドル}')
# warn if less than these levels is free
# warning = 20%
# critical = 10%
WARNING=$(expr $total / 5)
CRITICAL=$(expr $total / 10)
# -h int:transient:1 <-- don't store the notification
# https://unix.stackexchange.com/questions/393397/get-notify-send-to-clear-itself-from-notification-tray/401587
if [ $available -lt $CRITICAL ]; then
# using -u critical doesn't allow the notification to go away after -t ms have past
# this causes issues if afk, since the notifications will queue until the -u critical is closed
notify-send -i error -h int:transient:1 -t 60000 "Low memory!" "$available/$total MB free, critical at $CRITICAL MB"
elif [ $available -lt $WARNING ]; then
notify-send -h int:transient:1 -t 15000 "Memory is going low" "Available: $available/$total MB, warns at $WARNING MB"
fi
# outputs if not ran by cron
# https://unix.stackexchange.com/questions/46789/check-if-script-is-started-by-cron-rather-than-invoked-manually
if [ -t 0 ]; then
echo "Available: $available/$total MB, warns at $WARNING MB, critical at $CRITICAL MB"
fi
This script runs in crontab -e
, with the following:
# https://unix.stackexchange.com/questions/247860/notify-send-doesnt-work-at-cinnamon
DISPLAY=":0.0"
XAUTHORITY="/home/<username>/.Xauthority"
XDG_RUNTIME_DIR="/run/user/<output from id -u>"
* * * * * /usr/bin/env bash /home/<username>/<script-from-above>.sh
Conclusion
It was really hard to get to this point, with plenty of issues.
Most information was available online, but, making it work was a pain.
I'm not really good with Bash scripting, which probably means that I have some really bad mistakes.
As far as I know, it works as intended, when intended:
Besides that, is there anything I can improve or change?
Any optimization/optimisation I can do?
Any localization/localisation issues that may come?
2 Answers 2
Kudos to you! This is a nice little script, easy to read and to understand. However, there is no reason to reach for expr
. Most shells (including dash
or even busybox sh
) can interpret arithmetic expressions on their own:
WARNING=$(($total / 5))
CRITICAL=$(($total / 10))
Also, in [
]
expressions, I'd use quotes around variables, to make sure that they don't contain any spaces. In this case, we can be sure that they won't contain some, but it's still good practice to use them:
if [ "$available" -lt "$CRITICAL" ]; then
...
elif [ "$available" -lt "$WARNING" ]; then
...
fi
Other than that, I'd write the -u
comment as a direct negative instead of a positive:
# Don't use `-u critical', as it will keep the notification even after -t ms
# and will cause notifications to queue up.
This comment still contains the same message: "don't use -u critical
", but it's much more direct.
Since no bashism are used, we can even change the shebang to #!/bin/sh
. By the way, some of those remarks were also found by ShellCheck, but it rightfully marks them as info level only.
-
\$\begingroup\$ Thank you! I completely forgot about ShellCheck! Been years since I've read that name. You actually made really good points and I find your version quite superior. The only thing I didn't really liked was the shortening of the question URLs. It is fine for sharing over email, IM or something that shows a preview. Otherwise, the link doesn't really say what it is for. Can you quickly say what unix.stackexchange.com/q/393397 is for? I can't. But if the only thing I have to say that's slightly negative is a comment, it means you have here a great answer. \$\endgroup\$Ismael Miguel– Ismael Miguel2018年10月24日 19:29:25 +00:00Commented Oct 24, 2018 at 19:29
-
\$\begingroup\$ @IsmaelMiguel Thank you too. To be honest, the short URLs are a fragment of a general line-shortening process, as I also shortened the
notify-send
lines to 72 characters with\
, but then thought "that's personal preference, not necessarily style, although it would make it easier to read withless
orvi
ongetty
or similar". I reverted those changes but apparently forgot to revert the short links :| \$\endgroup\$Zeta– Zeta2018年10月25日 06:35:25 +00:00Commented Oct 25, 2018 at 6:35 -
\$\begingroup\$ By the way, I also forget about ShellCheck all the time when I write my own scripts. I only remember it when I write a review, to be honest :) \$\endgroup\$Zeta– Zeta2018年10月25日 06:36:43 +00:00Commented Oct 25, 2018 at 6:36
-
\$\begingroup\$ It's alright. I can apply the changes to the code without applying the short links. Also, you said to use quotes in the
[ ]
expressions, but you don't use it in the final version. I should start using ShellCheck more often as well. Another question, won't the command in the crontab change? \$\endgroup\$Ismael Miguel– Ismael Miguel2018年10月25日 08:46:08 +00:00Commented Oct 25, 2018 at 8:46
I like the script. But I have maybe some suggestions for improvement.
This part:
RAM=$(free -m) total=$(echo "$RAM"|awk '/^[mM]em\.?:/{print 2ドル}') available=$(echo "$RAM"|awk '/^[mM]em\.?:/{print 7ドル}')
Personalty I prefer to dig these values from /proc/meminfo
to avoid running unnecessary free utility, but I saw several scripts based on it. And in case that is a small script for desktop... why not :)
However. The main advantage of the output from free -m
is, that all information you need contains just one line. Try to hold the advantage. Multiple lines with awk usage isn't necessary here.
Try this:
read -r _ total _ _ _ _ available <<< $(free -m | grep -i mem)
Second thing is arithmetic: as @Zeta mentioned in previous answer, there isn't a reason for using expr
. But instead:
WARNING=$(($total / 5)) CRITICAL=$(($total / 10))
I would use the syntax below, due to issue which i had in bash GNU bash, 4.2.4(2)-release. (Btw. ShellCheck complains about that as well.)
warning=$(( total / 2 ))
critical=$(( total / 10 ))
The rest could be okay. But you are forcing bash to test condition about warning
and critical
, and when the script wasn't run by cron, you don't use it? Pity.
I would do that this way:
#!/usr/bin/env bash
# gets available and total ram
read -r _ total _ _ _ _ available <<< $(free -m | grep -i mem)
warning=$(( total / 5 ))
critical=$(( total / 10 ))
message="${available}/${total} MB free"
if (( $available < $critical )); then
icon="error"
title="Memory critical!"
message+=", critical at $critical MB"
elif (( $available < $warning )); then
icon=""
title="Memory warning."
message+=", warning at $warning MB"
fi
if [[ -t 0 ]]; then
echo "$title $message"
else
notify-send "--icon=$icon" "$title" "$message"
fi
Regards :)
Explore related questions
See similar questions with these tags.
ulimit -v
. \$\endgroup\$