2

My goal:

I would like to redirect the stdout output of my program foo to a changing output file depending on the running time of the program.

The program foo itself is listening on a bsd socket for arriving packets and displays information contained in them.
So essentially, after running the program foo for 10 minutes, I would like to have the stdout output of

  • the first minute inside the file bar_0.dat
  • the second minute inside the file bar_1.dat
  • the 10th minute inside the file bar_9.dat

Is it possible to achieve this in a shell script and if yes, how could I accomplish this?

What I have managed so far:

I only managed this solution, where the program is restarted after each minute with the redirection to a new output file:

#!/bin/bash
for i in {0..9}
do
  timeout 60s foo > "bar_${i}.dat"
done

However, I want the program foo to be running continuously and not having to restart it, because in the way I have realized it I am loosing some arriving packets (there is a 20-30ms gap between the running instances).

oh.dae.su
  • 607
  • 6
  • 12

3 Answers3

2

Let the program write to a named pipe (fifo) and then take the output from that pipe and put it in files. In the example here, I start the loop in the background and then immediately start writing to the named pipe:

mkfifo thepipe

for (( i = 0; i < 10; ++i )); do
    timeout 60 cat thepipe >"bar_$i.dat"
done &

foo >thepipe
rm -f thepipe

Alternatively, using a process substitution:

foo > >( 
    for (( i = 0; i < 10; ++i )); do
        timeout 60 cat >"bar_$i.dat"
    done
)
Kusalananda
  • 14,885
  • 3
  • 41
  • 52
  • Thanks a lot for your suggestion! The second solution (process substitution) works exactly as my intention was (+1 for that as soon as I have the necessary reputation). The first solution (pipe) fills the first file with the `stdout` of the first minute, however the following files are only created, but no data is entered. – oh.dae.su Jun 06 '18 at 10:18
  • After trying all suggested answers, your solution with the process Substitution is my favorite one. Could you remove or update the pipe solution (which currently is not working, see my previous comment)? Then I would switch the accept to your answer. Thanks a lot. – oh.dae.su Jun 07 '18 at 11:41
  • @oh.dae.su Thanks, it was an easy fix in the end. It should be working now. – Kusalananda Jun 07 '18 at 12:10
  • thanks for the update. unfortunately the pipe solution still only works until `bar_0.dat` is finished. then the behaviour changes from run to run. Sometimes it crashes with `cat: thepipe: No such file or Directory`. – oh.dae.su Jun 07 '18 at 13:49
1

If foo is producing text output, you might get away with something like:

#!/bin/bash

stamp=0
i=0
redirect() {
        if test "$(date +%s)" -gt "$((stamp + 60))"; then
                stamp=$(date +%s)
                exec > "bar_$((i++)).dat"
        fi
}
redirect
./foo | while read line; do
        echo "$line"
        redirect
done

If foo is not producing text output, you'll probably want to write foo so that it accepts an external input (eg, a signal) and redirects output on its own. Or, you might just want to use logrotate.

William Pursell
  • 204,365
  • 48
  • 270
  • 300
0

You could use an infinite loop and date/time stamp for the file name:

while true
do
    #This date/time stamp is month_day_hour_minute all numbers
    filename="bar_$(date "+%m_%d_%H_%M").dat"
    foo > $filename 2>> error_log
    sleep 60
done

Also added an error log for any error messages. Not sure how your program is handling stderr.

Jason
  • 2,493
  • 2
  • 27
  • 27
  • Thanks for your suggestion. However, as `foo` runs in an infinite loop itself, your code causes `stdout` to be written in a single file only never reaching the code line with `sleep 60`. – oh.dae.su Jun 06 '18 at 07:01
  • @oh.dae.su Bah. I believe I have misread your description =D. – Jason Jun 06 '18 at 13:02