How can I most portably get disk stats from a bash script?

I am already using

cat /sys/block/*/stat

to calculate overall bandwidth by disk / partition, however I am trying to also determine the overall size of the disk, and number of sectors used / free.

I know that these stats are available using df, however I am looking for an alternative ( how does df do its backend work? ), preferable something that uses /sys/class/ or /sys/block/.

Is this possible? Or would I need to use df | awk top get the stats I am looking for?



This is for use in a metrics reporting script that will collect various bits of information from the system and report it to a graphing system.

i.e. grabbing sector read / write stats

for device in /sys/block/*

    stats=$( cat $device/stat )

    sectorsRead=$( echo $stats | awk '{print $3}' )
    sectorsWrite=$( echo $stats | awk '{print $7}' )

    doSomethingWith sectorsRead
    doSomethingWith sectorsWrite

I am looking to do something similar, but grab total number of sectors, vs sectors used / free.

  • 1
    Yes, I guessed as much or you wouldn't have posted here :) But what nix systems? The /sys directory is not standard. I think it's only Linux, but it certainly isn't POSIX. I also expect it won't be present in most embedded systems. The df command, on the other hand is POSIX and will be present on any *nix system. So I'm trying to understand why you would want to avoid using the standard, portable tool and instead roll your own, less portable approach.
    – terdon
    Dec 8, 2016 at 19:40
  • Fair enough - so my best bet would be looping the output of df, and awking through it? mountPoint=$( df | awk '{print $5}' )? Will the fields always be the same?
    – Matt Clark
    Dec 8, 2016 at 19:45
  • Heh, no they won't. It's still probably the best tool for the job. Hang on, I'll write something up.
    – terdon
    Dec 8, 2016 at 19:46
  • Aha, that was what I suspected, and why I was looking into /sys ;)
    – Matt Clark
    Dec 8, 2016 at 19:48
  • Please see the updated answer for an even more robustly portable approach (thanks to derobert).
    – terdon
    Dec 8, 2016 at 20:35

1 Answer 1


The most portable tool for what you are trying to do is df. Don't fiddle with /sys which is not guaranteed to be on any non-Linux system. And don't reinvent the wheel. This is precisely what df is for and why it is specified by POSIX.

Of course, things aren't quite that simple and there are various df implementations, some of which have different formats. However, the good folks who write the POSIX specs have, in their infinite wisdom, included the following option in the specification of df:

    Produce output in the format described in the STDOUT section.

So, if you always use df -P that should be about as portable as it is possible to get. When using that flag, any POSIX-compliant df version (which should be all of them or as close as makes no difference) will produce oputput following the specification below (taken from the POSIX df page):

When both the -k and -P options are specified, the following header line shall be written (in the POSIX locale):

"Filesystem 1024-blocks Used Available Capacity Mounted on\n"

When the -P option is specified without the -k option, the following header line shall be written (in the POSIX locale):

"Filesystem 512-blocks Used Available Capacity Mounted on\n"

The implementation may adjust the spacing of the header line and the individual data lines so that the information is presented in orderly columns.

The remaining output with -P shall consist of one line of information for each specified file system. These lines shall be formatted as follows:

"%s %d %d %d %d%% %s\n", , , , , ,

In the following list, all quantities expressed in 512-byte units (1024-byte when -k is specified) shall be rounded up to the next higher unit. The fields are:

The name of the file system, in an implementation-defined format. The total size of the file system in 512-byte units. The exact meaning of this figure is implementation-defined, but should include , , plus any space reserved by the system not normally available to a user. The total amount of space allocated to existing files in the file system, in 512-byte units. The total amount of space available within the file system for the creation of new files by unprivileged users, in 512-byte units. When this figure is less than or equal to zero, it shall not be possible to create any new files on the file system without first deleting others, unless the process has appropriate privileges. The figure written may be less than zero. The percentage of the normally available space that is currently allocated to all files on the file system. This shall be calculated using the fraction: /( + )

expressed as a percentage. This percentage may be greater than 100 if is less than zero. The percentage value shall be expressed as a positive integer, with any fractional result causing it to be rounded to the next highest integer.

The directory below which the file system hierarchy appears.

You should probably also use the -k flag to always have the results printed as 1024-byte units instead of 512. And you may as well set LC_ALL=POSIX to make sure the locale won't affect the output. Putting all that together gives:

Combining that with the also ultra-portable awk, you can do:

totalSectors=$(env -i LC_ALL=POSIX df -k -P /dev/sda1 | awk 'NR>1{print $2}')
usedSectors=$(env -i LC_ALL=POSIX df -k -P /dev/sda1 | awk 'NR>1{print $3}')
availableSectors=$(env -i LC_ALL=POSIX df -k -P /dev/sda1 | awk 'NR>1{print $4}')
  • Though here, LC_ALL=POSIX df -P (without -k) produces the 1024-blocks header line. Same with even env -i LC_ALL=POSIX df -P. You need to also set POSIXLY_CORRECT with GNU Coreutils: env -i LC_ALL=POSIX POSIXLY_CORRECT=1 df -P
    – derobert
    Dec 8, 2016 at 20:21
  • @derobert why? The header is part of the POSIX definition and I'm excluding it with the NR>1 in awk.
    – terdon
    Dec 8, 2016 at 20:25
  • 1
    Well, GNU Coreutils ignores the POSIX requirement—POSIX (as you quoted) says you should be getting sizes in 512-byte blocks, but coreutils thinks that's silly and gave you 1024-byte blocks instead. Your numbers will be off by a factor of 2.
    – derobert
    Dec 8, 2016 at 20:27
  • (You could also of course use df -P -k, and then always have KiB everywhere. Which is probably a saner default, especially since it's not like disks have 512-byte sectors anymore)
    – derobert
    Dec 8, 2016 at 20:32
  • @derobert yes, that seems far more reasonable. Thanks, again.
    – terdon
    Dec 8, 2016 at 20:35

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.