Skip to content

Instantly share code, notes, and snippets.

@narate
Last active August 26, 2025 15:40
Show Gist options
  • Save narate/706478c88897753d9c3f843f6338cb39 to your computer and use it in GitHub Desktop.
Save narate/706478c88897753d9c3f843f6338cb39 to your computer and use it in GitHub Desktop.
This script helps identify and optionally truncate files that have been deleted but are still held open by processes on a Linux system (commonly causing df to show full disk while du shows much less).

Script Explanation: deleted_fd_cleanup.sh

This script helps identify and optionally truncate files that have been deleted but are still held open by processes on a Linux system (commonly causing df to show full disk while du shows much less).


Features

  1. Detects all (deleted) files currently held open by processes using lsof.
  2. Ensures each PID+FD is handled only once to avoid duplicate operations.
  3. Can run in two modes:
  • Dry-run [--dry-run] (default): Shows which files would be truncated and their sizes without making any changes.
  • Prune [--prune]: Actually truncates the files, freeing disk space.
  1. Displays:
  • PID and file descriptor (FD) path /proc/<pid>/fd/<fd>
  • File size in human-readable form and in bytes
  • Total size of all deleted files at the end

How It Works

  • Uses lsof to list deleted files.
  • Parses PID and FD using awk.
  • Checks if the /proc/<pid>/fd/<fd> exists.
  • Truncates the file if in prune mode.
  • Accumulates the total size of all deleted files.
  • Prints per-file info and total space held by deleted files.

Usage

  1. Dry-run (default)
./deleted_fd_cleanup.sh
  • Shows which deleted files are still held open and their sizes.
  • Does not modify any files.
  • Example output:
Would truncate /proc/30853/fd/62 size: 2.3G bytes: 2411724800
Would truncate /proc/31110/fd/1811 size: 500M bytes: 524288000

TOTAL space of deleted files: 2.78 MB
  1. Explicit dry-run
./deleted_fd_cleanup.sh --dry-run
  • Same as above; explicitly specifies dry-run mode.
  1. Prune (truncate)
./deleted_fd_cleanup.sh --prune
  • Truncates all deleted files that are still held open, freeing disk space.
  • Prints per-file info and total space freed.
  • Example output:
Truncated /proc/30853/fd/62 size: 2.3G bytes: 2411724800
Truncated /proc/31110/fd/1811 size: 500M bytes: 524288000

TOTAL space of deleted files: 2.78 MB

Notes / Warnings

  • Safe to use on log or temporary files.
  • Do not truncate critical database or index files; it may corrupt the process.
  • Always consider running dry-run first before pruning.
  • Uses sudo because lsof and /proc/<pid>/fd/ require root permissions.
#!/bin/bash
# deleted_fd_cleanup.sh
# Usage:
# ./deleted_fd_cleanup.sh # default dry-run
# ./deleted_fd_cleanup.sh --dry-run
# ./deleted_fd_cleanup.sh --prune
MODE="dry-run"
if [ "$1" == "--prune" ]; then
MODE="prune"
elif [ "$1" == "--dry-run" ]; then
MODE="dry-run"
fi
TOTAL=0
sudo lsof -nP | awk -v mode="$MODE" -v total_ref="$TOTAL" '
/\(deleted\)/ {
pid=$2;
rawfd=$4;
gsub(/[^0-9]/,"",rawfd);
if (rawfd != "") {
key=pid"-"rawfd;
if (!(key in seen)) {
fdpath="/proc/"pid"/fd/"rawfd;
if (system("[ -e " fdpath " ]")==0) {
size_cmd="stat -c%s " fdpath;
size=0;
if ((size_cmd | getline sz) > 0) { size=sz }
close(size_cmd)
total += size
# human-readable
hum_cmd="du -h " fdpath;
if ((hum_cmd | getline hum)>0) { }
close(hum_cmd)
if (mode=="dry-run") {
print "Would truncate", fdpath, "size:", hum, "bytes:", size
} else if (mode=="prune") {
system("truncate -s 0 " fdpath)
print "Truncated", fdpath, "size:", hum, "bytes:", size
}
}
seen[key]=1
}
}
}
END {
printf "\nTOTAL space of deleted files: %.2f MB\n", total/1024/1024
}'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment