Using sudo
can be problematic within scripts. It's really designed to be used interactively, and may request a password (which won't work very well if its standard input isn't a terminal). The alternative is to require the script to be run as a suitably privileged user, and I can see that you want to minimise the amount of privileged code.
I couldn't initially find an unprivileged way to do what dmidecode
- it inspects /sys/firmware/dmi/tables/DMI
which is unreadable by ordinary users and I don't see the same information anywhere else in sysfs. However, lshw
can produce a total RAM summary without privilege (but can't see the individual DIMMs). After a bit of tracing, it seems that it reads from /sys/devices/system/memory
- multiply the contents of block_size_bytes
(in hexadecimal) by the total of */online
to get the total RAM size in bytes:
printf '%c' /sys/devices/system/memory/memory*/online | wc -c |
dc -e '16i' -f /sys/devices/system/memory/block_size_bytes -e 'Ai ? *p'
If coreutils
is available (almost all Linux systems, I think; certainly as likely as sed
or awk
and much more so than dmidecode
), then we can use the numfmt
utility to convert that number to a friendly human form:
numfmt --to=iec-i --format='%.1f%f' --suffix B
(see the man page to understand the options, and adjust to taste).
Full working alternative
#!/bin/sh
printf 'Total RAM installed = '
printf '%c' /sys/devices/system/memory/memory*/online | wc -c |
dc -e '?16i' -f /sys/devices/system/memory/block_size_bytes -e '*p' |
numfmt --to=iec-i --suffix B
As a bonus, this is much faster than using dmidecode
(which seems to gather all its information even when printing only a small subset).
Other points:
I recommend setting -e
and -u
in all shell scripts (both are specified in POSIX):
set -eu
Don't pipe through grep
and awk
- since the latter does regexp matching, just add a pattern to the awk
program:
dmidecode --type 17 | awk '/^[[:space:]]*Size: [0-9]/ { print $2 }'
Including a digit in the pattern eliminates the empty slots neatly at that stage, so obviating is_number
.
Counting using sprintf|wc
is a little safer if you use %c
or %.1s
conversion; that uses just the first letter, so you can count using wc -c
and avoid double-counting strings with embedded newlines (for example, see my worked code). You won't get the problem lines here, but it's a technique worth knowing.
Instead of iterating through the indices of $ram_entries
and repeatedly invoking sed
to extract individual lines, it's better to check that it's safe to expand without quotes (we know this to be true, since we've ensured it contains only decimal numbers), and then simply for ram_entry in $ram_entries
.
Even better, instead of capturing and assigning to a variable, we could pipe into a while read
loop to process each line. Be careful to make the loop be in the main shell, not a sub-shell, though, or make it communicate the result out.