lsof (List Open Files) command displays information about files opened by processes. In Linux, "everything is a file" - including network connections, devices, and pipes - making lsof an essential troubleshooting and system monitoring tool.
lsof is not installed:sudo apt install lsofsudo yum install lsof
Display all files opened by all processes on the system:
sudo lsof
Sample Output (truncated):
COMMAND PID TID USER FD TYPE DEVICE SIZE/OFF NODE NAME systemd 1 root cwd DIR 253,0 4096 2 / systemd 1 root rtd DIR 253,0 4096 2 / systemd 1 root txt REG 253,0 1632776 134678 /lib/systemd/systemd sshd 1234 root 3u IPv4 12345 0t0 TCP *:22 (LISTEN) nginx 5678 nginx 6u IPv4 56789 0t0 TCP *:80 (LISTEN)
Output Column Meanings:
Identify the process accessing a specific file:
lsof /var/log/syslog
Sample Output:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME rsyslogd 1234 syslog 6w REG 253,0 1234567 789012 /var/log/syslog
Use Cases:
Real-World Example:
# Can't unmount filesystem? sudo lsof /mnt/data # Find out what's keeping it busy COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME bash 5678 john cwd DIR 8,1 4096 2 /mnt/data
Show all files opened by a particular user:
lsof -u username
Multiple users:
lsof -u user1,user2
Exclude specific user:
lsof -u ^root
What it shows: Every file, directory, network connection, and device opened by the specified user's processes. Useful for:
Display all network connections:
sudo lsof -i
Specific port:
sudo lsof -i :80
Specific protocol and port:
sudo lsof -i TCP:22 sudo lsof -i UDP:53
Show listening ports only:
sudo lsof -i -sTCP:LISTEN
Sample Output:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME sshd 1234 root 3u IPv4 12345 0t0 TCP *:22 (LISTEN) nginx 5678 nobody 6u IPv4 56789 0t0 TCP *:80 (LISTEN) mysqld 9012 mysql 20u IPv4 90123 0t0 TCP localhost:3306 (LISTEN)
Common Uses:
List all files opened by a process ID:
lsof -p 1234
Multiple processes:
lsof -p 1234,5678,9012
By process name (all instances):
lsof -c nginx
Practical Examples:
# Find all files used by Apache sudo lsof -c httpd # Check what database server has open sudo lsof -c mysql # See all files for a specific PID sudo lsof -p $(pgrep -f "my_application")
Helps diagnose:
Identify deleted files still held open by processes:
sudo lsof | grep deleted
Or more specifically:
sudo lsof +L1
Sample Output:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME java 5678 app 12u REG 253,0 5368709120 0 123456 /tmp/app.log (deleted)
What this means: The file has been deleted from the filesystem, but the process still has it open. The disk space won't be freed until the process closes the file or terminates.
To recover disk space:
# Option 1: Restart the process gracefully sudo systemctl restart application # Option 2: Kill the specific process sudo kill 5678 # Option 3: Truncate the file while still open (for logs) sudo truncate -s 0 /proc/5678/fd/12
Common Scenario: Your /var is full, you deleted huge logs, but df still shows no space freed. This command finds the culprit!
Monitor file operations in real-time:
sudo lsof -r 2
What it does: Refreshes the list every 2 seconds, showing dynamic file access. The -r option enables repeat mode.
Monitor specific directory continuously:
sudo lsof -r 1 +D /var/www/html
Monitor network connections to specific host:
sudo lsof -r 2 -i @192.168.1.100
Practical monitoring script:
#!/bin/bash
# Monitor who's accessing sensitive directory
echo "Monitoring /etc/shadow access..."
sudo lsof -r 1 /etc/shadow | while read line; do
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $line"
# Could send alert here
done
Press Ctrl+C to stop monitoring.
Combine multiple criteria (default is OR logic):
# Files opened by nginx OR on port 80 sudo lsof -c nginx -i :80
Use AND logic (both conditions must match):
# Files opened by nginx AND on port 80 sudo lsof -c nginx -a -i :80
Complex real-world examples:
# All network connections by user www-data sudo lsof -a -u www-data -i # TCP connections to port 443 in ESTABLISHED state sudo lsof -i TCP:443 -a -sTCP:ESTABLISHED # Files in /var/log opened by non-root users sudo lsof +D /var/log -a -u ^root # Network connections excluding localhost sudo lsof -i -a -u ^root | grep -v localhost
Troubleshooting Example:
# Find why specific user can't bind to port sudo lsof -i :8080 -a -u appuser
Comprehensive security monitoring script:
#!/bin/bash
# security-audit.sh - Detect suspicious file and network activity
REPORT_FILE="/tmp/security_audit_$(date +%Y%m%d_%H%M%S).txt"
echo "=== Security Audit Report ===" > $REPORT_FILE
echo "Date: $(date)" >> $REPORT_FILE
echo "" >> $REPORT_FILE
# 1. Check for suspicious network connections
echo "=== Suspicious Outbound Connections ===" >> $REPORT_FILE
sudo lsof -i -sTCP:ESTABLISHED | grep -v -E "(localhost|127.0.0.1)" | \
grep -v -E "(github.com|google.com)" >> $REPORT_FILE || echo "None found" >> $REPORT_FILE
echo "" >> $REPORT_FILE
# 2. Check deleted but open files (possible rootkit)
echo "=== Deleted Files Still Open ===" >> $REPORT_FILE
sudo lsof +L1 >> $REPORT_FILE || echo "None found" >> $REPORT_FILE
echo "" >> $REPORT_FILE
# 3. Check for processes with no name (suspicious)
echo "=== Unnamed Processes ===" >> $REPORT_FILE
sudo lsof | grep -E "^\s*$" >> $REPORT_FILE || echo "None found" >> $REPORT_FILE
echo "" >> $REPORT_FILE
# 4. Check unusual ports (not 22,80,443)
echo "=== Non-Standard Listening Ports ===" >> $REPORT_FILE
sudo lsof -i -sTCP:LISTEN | grep -v -E ":(22|80|443|3306|5432)" >> $REPORT_FILE || \
echo "None found" >> $REPORT_FILE
echo "" >> $REPORT_FILE
# 5. Files opened from /tmp (execution from tmp)
echo "=== Execution from /tmp ===" >> $REPORT_FILE
sudo lsof | grep "/tmp" | grep -E "(EXEC|txt)" >> $REPORT_FILE || echo "None found" >> $REPORT_FILE
echo "" >> $REPORT_FILE
# 6. Check for excessive file descriptors (leak detection)
echo "=== Processes with >1000 Open Files ===" >> $REPORT_FILE
for pid in $(ps -eo pid); do
count=$(sudo lsof -p $pid 2>/dev/null | wc -l)
if [ "$count" -gt 1000 ]; then
echo "PID $pid has $count open files" >> $REPORT_FILE
ps -p $pid -o pid,user,comm >> $REPORT_FILE
fi
done
echo "Audit complete! Report: $REPORT_FILE"
cat $REPORT_FILE
Run this daily via cron to catch suspicious activity early.
Advanced network monitoring and analysis tool:
#!/bin/bash
# netmon.sh - Comprehensive network connection monitor
print_header() {
echo "======================================"
echo "$1"
echo "======================================"
}
# Summary of all network connections
print_header "LISTENING SERVICES"
sudo lsof -i -sTCP:LISTEN -P | awk 'NR>1 {print $1, $3, $9}' | column -t
echo ""
print_header "ESTABLISHED CONNECTIONS"
sudo lsof -i -sTCP:ESTABLISHED -P | awk 'NR>1 {print $1, $3, $9}' | column -t
echo ""
print_header "UDP SERVICES"
sudo lsof -i UDP -P | awk 'NR>1 {print $1, $3, $9}' | column -t
echo ""
print_header "CONNECTION COUNT BY PROCESS"
sudo lsof -i -sTCP:ESTABLISHED | awk 'NR>1 {print $1}' | sort | uniq -c | sort -rn
echo ""
print_header "CONNECTION COUNT BY USER"
sudo lsof -i -sTCP:ESTABLISHED | awk 'NR>1 {print $3}' | sort | uniq -c | sort -rn
echo ""
print_header "EXTERNAL CONNECTIONS (Non-Localhost)"
sudo lsof -i -sTCP:ESTABLISHED | grep -v -E "(localhost|127.0.0.1)" | \
awk 'NR>1 {print $1, $3, $9}' | column -t
echo ""
print_header "SUSPICIOUS HIGH PORTS (>10000)"
sudo lsof -i -sTCP:LISTEN | awk '$9 ~ /:/ {split($9,a,":"); if(a[2]>10000) print $0}'
# Optional: continuous monitoring
if [ "$1" == "-w" ]; then
echo ""
echo "Entering watch mode (refreshing every 3 seconds)..."
watch -n 3 "$0"
fi
Usage:
# Single snapshot bash netmon.sh # Continuous monitoring bash netmon.sh -w
| Option | Description | Example |
|---|---|---|
-u user |
List files for specific user | lsof -u john |
-c name |
List files for processes beginning with name | lsof -c nginx |
-p PID |
List files for specific process ID | lsof -p 1234 |
-i |
List network connections | lsof -i |
-i :port |
List connections on specific port | lsof -i :80 |
-i protocol |
List connections for protocol (TCP/UDP) | lsof -i TCP |
-i @host |
List connections to/from host | lsof -i @192.168.1.1 |
+D dir |
List all open files under directory | lsof +D /var/log |
-a |
AND logic for multiple conditions | lsof -u john -a -i |
-r seconds |
Repeat mode (monitor continuously) | lsof -r 2 |
+L1 |
Find deleted files still open | lsof +L1 |
-P |
Don't resolve port names | lsof -P -i |
-n |
Don't resolve hostnames | lsof -n -i |
-t |
Terse output (PIDs only) | lsof -t /file |
sudo kill $(sudo lsof -t -i :8080)
if sudo lsof -i :8080 > /dev/null; then
echo "Port 8080 is in use"
else
echo "Port 8080 is available"
fi
sudo lsof +D /mnt/external
watch -n 1 'sudo lsof -i :80 -i :443 | grep ESTABLISHED | wc -l'
sudo lsof -i :3306 -sTCP:ESTABLISHED
Most lsof commands require root privileges:
sudo lsof ...
Filter and use grep:
sudo lsof | grep pattern sudo lsof -i | less
Disable hostname and port resolution:
sudo lsof -n -P
| Task | Command |
|---|---|
| All open files | sudo lsof |
| What's using this file? | lsof /path/to/file |
| What's listening on port 80? | sudo lsof -i :80 |
| All user's files | lsof -u username |
| All process's files | lsof -p PID |
| All network connections | sudo lsof -i |
| Deleted but open files | sudo lsof +L1 |
| Monitor continuously | sudo lsof -r 2 |
lsof with other commands:sudo lsof -t -i :8080 | xargs kill - Kill process on port 8080sudo lsof | wc -l - Count total open filessudo lsof -p $(pgrep nginx) - Files for all nginx processes