2

So far, in a previously asked question: Calculate the real memory usage (memtotal - memfree - cached - bufferd) with /proc/meminfo one-liner

With many thanks to @Philippos this command prints the right memory usage:

echo $(( $(sed -E '/^(MemTotal|MemFree|Cached|Buffers): *([0-9]*).*/{s//\2/;H;};$!d;x;s/[[:cntrl:]]//;s__/1024-_g;s_$_/1024_' /proc/meminfo))) > test && awk -v CONVFMT='%.2f' '{print $1/1024""}' test && rm test

This prints:

1.32

Which is very close to the final value I'm calculating in bash in a one-liner. However, trying to add more steps (like embarrassingly creating a file and multiple of the same command) and trying to convert all dots to commas. I haven't been able to print the value as:

1,32

This command/solution is then suitable for something I pass to a friend to make it in python to display on an OLED screen.

However, the next and final step is to then calculate the usage in % compared to the total amount of memory on the system. So something like:

43%

Should then come out of the given one-liner.

Thanks to everyone for their time reading and helping, it's very appreciated!

3
  • @Philippos could i ask you to answer this perhaps?
    – Macley
    May 21 at 17:52
  • The sed program in your question divides the amounts by 1024, which would generate a certain error margin when calculating percentages. It would be better to calculate the percentages using raw values, then separately divide the amount by 1024 to also calculate the amounts in given units. The program printf(1) could then be used to format the output using a specific locale or the output could be piped through tr or sed to replace dots with commas. May 23 at 20:15
  • Dear @Vilinkameni thank you so much for your reply! I know about the rounding off error, i find it a bit strange aswell but it seems that grafana(promtheus) aswell as mobaxterm seem to calculate the kb's to mb's first. And then do the substraction. I find it a bit strange aswell, but because i want to have the numbers the same, i sadly have to go with the rounding off error. May i ask how you would use printf(1) to format it correctly? the final followup would then be: how would you then do: 100/(MemTotal)*(previous value) ?
    – Macley
    May 24 at 9:16

1 Answer 1

1

You can use the printf(1) utility for output. See man 3 printf for the description of format specifiers. In particular,

For some numeric conversions a radix character ("decimal point") or thousands' grouping character is used. The actual character used depends on the LC_NUMERIC part of the locale. (See setlocale(3).) The POSIX locale uses '.' as radix character, and does not have a grouping character. Thus, printf("%'.2f", 1234567.89); results in "1234567.89" in the POSIX locale, in "1234567,89" in the nl_NL locale, and in "1.234.567,89" in the da_DK locale.

printf "%0.2f MB\n" $(( $(sed -E '/^(MemTotal|MemFree|Cached|Buffers): *([0-9]*).*/'\
'{s//\2/;H;};$!d;x;s/[[:cntrl:]]//;s__/1024-_g;s_$_/1024_' /proc/meminfo) ))

As for the percentage output, the division by 1024 only leads to precision loss and probably isn't desired. Thus:

printf "%d%%\n" $(( $(sed -E '/^(MemTotal|MemFree|Cached|Buffers): *([0-9]*).*/'\
'{s//\2/;H;};$!d;x;s/[[:cntrl:]]//;s_([^\n]+)\n_@\1@100*(\1-(_;s_\n_+_g;'\
's_@([^@]+)@(.*)$_\2))/\1_' /proc/meminfo) ))

Note that the regex in the above command can probably be made more elegant/compact.

Combining both commands into one:

printf "%0.2f MB %d%%\n" $(( $(sed -E '/^(MemTotal|MemFree|Cached|Buffers): *([0-9]*).*/'\
'{s//\2/;H;};$!d;x;s/[[:cntrl:]]//;s__/1024-_g;s_$_/1024_' /proc/meminfo) )) \
$(( $(sed -E '/^(MemTotal|MemFree|Cached|Buffers): *([0-9]*).*/'\
'{s//\2/;H;};$!d;x;s/[[:cntrl:]]//;s_([^\n]+)\n_@\1@100*(\1-(_;s_\n_+_g;'\
's_@([^@]+)@(.*)$_\2))/\1_' /proc/meminfo) ))

Update: To compute the percentage of used memory using the amounts in MB, despite the precision loss, one could instead execute:

printf "%d%%\n" $(( $(sed -E '/^(MemTotal|MemFree|Cached|Buffers): *([0-9]*).*/'\
'{s//\2/;H;};$!d;x;s/[[:cntrl:]]//;s_([^\n]+)\n_@\1@100*(\1/1024-(_;s_\n_/1024+_g;'\
's_@([^@]+)@(.*)$_\2/1024))/(\1/1024)_' /proc/meminfo) ))

where /1024 is added in necessary places. In that case, the single command having both calculations becomes:

printf "%0.2f MB %d%%\n" $(( $(sed -E '/^(MemTotal|MemFree|Cached|Buffers): *([0-9]*).*/'\
'{s//\2/;H;};$!d;x;s/[[:cntrl:]]//;s__/1024-_g;s_$_/1024_' /proc/meminfo) )) \
$(( $(sed -E '/^(MemTotal|MemFree|Cached|Buffers): *([0-9]*).*/'\
'{s//\2/;H;};$!d;x;s/[[:cntrl:]]//;s_([^\n]+)\n_@\1@100*(\1/1024-(_;s_\n_/1024+_g;'\
's_@([^@]+)@(.*)$_\2/1024))/(\1/1024)_' /proc/meminfo) ))

Update 2: If you are entering the above command interactively in Bash, it may not work due to different behavior of Bash in interactive mode and when processing a script, with regards to the \<newline> combination inside of a $() within $(()). Other shells, like mksh (which I'm using) don't treat interactively entered commands differently in this regard.

Here are the commands in less readable form, as very long lines:

Percentage:

printf "%d%%\n" $(( $(sed -E '/^(MemTotal|MemFree|Cached|Buffers): *([0-9]*).*/{s//\2/;H;};$!d;x;s/[[:cntrl:]]//;s_([^\n]+)\n_@\1@100*(\1/1024-(_;s_\n_/1024+_g;s_@([^@]+)@(.*)$_\2/1024))/(\1/1024)_' /proc/meminfo) ))

Both outputs in a single command:

printf "%0.2f MB %d%%\n" $(( $(sed -E '/^(MemTotal|MemFree|Cached|Buffers): *([0-9]*).*/{s//\2/;H;};$!d;x;s/[[:cntrl:]]//;s__/1024-_g;s_$_/1024_' /proc/meminfo) )) $(( $(sed -E '/^(MemTotal|MemFree|Cached|Buffers): *([0-9]*).*/{s//\2/;H;};$!d;x;s/[[:cntrl:]]//;s_([^\n]+)\n_@\1@100*(\1/1024-(_;s_\n_/1024+_g;s_@([^@]+)@(.*)$_\2/1024))/(\1/1024)_' /proc/meminfo) ))
5
  • Heya! Thank you so much for this! I do prefer the rounding off error (make from kilobytes megabytes) bc this is how grafana and mobaxterm calculate it. Everything else is fine! Is it possible to add an extra answer where the rounding off error is used? That would be absolutely perfect!!! Again thank you for this, sorry for the late reply.
    – Macley
    May 26 at 7:30
  • 1
    @Macley I updated my answer. When you find some time, I recommend reading the sed(1) manual page, as well as man perlre, in addition to man 3 printf, as all the logic behind the answer is explained there in great detail. May 26 at 8:53
  • I will definitely read those! I'm really sorry to ask this silly question, but i tried all answers but somehow, i keep getting -bash: syntax error near unexpected token `)' I looked this up and tried to figure out why, but i tried copying it in notepad. However it does work in a script which is interesting. Thank you again so much for this! I'll see if i can figure out how this works with the man pages, learn and also split and tweak it a bit so that i can learn aswell. again, thank you for this, it's incredible to see this answer
    – Macley
    May 27 at 20:51
  • 1
    Yeah, sorry about that. Bash seems to differ from mksh (which I'm using) with regards to parsing bachslash-newline combination when used interactively and when executing a script. In mksh, when you end a line input with \<newline>, the shell continues taking input on the next line. When used interactively, Bash instead stops taking input and processes the line as a whole. When processing a script, however, Bash interprets the code as expected. I used the combination \<newline> to produce more readable code. I will edit the question to include the version without it. May 28 at 9:15
  • 1
    This behavior of Bash seems to be connected specifically to having a $() inside of a $(()). Outside of that specific use case, Bash seems to treat \<newline> correctly even with interactive input. May 28 at 9:28

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.