How to find the largest open files?

You can use the -F option of lsof to get almost unambiguous output which is machine-parseable with only moderate pain. The output is ambiguous because lsof rewrites newlines in file names to \n.

The lsof output consists of one field per line. The first character of each name indicates the field type and the rest of the line is the field value. The fields are: p=PID (only for the first descriptor in a given process), f=descriptor, t=type (REG for regular files, the only type that has a size), s=size (only if available), n=name. The awk code below collects entries that have a size and prints the size and the file name. The rest of the pipelines sorts the output and retains the entry with the largest size.

lsof -Fnst | awk '
    { field = substr($0,1,1); sub(/^./,""); }
    field == "p" { pid = $0; }
    field == "t" { if ($0 == "REG") size = 0; else next; }
    field == "s" { size = $0; }
    field == "n" && size != 0 { print size, $0; }
' | sort -k1n -u | tail -n42 | sed 's/^[0-9]* //'

One convoluted way looks like this:

lsof \
| grep REG \
| grep -v "stat: No such file or directory" \
| grep -v DEL \
| awk '{if ($NF=="(deleted)") {x=3;y=1} else {x=2;y=0}; {print $(NF-x) "  " $(NF-y) } }'  \
| sort -n -u  \
| numfmt  --field=1 --to=iec


129M  /var/log/maillog
166M  /var/log/nginx/access_log
172M  /var/log/metrics/kubernetes/kubelet.log
185M  /var/log/metrics/kubernetes/etcd.log
257M  /var/log/metrics/kubernetes/etcd.log.1
335M  /var/log/metrics/kubernetes/kubelet.log.1

I know this is not perfect. For example if the file name contains "DEL" this would purge that file from the output list. lsof also has a -F option described in the OUTPUT FOR OTHER PROGRAMS section. Using that may be simpler.


lsof prints something like this:

COMMAND      PID    TID           USER   FD      TYPE             DEVICE  SIZE/OFF       NODE NAME
systemd        1                  root  cwd       DIR              253,0      4096        128 /
tuned       2975                  root    7u      REG              253,0      4096  805307770 /tmp/ffiKkVeXD (deleted)
python2    49888  49890           root  DEL       REG               0,18            196039884 /dev/shm/sem.NXPFow
systemd        1                  root  mem       REG              253,0     90664      10063 /usr/lib64/
java      149435 175229            box   69r      REG              253,0 350872273  808108999 /box/var/log/metrics/kubernetes/kubelet.log.1
java      149435 149580            box  107w     FIFO                0,8       0t0  272526226 pipe
prometheu 147867 148211           root  mem       REG              253,6             31457463 /lib64/ (stat: No such file or directory)

grep REG keep regular files

grep -v "stat: No such file or directory" Remove the files that have stat error. (I do not know why this happens)

grep -v DEL Discard the Linux map files that have been deleted;

From lsof documentation:

''DEL'' for a Linux map file that have been deleted;

After this processing we are left with something like this:

tuned       2975                  root    7u      REG              253,0      4096  805307770 /tmp/ffiKkVeXD (deleted)
systemd        1                  root  mem       REG              253,0     90664      10063 /usr/lib64/
java      149435 175229            box   69r      REG              253,0 350872273  808108999 /box/var/log/metrics/kubernetes/kubelet.log.1

The size is either the 3rd or the 2nd column from last depending on the value of the last column. If the last column is (deleted) pick the 3rd from last otherwise 2nd.

awk '{if ($NF=="(deleted)") {x=3;y=1} else {x=2;y=0}; {print $(NF-x) " " $(NF-y) } }'

sort -n -u | numfmt --field=1 --to=iec

Sort, uniquify and make the bytes count human readable

You can do the following

lsof | grep REG | awk '{ print $1,$7,$9 }' | sort -t ' ' -k 2 -V

Using the awk you filter the output to include command, size and filename and sort it based on the 2nd column, which is size. -t specifies the delimiter, -V sort 'naturally' - so 1, 2, 10 will be sorted this way instead of 1, 10, 2. -k is the key for the sort (the column you want to sort by)