Swandrew
Swandrew

Reputation: 73

Bash- Converting a variable to human readable format (KB, MB, GB)

In my bash script, I run through a list of directories and read in the size of each directory to a variable using the du command. I also keep a running total of the total size of the directories. The only problem is that after I get the total size, it's in an unreadable format (ex. 64827120). How can I convert the variable containing this number into GBs, MBs, etc?

Upvotes: 5

Views: 8455

Answers (4)

You can use numfmt to convert raw (decimal) numbers to human-readable form.  Use --to=iec to output binary-prefix numbers (i.e., K=1024, M=220, etc.)

$ printf '%s %s\n' 1000000 foo 1048576 bar | numfmt --to=iec
977K foo
1.0M bar

and use --to=si to output metric-prefix numbers (i.e., K=1000, M=106, etc.)

$ printf '%s %s\n' 1000000 foo 1048576 bar | numfmt --to=si
1.0M foo
1.1M bar

If you specifically want to get “MB”, “GB”, etc., use --suffix:

$ printf '%s %s\n' 1000000 foo 1048576 bar | numfmt --to=si --suffix=B
1.0MB foo
1.1MB bar

If your numbers are in a column other than the first (as in Mik R’s answer), use --field:

$ printf '/home/%s %s\n' foo 1000000 bar 1048576 | numfmt --to=si --field=2
/home/foo    1.0M
/home/bar    1.1M

Or you can convert numbers on the command line (instead of using a pipe):

$ numfmt --to=si 1000000 1048576
1.0M
1.1M

Upvotes: 12

Mike R
Mike R

Reputation: 879

This is a combination of @Mahattam response and some others I combined which tallys the total amount in the standard format and then formats the output in human readable.

for path in $(awk -F: '{if ($3 >= 1000) print $6}' < /etc/passwd); do disk_usage=0; disk_usage=$(du -s ${path} | grep -oE '[[:digit:]]+');  echo "$path: $(echo $disk_usage | tail -1 | awk {'print $1'} | awk '{ total = $1 / 1024/1024 ; printf("%.2fGB\n", total) }')"; myAssociativeArray[${path}]=${disk_usage}; done ; total=$(IFS=+; echo "$((${myAssociativeArray[*]}))"); echo "Total disk usage: $(echo $total | tail -1 | awk {'print $1'} | awk '{ total = $1 / 1024/1024 ; printf("%.2fGB\n", total) }')"; unset total; unset disk_usage ;

Example of how it looks

How it works.

This could be anything you want to iterate through path list but in this example its just using the /etc/pass to loop over users paths source is here for path in $(awk -F: '{if ($3 >= 1000) print $6}' < /etc/passwd)

It then calculates the usage per folder and extracts only digits from the output in the loop disk_usage=0; disk_usage=$(du -s ${path} | grep -oE '[[:digit:]]+')

It outputs the nice formatting rounded to 2 decimal points echo "$path: $(echo $disk_usage | tail -1 | awk {'print $1'} | awk '{ total = $1 / 1024/1024 ; printf("%.2fGB\n", total) }')";

Adds this to the bash associative array myAssociativeArray[${path}]=${disk_usage}

then it sums the total value in the original amount from the array total=$(IFS=+; echo "$((${myAssociativeArray[*]}))")

then we use the same fancy output formatting to show this nicely echo "Total disk usage: $(echo $total | tail -1 | awk {'print $1'} | awk '{ total = $1 / 1024/1024 ; printf("%.2fGB\n", total) }')";

I used a variation of this for calculating cPanel Resellers accounts disk usage in the below monster oneliner.

Reseller="CPUsernameInputField"; declare -A myAssociativeArray ; echo "==========================================" | tee -a ${Reseller}_disk_breakdown.txt ; echo "Reseller ${Reseller}'s Disk usage by account"| tee -a ${Reseller}_disk_breakdown.txt; for acct in $(sudo grep ${Reseller} /etc/trueuserowners | cut -d: -f1); do disk_usage=0; disk_usage=$(du -s /home/${acct} | grep -oE '[[:digit:]]+');  echo "$acct: $(echo $disk_usage | tail -1 | awk {'print $1'} | awk '{ total = $1 / 1024/1024 ; printf("%.2fGB\n", total) }')" | tee -a ${Reseller}_disk_breakdown.txt ; myAssociativeArray[${acct}]=${disk_usage}; done ; total=$(IFS=+; echo "$((${myAssociativeArray[*]}))"); echo "Total disk usage: $(echo $total | tail -1 | awk {'print $1'} | awk '{ total = $1 / 1024/1024 ; printf("%.2fGB\n", total) }')" | tee -a ${Reseller}_disk_breakdown.txt; unset total; unset disk_usage;echo "==========================================" | tee -a ${Reseller}_disk_breakdown.txt ; echo "Sorted by top users" | tee -a ${Reseller}_disk_breakdown.txt; for key in "${!myAssociativeArray[@]}"; do printf '%s:%s\n' "$key" "${myAssociativeArray[$key]}"; done | sort -t : -k 2rn | tee -a ${Reseller}_disk_breakdown.txt;echo "==========================================" | tee -a ${Reseller}_disk_breakdown.txt ;for key in "${!myAssociativeArray[@]}"; do USER_HOME=$(eval echo ~${key}); echo "Disk breakdown for $key" | tee -a ${Reseller}_disk_breakdown.txt ; sudo du -h ${USER_HOME} --exclude=/app --exclude=/home/virtfs| grep ^[0-9.]*[G,M]  | sort -rh|head -n20 | tee -a ${Reseller}_disk_breakdown.txt;echo "=======================================" | tee -a ${Reseller}_disk_breakdown.txt; done

Example of the cPanel Reseller disk usage

Upvotes: 0

Mahattam
Mahattam

Reputation: 5783

Try using du -sh for getting summarise size in human readable, also you can find the command related help in manual.
Try below command, it will give you the size in Human readable format

 du | tail -1 | awk {'print $1'} | awk '{ total = $1 / 1024 ; print total "MB" }'

 du | tail -1 | awk {'print $1'} | awk '{ total = $1 / 1024/1024 ; print total "GB" }'

Upvotes: 1

Brian
Brian

Reputation: 1667

You want to use du -h which gives you a 'human readable' output ie KB, MB, GB, etc.

Upvotes: 1

Related Questions