When you do cmd > /dev/null
, the shell, in a new child process, opens fd 1 onto /dev/null
and executes cmd
.
cmd
does some write(1, "output"...)
to write data to stdout (or possibly other writing system calls such as send
, sendmsg
or pwrite
) regardless of what that fd is open on.
On Linux, /proc/$pid_of_cmd/fd/1
will just be a magic symlink to /dev/null
, the file fd 1 is open on, so tail -f that
will just be the same as tail -f /dev/null
.
You could intercept those write()
system calls with strace
and decode the result:
strace -zqqs99999 -xxa0 -o /dev/stdout -e write -p "$pid" |
perl -ne 'print chr(hex($_)) for /\\x(..)/g'
Or:
strace -zqqs99999 -xxa0 -o /dev/stdout -e write -p "$pid" |
perl -ne 'if (/^write\(1,/) {print chr(hex($_)) for /\\x(..)/g}'
For only the write()
s to stdout (fd 1). Or:
strace -yzqqs99999 -xxa0 -o /dev/stdout -e write -p "$pid" |
perl -ne 'if (/^\Qwrite(1<\x2f\x64\x65\x76\x2f\x6e\x75\x6c\x6c>,\E "(.*)"/) {
print chr(hex($_)) for $1 =~ /\\x(..)/g}'
For only the write()
s to stdout (fd 1) and only when stdout is open on /dev/null
.
Note that if you redirect the output of perl
to something other than a tty device, it will start buffering by blocks instead of by line, so you'll only see the output in batches of a few kibibytes. You can disable that buffering by setting the $|
variable to 1
:
strace -zqqs99999 -xxa0 -o /dev/stdout -e write -p "$pid" |
perl -ne 'BEGIN{$| = 1}
print chr(hex($_)) for /\\x(..)/g' > file
Alternatively, instead of doing cmd > /dev/null
, do cmd | cat > /dev/null
.
Then cmd
's stdout will be a pipe, and on Linux, /proc/$pid_of_cmd/fd/1
will behave like a named pipe, so if you do:
kill -s STOP "$pid_of_cat"
cat "/proc/$pid_of_cmd/fd/1"
You will actually redirect the pipe to your new cat
command.
If you terminate that new cat
, you'll want to resume the other one (kill -s CONT "$pid_of_cat"
) to reopen the tap to /dev/null
on that pipe.
To actually change where stdout of the process goes, you could attach a debugger:
gdb --pid "$pid_of_cmd"
Then in gdb:
call close(1)
p open("/path/to/some/file", 0x241, 0600)
detach
(0x241 for O_WRONLY|O_TRUNC|O_CREAT)
Check that open()
returns fd 1. If not, you may need some call to dup2()
and an extra close()
.
/proc/$PID/fd/$N
is only a link to what ever file-like object the fd in question is connected to it, nothing else. If it's a file, you can use that to open the file. If it's/dev/null
, you can use that to open/dev/null
, though it still contains no data. – ilkkachu Jun 06 '23 at 08:21no resource
is indeed ambiguity, orminimal resource
is more precise...e.g., when i'm not retrieving it, the stdout could be discarded, when i start to retrieve, then the stdout could redirected to a log file. – furynerd Jun 06 '23 at 08:21