0

is there a way to retrieve std out redirected to /dev/null? I tried tail -f /proc/{PID}/fd/1, looks like only works except redirects to /dev/null.

i.e.

tail -f /proc/${cmd_pid}/fd/1

works on cmd > log.txt but not cmd > /dev/null

------------update------------

in fact, my problem is log.txt will be quite large if always log the stdout. it will be best if i could control when to log or stop the log (without stopping the cmd process itself).

so if is possible to redirect the stdout to some tmp filesystem that occupies no resouce, and when i need i can retrieve the stdout?

3 Answers3

0

No, you can't change the file handle.

so if is possible to redirect the stdout to some tmp filesystem that occupies no resouce, and when i need i can retrieve the stdout?

No, you can't store the data without storing it somewhere.

Someone has probably written a cirular buffer - which would allow you to see (say) the last 100k of data. Alternatively if the program is normally running headless you could send the output to the screen and run it via screen.

symcbean
  • 5,540
0

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.

This could be done by piping the output to a shell script, like the following:

#! /bin/sh
# a-script.sh
while IFS= read -d input
do
  printf "%s\n" input >> /some/file
done 

Note that the redirection is done inside the loop, specifically so that the output file is opened anew in each iteration. Then, to start:

ln -s /dev/null /some/file
cmd | a-script.sh

Then when you want the logs:

ln -sf /some/log/file /some/file

And the next iteration of the loop will start writing to /some/log/file instead of to /dev/null.

If your command logs heavily, this could place a severe performance penalty.

muru
  • 72,889
0

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().

  • strace -zqqs99999 -xxa0 -o /dev/stdout -e write -p "$pid" | perl -ne 'print chr(hex($_)) for /\\x(..)/g' works, but only works on terminal output, how can i redirect this to some log file? i tried (strace -zqqs99999 -xxa0 -o /dev/stdout -e write -p "$pid" | perl -ne 'print chr(hex($_)) for /\\x(..)/g') >> t.log but no works – furynerd Jun 13 '23 at 04:55
  • @furynerd, you're probably seeing the effect of buffering, it's not that it doesn't work, it's that the output then comes in large blocks. See edit for how to work around that. – Stéphane Chazelas Jun 13 '23 at 05:30