This question already has an answer here:

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.

marked as duplicate by Mikel, Anthon, jasonwryan, rahmu, slm Feb 2 '14 at 20:35

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • locale dependant maybe, but your can force the language with LANG=C df. – Totor Dec 19 '13 at 15:46
  • @Totor see 1_CR's answer, that's exactly what he suggested. – terdon Dec 19 '13 at 15:50
  • This has been answered several times previously. See e.g. body from unix.stackexchange.com/a/11859/3169 – Mikel Feb 2 '14 at 19:46

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'
  • Your sed one would print the 1st line twice if it started with /dev. Unlikely, but sed -e 1b -e '/^\/dev/!d' wouldn't have the problem. – Stéphane Chazelas Dec 18 '13 at 18:31

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/' 

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

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


df | (head -n 1; grep ^/dev)
  • 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
df -h | tee >(head -1) >(sleep 0.5;grep ^/dev) > /dev/null;sleep 1.0
  • 2
    I just tried that; it printed the header line first. – Keith Thompson Dec 18 '13 at 19:15
  • 1
    @KeithThompson Why is that a problem? The accepted answers also print the header line first. – l0b0 Dec 19 '13 at 11:49
  • @l0b0: Sorry, I mistyped; it printed the header line last. – Keith Thompson Dec 19 '13 at 16:00
  • Worked correcly on my machine. Try df -h | tee >(head -1) >(sleep 0.5;grep ^/dev) > /dev/null;sleep 1.0 – Robert Jacobs Dec 19 '13 at 18:18
  • Anonymous edited my post and it does not work for me. df -h | tee >(head -n 1) | grep ^/dev – Robert Jacobs Aug 26 '15 at 16:14
df | head -n 1; df | grep ^/dev
  • Not bad, but it has the disadvantage that it invokes df twice. – Keith Thompson Dec 18 '13 at 19:16
  • 1
    Are we short on df invocations? – ohlemacher Dec 18 '13 at 19:33
  • Not particularly, but there are solutions like terdon's df -h | sed -e 1b -e '/^\/dev/!d' that only invoke df once. The df command can hang in some circumstances; in that case, invoking it just once is probably better. – Keith Thompson Dec 18 '13 at 19:38
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.*)'

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


$ 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


$ 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

  • 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 8:00

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
df -h | egrep -e Filesystem -e "/dev" -v
  • 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 7 '15 at 16:24

Not the answer you're looking for? Browse other questions tagged or ask your own question.