408

I have left a script running on a remote machine from when I was locally working at it. I can connect over SSH to the machine as the same user and see the script running in ps.

$ ps aux | grep ipcheck
myuser  18386  0.0  0.0  18460  3476 pts/0    S+   Dec14   1:11 /bin/bash ./ipchecker.sh

It is simply outputting to stdout on a local session (I ran ./ipchecker.sh form a local terminal window, no redirection, no use of screen etc).

Is there anyway from an SSH session I can view the output of this running command (without stopping it)?

So far the best I have found is to use strace -p 18386 but I get hordes of text flying up the screen, its far too detailed. I can stop strace and then sift through the output and find the text bring printed to stdout but its very long and confusing, and obviously whilst it's stopped I might miss something. I would like to find a way to see the script output live as if I was working locally.

Can anyone improve on this? The obvious answer is to restart the script with redirection or in a screen session etc, this isn't a mission critical script so I could do that. Rather though, I see this as a fun learning exercise.

Baldrick
  • 7,652

13 Answers13

379

You can access the output via the proc filesystem.

tail -f /proc/<pid>/fd/1

1 = stdout, 2 = stderr

(or like @jmhostalet says: cat /proc/<pid>/fd/1 if tail doesn't work)

tvlooy
  • 3,899
  • 1
  • 14
  • 4
  • 13
    It gives me: 'cannot open /proc//fd/1 for reading: No such device or address'. – Yaroslav Nikitenko Oct 27 '16 at 13:36
  • 32
    yeah should be your process id – tvlooy Nov 08 '16 at 14:10
  • 70
    This won't work if the output is going to a tty (or redirected to /dev/null) — it will only work if the output is redirected to a file. – mattdm Apr 26 '17 at 18:19
  • The output of yum is not loading, coz it is downloading some file from a repo and the progress is not showing under the file 1 – Ranvir Oct 05 '17 at 04:41
  • @mattdm facing same issue. is there any way to get stdout in that case? – mohitmun Feb 04 '18 at 19:06
  • are you sure it is the same process? Because if it exec's a wget or whatever then it's is a different process so another /fd/1 – tvlooy Feb 05 '18 at 15:33
  • 3
    Tested on Ubuntu 16.04 and it doesn't work. In a session I do: ping google.es and in another one as root: tail -f /proc/\pgrep ping`/fd/2` and nothing is shown. – david.perez Mar 02 '18 at 08:57
  • 1
    you are right. Because fd 1 and 2 are symlinks to the /dev/pts device. You can't tail -f a pts device. Schedule your ping command as a cronjob and then do the same. tail -f will then work – tvlooy Mar 02 '18 at 11:09
  • 1
    workaround for tail -f of pts device: gdb -p , p dup2(open("/dev/pts/", 1), 1), detach – tvlooy Mar 02 '18 at 11:11
  • 13
    tail doesn't work for me, I am using sudo cat /proc/<pid>/fd/1 instead – jmhostalet Feb 11 '19 at 08:39
  • 2
    somehow, neither of tail cat more works for me, but strace -ewrite does. I need stdout of a thread, strace -ewrite -p 50287 shows it: [pid 50287] write(1, "...", 97) = 97 - it does write to the 1 file descriptor. I see the output on the gnome terminal, but the utilities do not get it. – xealits Mar 30 '21 at 19:12
282

If all you want to do is spy on the existing process, you can use strace -p1234 -s9999 -e write where 1234 is the process ID. (-s9999 avoids having strings truncated to 32 characters, and write the system call that produces output.) If you want to view only data written on a particular file descriptor, you can use something like strace -p1234 -e trace= -e write=3 to see only data written to file descriptor 3 (-e trace= prevents the system calls from being loged). That won't give you output that's already been produced.

If the output is scrolling by too fast, you can pipe it into a pager such as less, or send it to a file with strace -o trace.log ….

With many programs, you can divert subsequent output with a ptrace hack, either to your current terminal or to a new screen session. See How can I disown a running process and associate it to a new screen shell? and other linked threads.

Note that depending on how your system is set up, you may need to run all these strace commands as root even if the process is running under your user with no extra privileges. (If the process is running as a different user or is setuid or setgid, you will need to run strace as root.) Most distributions only allow a process to trace its children (this provides a moderate security benefit — it prevents some direct malware injection, but doesn't prevent indirect injection by modifying files). This is controlled by the kernel.yama.ptrace_scome sysctl.

  • 24
    I don't suppose there's a way to narrow down the output to just the standard output? – Jonah Mar 28 '13 at 00:33
  • 4
    Can you explain all the arguments? – User Mar 16 '15 at 22:15
  • 7
    There was a lot of backslashes and numbers in a lot of the output I was getting from nodejs; Any leads on what encoding they might be in? There was plenty of plain text too, which was all i needed. – ThorSummoner Jul 16 '15 at 17:27
  • 2
    @ThorSummoner If you see a backslash followed by three octal digits between double quotes in the strace output, that means a byte whose numeric value is given by the digits. You'll see that for non-ASCII characters. – Gilles 'SO- stop being evil' Jul 16 '15 at 17:46
  • 4
    "Can you explain all the arguments?" @User: man strace – Pistos Sep 01 '15 at 15:32
  • I have a php process running a websocket and some variables inside, but I can't stop it right now to 'echo/print_r'. Is there a way to capture those variables 'on the fly'? – Rafael Moni Dec 22 '15 at 11:39
  • 5
    @RafaelMoni A program to do what you're asking is called a debugger. – Gilles 'SO- stop being evil' Dec 22 '15 at 12:12
  • 1
    @Gilles well, damn. I didn't build the application. So, I guess I'll have to 'dissect' the application. – Rafael Moni Dec 22 '15 at 12:25
  • 2
    I came here looking for STDout and got a treasure trove of valuable debug data. Thanks! – Adam Fowler Aug 25 '16 at 23:53
  • 2
    I hope I'm not saying something stupid, but to only get stdout you can use: strace -p1234 -s9999 -e write 2>&1|grep '^write(1' – Syco Sep 19 '18 at 16:23
  • 3
    @User https://explainshell.com/ allows you to paste a command and it'll explain the arguments, e.g. https://explainshell.com/explain?cmd=strace+-p1234+-s9999+-e+write – Daniel Serodio Oct 30 '18 at 20:41
  • worked for me with sudo – Saber Jun 18 '19 at 15:06
  • Thanks for this! See how to parse the output of strace in my answer below. – Jeff Ward Jul 03 '19 at 21:28
  • In case you're on a server which does not have strace by default, you can usually install it by running yum install strace. – Micros Sep 06 '19 at 10:13
  • To just get the current ffplay output, can strace be instructed to print one line and exit immediately? Currently it runs in a loop until <CTRL>+C is pressed. If relevant I will be calling strace from python and ffplay is also called from python but launched as a background task. Is a new question warranted in this case? – WinEunuuchs2Unix Mar 13 '21 at 16:55
  • @WinEunuuchs2Unix strace -p… … 2>&1 | head -n 1 will cause strace to exit when it can't write to the pipe anymore. I doubt that it would be useful to "get the current ffplay output" in this way, but there you are. – Gilles 'SO- stop being evil' Mar 13 '21 at 19:21
  • @Gilles'SO-stopbeingevil' Thank you. I agree strace is probably a silly way of solving the problem. I've actually started down a new path of parsing ffplay output redirected to temp file. – WinEunuuchs2Unix Mar 13 '21 at 19:45
17

In BSD, you can use watch which snoops a given tty, e.g.

watch /dev/pts/0

In Linux, it won't be possible if the process wasn't run under multiplexer before such as screen or tmux. See also: Reptyr: Attach a Running Process to a New Terminal

It seems the only way is to debug the process (e.g. strace, dtrace/dtruss, gdb, lldb, etc.).

Since you've used strace, to fetch any meaningful output, you need to filter by a qualifying expression (such as file), then parse the output. Here is example:

strace -e trace=write -s1000 -fp 18386 2>&1 | grep -o '".\+[^"]"'

What it does it prints write operation of the process (1000 length) specified by PID (use pgrep to find it by name), redirects standard error into output (to be filtered), and prints double-quoted string.

If you're dealing with binary output, you may parse escape sequences characters by using read (with -r) and printf (with %b), e.g.

while read -r -t1 line; do printf "%b" $line; done

Check help read for more parameters (e.g. -n to print after certain amount of characters, rather than newline).

Here is more complete example:

strace -e trace=write -s1000 -fp 18386 2>&1 \
| grep --line-buffered -o '".\+[^"]"' \
| grep --line-buffered -o '[^"]\+[^"]' \
| while read -r line; do
  printf "%b" $line;
done

For examples using any process, please check: How to parse strace in shell into plain text? at stackoverflow

kenorb
  • 20,988
9

Parsing the output of strace:

I used the top answer (with my process ID of 28223) ...

> sudo strace -p28223 -s9999 -e write
...
write(9, "Info\nI\nCare\nabout", 55) = 55
...

To determine that I care about write(9. (The 9 is used below, it's probably a file handle and might be different for your process.) Then I wrote a quick Ruby script to parse and display them.

Paste the following into /usr/bin/parse_strace.rb

#!/usr/bin/ruby

num = ARGV[0]
STDIN.each { |line|
  if (line.match(/write\(#{ num },\s*"(.*?)"/)) then
    puts $1.split('\x').map { |s| s.to_i(16).chr }.join()
  end
}

Don't forget chmod a+x /usr/bin/parse_strace.rb

I invoke strace -xx (outputs hex, so the regex matches properly), piping (including STDERR) to my script with 9 as the first arg.

sudo sh -c 'strace -xx -p28223 -s9999 -e write 2>&1 | parse_strace.rb 9'

And, voila, it outputs the process's original STDOUT, newlines, color, and all!

Attaching to process STDOUT

Jeff Ward
  • 643
8

If you want to get stderr and stdout you can just run this:

tail -f /proc/<pid>/fd/*
Oded BD
  • 181
6

You always can launch a process whith nohup and &

nohup rsync source_file dest_file &

Then, you'll can check the progress from any tty with:

tail -f nohup.out

This works fine to me.

4

I'd advise to make a named pipe (mkfifo) and then write to that file. Then, read from it. You can always do that with things like tail, to minimize output, etc. Whenever you clear the pipe (read from it), it gets cleared, so the output is not preserved.

The other option would be to write everything to a file (much like a logfile) and then analyze it an any time. This would be the preferred action, if you want to preserve all output.

slm
  • 369,824
polemon
  • 11,431
4
  1. You might be able to peek at the remote screen using ssh localhost 'DISPLAY=:0.0 xwd -root' | xwud -scale where localhost is to be replaced by your remote server login credentials and :0.0 with the display number of your GUI.

  2. Use x11vnc, which is a VNC server for your on screen X-session.

  3. When running on one of the 6 virtual consoles try sudo setterm -dump 2 -file /dev/stdout, where you replace 2 with the appropriate vc.

jippie
  • 14,086
1

A very simple to get the output would be capturing your output to a file and tailing that file.

IF DO:

./ipcheck

INSTEAD DO:

./ipcheck > [replacewithyourfilename]

This would create an output file where your script is located. Then from any other bash shell you can simply tail the file:

tail [replacewithyourfilename] -f
AdminBee
  • 22,803
MrAllen
  • 11
  • 1
  • File redirection tends to use block-based buffering by default (see setbuf(3)) which might make a tail problematical. – thrig Apr 26 '17 at 18:14
  • 8
    Also this doesn't help with the question, which was about how to view the output of an already running process, not how to redirect stdout for a new process. – Baldrick Apr 26 '17 at 18:19
1

I wrote catp to do this.

It's basically strace with only tracee's output extracted.

Usage: catp $PID

rapiz
  • 11
0

In bash 1:

any_command_you_want > my_output.txt&

In bash 2:

cat my_output.txt
URL87
  • 403
  • 1
    Pleas note that the OP said "Is there anyway from an SSH session I can view the output of this running command (without stopping it)?". While your approach doesn't require stopping the command, it would not help to watch the output of a command that is already running (as in the OPs problem). Also, I would at least consider using tail -f instead of cat so that the output can be followed in near-realtime. You may want to look at the OPs comment to this answer which is basically the same as yours. – AdminBee Jul 13 '20 at 09:19
0

If you have the commercial LSF from IBM as job scheduler, then you have also the option

bpeek displays the stdout and stderr output of an unfinished job

bpeek [-f] [-q queue_name | -m host_name | -J job_name | job_ID |
"job_ID[index_list]"]
Joniale
  • 101
0

Simple solution using strace, pgrep, grep and sed filtering.

  • create bash script
    nano ~/.local/bin/strace_cout
    
  • copy paste
    #/usr/bin/bash
    PID_CUR=$(pgrep $1)
    STREAM=1 # 1 = stdout 2 = stderr
    strace -p$PID_CUR -s9999 -e write 2>&1 \
     | grep --line-buffered -E "^write\($STREAM," \
     | sed -u -e 's/^[^"]*"//' | sed 's/\\n"[^"]*$//'
    
  • Example use:
    ./strace_cout name_of_process
    
AdminBee
  • 22,803
tomy
  • 101