35

Consider the following output from df.

Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        23G  6.1G   16G  29% /
udev             10M     0   10M   0% /dev
tmpfs           397M  420K  397M   1% /run
tmpfs           5.0M     0  5.0M   0% /run/lock
tmpfs           1.8G  904K  1.8G   1% /run/shm
/dev/sda6       890G  324G  521G  39% /home
/dev/sdb1       459G  267G  169G  62% /home/user/mnt
none            4.0K     0  4.0K   0% /sys/fs/cgroup

How can I only show lines that begin with "/dev" and keep the heading, but filter out everything else. I'd also like to not have to resort to using using temporary files or variables? Note: the heading is locale dependent, therefore you can't catch it with a regexp.

Ernest A
  • 1,873

9 Answers9

47

I would use a slightly more sophisticated approach than simple grep:

  1. awk

    df -h | awk 'NR==1 || /^\/dev/'
    

    NR is the current line number so the awk scriptlet above will print if this is the first line or if the current line begins with /dev. And after posting this I see it is the same as @1_CR's answer. Oh well...

  2. Perl

    df -h | perl -ne 'print if (/^\/dev/ || $.==1)'
    

    The same idea here, in Perl the special variable $. is the current line number. An alternative way would be

    df -h | perl -pe '$_="" unless /^\/dev/ || $.==1'
    

    The -p switch will print all lines of the input file. The value of the current line is held in $_ so we set $_ to empty unless we want the current line.

  3. sed

    df -h | sed -n '1p; /^\/dev/p'
    

    The -n suppresses normal output so no lines are printed. The 1p means print the first line and the /^\/dev/p means print any line that starts with /dev.

    As pointed out in the comments below, in the unlikely case where the locale on your current system causes the header line to start with /dev, the command above will print it twice. Stephane Chazelas points out that this one will not have that problem:

    df -h | sed  -e 1b -e '/^\/dev/!d'
    
  4. grep

    df -h | grep -E '^(/dev|File)'
    

    This might not be portable because of LOCALE problems as you said. However, I am reasonably certain that no locale or df version will give a path in the first line, so searching for lines that contain no / should also work:

    df -h | grep -E '^[^/]*$|^/dev'
    
terdon
  • 242,166
15

You are going to run into issues attempting to parse df output, however for simple cases, the following may work

LC_ALL=C df -P | awk 'NR == 1 || /^\/dev/' 
iruvar
  • 16,725
12

This should work as well (not tested, though):

df | (read a; echo "$a"; grep /dev)

or

df | (head -n 1; grep ^/dev)
daniel kullmann
  • 9,527
  • 11
  • 39
  • 46
  • 1
    very nice. ^/dev will limit this to lines that start with /dev. Wasn't able to do that as an an edit since I was short 9 characters – Chaim Geretz Dec 20 '13 at 17:02
  • 1
    First one works, but you should echo "$a" (note quotes) or any spacing in the header will shrink down to single spaces (eg with column aligned output).

    Second one does not.

    – steveayre Dec 16 '15 at 15:25
  • @steveayre Thanks for the suggestion. Integrated it into answer. – daniel kullmann Dec 17 '15 at 13:33
  • 1
    @steveayre even echo "$a" is not completely safe (possible different interpretation of - or \ ). It is always better to use printf %s\\n "$a" to print a string which could possibly contain the mentioned characters. – pabouk - Ukraine stay strong Jan 09 '20 at 10:55
5
df -h | tee >(head -1) >(sleep 0.5;grep ^/dev) > /dev/null;sleep 1.0
4
df | head -n 1; df | grep ^/dev
iruvar
  • 16,725
sqqqrly
  • 160
3
df | grep -n '' | grep -E '^1:|[^:]*:/dev' | sed 's/[^:]*://'

The grep -n inserts line numbers into the stream, we then pull out the first one and the lines of interest via egrep, then remove the added numbers from the output via sed.

You can avoid the sed by using positive lookbehinds (which grep can handle with its PCRE engine (-P)) and -o which tells grep to only print the matched pattern:

df | grep -n '' | grep -Po '(?<=^1:)(.*)|(?<=:)(/dev.*)'
3

Take the definition of body from sort but keep header line at the top

# print the header (the first line of input)
# and then run the specified command on the body (the rest of the input)
# use it in a pipeline, e.g. ps | body grep somepattern
body() {
    IFS= read -r header
    printf '%s\n' "$header"
    "$@"
}

Use it like this

$ df -h | body grep ^/dev
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        23G  6.1G   16G  29% /
/dev/sda6       890G  324G  521G  39% /home
/dev/sdb1       459G  267G  169G  62% /home/user/mnt
Mikel
  • 57,299
  • 15
  • 134
  • 153
2

Instead of filtering df output, you can filter its input.

Before:

$ df -h
Filesystem      Size  Used Avail Use% Mounted on
C:/Cygwin/bin   150G   76G   74G  51% /usr/bin
C:/Cygwin/lib   150G   76G   74G  51% /usr/lib
C:/Cygwin       150G   76G   74G  51% /
C:              150G   76G   74G  51% /cygdrive/c

After:

$ mount | grep ^C:/Cygwin | cut -d' ' -f3 | xargs df -h
Filesystem      Size  Used Avail Use% Mounted on
C:/Cygwin/bin   150G   76G   74G  51% /usr/bin
C:/Cygwin/lib   150G   76G   74G  51% /usr/lib
C:/Cygwin       150G   76G   74G  51% /

UPDATE 2014-01-24 08:03 UTC: Only grep for C:/Cygwin in the beginning of a line

mkalkov
  • 157
  • Nice trick but it will not work as expected if you are mounting by UUID instead of device name. The OP wants df style output which means /dev/foo your command will return /dev/disk/by-uuid/ format output unless fstab contains the /dev name. – terdon Dec 19 '13 at 16:29
  • The mount command uses /etc/mtab, which is updated dynamically, and while I am not sure where df gets its data from, I suspect that on Linux it reads /proc/mounts. Either way I don't understand how fstab is relevant and could not reproduce the problem with UUID-mounted disks. Can you elaborate on it? – mkalkov Jan 24 '14 at 08:00
-3
df -h | egrep -e Filesystem -e "/dev" -v
derobert
  • 109,670
  • OP has said the heading is locale-dependent, so you can't match it like that. And what is the sda1 doing at the end? – derobert Dec 20 '13 at 20:06
  • @syntaxerror the heading is locale-dependent, as is sorting. So that won't work either, for the same reason. Try LANGUAGE=ja df -h, for example. Or Spanish, or French, or many others. – derobert Jan 07 '15 at 16:24