The /proc
file system will list exactly this information:
$ ls -l /proc/self/fd
total 0
lrwx------ 1 michas users 1 Apr 6 04:44 0 -> /dev/pts/0
lrwx------ 1 michas users 1 Apr 6 04:44 1 -> /dev/pts/0
lrwx------ 1 michas users 1 Apr 6 04:44 2 -> /dev/pts/0
lr-x------ 1 michas users 1 Apr 6 04:44 3 -> /proc/6934/fd
$ ls -l /proc/self/fd 2>/dev/null <<<foo |cat
total 0
lr-x------ 1 michas users 1 Apr 6 04:45 0 -> /tmp/sh-thd-361068043 (deleted)
l-wx------ 1 michas users 1 Apr 6 04:45 1 -> pipe:[136729]
l-wx------ 1 michas users 1 Apr 6 04:45 2 -> /dev/null
lr-x------ 1 michas users 1 Apr 6 04:45 3 -> /proc/6952/fd
If you are interested in some other process just replaces "self" with the corresponding PID.
On OS X, like on all systems where they are supported except Linux, opening /dev/fd/x
is like doing a dup(x)
, the resulting fd more or less points to the same open file description as on fd x and in particular will have the same offset within the file.
Linux is the exception here. On Linux, /dev/fd/x
is a symlink to /proc/self/fd/x
and /proc/self/fd/x
is a pseudo-symlink to the file open on fd x. On Linux when you do a open("/dev/fd/x", somemode)
, you get a brand new open file description to the same file as open on x
. The new fd you obtain is not related to fd x in any way. In particular, the offset will be at the start of the file (except if you open it with O_APPEND
of course) and the mode (read/write/append...) can be different from the one on fd x (you can even get something quite different from what's on fd x, like the other end of the pipe when opening it in the opposite mode). (That also means that that doesn't work for sockets for instance which you can't open()).
So, on Linux, when you do
exec 5<> file
echo test >&5
The fd 5's offset is at the end of the file. If you do
cat <&5
You get nothing.
Still when you do:
cat /dev/fd/5
You see test
because cat
gets a new read-only fd to file
unrelated to fd 5.
On other systems, upon
cat /dev/fd/5
cat
gets a fd that is a duplicate of fd 5, so still with an offset at the end of the file.
The reason why it works with less
is that for some reason, less
does a lseek()
on that fd to the beginning of the file (does a lseek(1); lseek(0)
to determine whether the file is seekable or not).
Here, you probably want to have a fd for reading and one for writing if you want both to have different offsets:
exec 5< file 9>&1 > file
Or you'll have to reopen the file if still there, or do an lseek()
as less
does.
ksh93
and zsh
are the only shells with a builtin lseek()
operator though:
cat <&5 <#((0)) # ksh93
{sysseek 0; cat} <&5 # zsh, zmodload zsh/system to enable that builtin
Or:
cat /dev/fd/5 5<#((0)) # ksh93
sysseek -u 5 0; cat /dev/fd/5 # zsh
Best Answer
There are two reasons
lsof | wc -l
doesn't count file descriptors. One is that it lists things that aren't open files, such as loaded dynamically linked libraries and current working directories; you need to filter them out. Another is thatlsof
takes some time to run, so can miss files that are opened or closed while it's running; therefore the number of listed open files is approximate. Looking at/proc/sys/fs/file-nr
gives you an exact value at a particular point in time.cat /proc/sys/fs/file-nr
is only useful when you need the exact figure, mainly to check for resource exhaustion. If you want to list the open files, you need to calllsof
, or use some equivalent method such as trawling/proc/*/fd
manually.