Przekierowanie wyjścia z uruchomionego procesu

Zapytał mnie kiedyś kolega fizyk, jak zmienić przekierowanie wyjścia z uruchomionego programu. Miał bowiem uruchomiony na swoim komputerze napisany przez siebie program obliczający dane do jakiejś symulacji, a wyniki swoich obliczeń wyświetlał na ekranie. Wówczas nie odpowiedziałem mu natychmiast, ale problem okazuje się dość prosty.

Na potrzeby zobrazowania zagadnienia uruchomię taki prosty skrypt, który losuje i wyświetla 10 000 liczb (chodzi o to, by trwał długo i wyświetlał dużo wyników):

$ cat losowe.sh
#! /bin/bash

for i in 'seq 1 10000'; do echo $i $((RANDOM % 1000)); sleep 1; done

Uruchamiam:

$ ./losowe.sh
1 271
2 880
3 273
4 286
5 868
6 629
7 168
8 514
9 791
10 211
11 854
12 376
13 719
14 311
15 274
16 784

i dalej wyświetla wyniki...  I teraz należałoby czekać na jego zakończenie się. A co, jeśli potrzebujemy się wylogować z systemu i nie utracić wyników obliczeń? Co, jeśli nie możemy przerwać aplikacji, bo uruchomienie jej od nowa spowoduje utratę możliwości kontynuacji obliczeń np. ze względu na dane przechowywane w pamięci? Ano właśnie przekierujemy wyjście uruchomionego programu. :-)

1. Szukam numeru procesu uruchomionego skryptu:

$ ps -e | grep losowe
1358 pts/2 00:00:00 losowe.sh

2. Znajduję deskryptory tego procesu:

$ ls -log /proc/1358/fd
razem 0
lrwx------ 1 64 2011-01-16 21:14 0 -> /dev/pts/2
lrwx------ 1 64 2011-01-16 21:14 1 -> /dev/pts/2
lrwx------ 1 64 2011-01-16 21:13 2 -> /dev/pts/2
lr-x------ 1 64 2011-01-16 21:14 255 -> /home/tomek/losowe.sh
lr-x------ 1 64 2011-01-16 21:14 7 -> /proc/28874/status

3. Uruchamiam GDB i przekierowuję wyniki:

$ gdb -q -p 1358
Attaching to process 1358
Reading symbols from /bin/bash...(no debugging symbols found)...done.
Reading symbols from /lib/libncurses.so.5...(no debugging symbols found)...done.
Loaded symbols for /lib/libncurses.so.5
Reading symbols from /lib/tls/i686/cmov/libdl.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib/tls/i686/cmov/libdl.so.2
Reading symbols from /lib/tls/i686/cmov/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib/tls/i686/cmov/libc.so.6
Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib/ld-linux.so.2
0x00c63422 in __kernel_vsyscall ()
(gdb) p close(1)
$1 = 0
(gdb) p creat("/tmp/wyniki.txt", 0600)
$2 = 1
(gdb) q
A debugging session is active.

Inferior 1 [process 1358] will be detached.

Quit anyway? (y or n) y
Detaching from program: /bin/bash, process 1358

4. I teraz możemy spokojnie posłać program do pracy w tle i wylogować się, bo dane są już bezpieczne:

$ head /tmp/wyniki.txt
141 550
142 98
143 503
144 963
145 726
146 279
147 864
148 629
149 47
150 340

$ tail /tmp/wyniki.txt
1708 2
1709 430
1710 972
1711 575
1712 10
1713 444
1714 235
1715 638
1716 851
1717 851

Ktoś może powiedzieć, że trzeba było uruchomić program przy użyciu screen albo od razu uruchomić zapis danych do pliku, ale odpowiem trawestując znane powiedzenie: "Mądry fizyk po szkodzie...". ;-)