Edit: I realized that the program I was trying to run had an infinite loop after scanning all my input. Then just prints out infinitely so it'll never read the EOF. It'll read the last input then go into an infinite loop

For example let's say the program is something like this.

printf("%c\n", getch());
   printf("%c\n", getch());

Is there a way to kill the program after reading in all my input? I want to be able to kill the program when it finishes redirecting the input file. (When the program gets the last character from the input file) Note: I cannot modify the program I'm running but I can modify the script that runs this program.

Right now I have this in my script to run the program. Basically this helps me combine/merge input + output together.

EDIT: Someone helped me with solving my earlier problem. I was suppose to add || break to this strace but I have a new problem which is in the edit at the top.

mkfifo strace.fifo
  while read -d, trace; do
    if [[ $trace = *"read(0" ]]; then
       IFS= read -rn1 answer <&3 || break
       echo "$answer" >> in
       printf %s "$answer" >&2
       printf %s "$answer"
  done < strace.fifo 3< input | strace -o strace.fifo -e read stdbuf -o0 ./a.out &
} >> $fname.out 2>&1

So right now I have a really hacky way of trying to end the program by using sleep and killing the program. Also I'm checking the input used so far and comparing it with the input file. This is just a small example of what I'm doing my current script has multiple timer and if comparisons similar to the code below. However this way is not a good way because if the program isn't done reading the input file after whatever second I put it won't kill the program. In my real script I used multiple of these if statements and it works like 80% of the time but sometimes it won't do the right thing probably because of the timer and the fluctuation of the way the program runs.

sleep .05
awk 'BEGIN{ORS="";} NR==1{print; next;} /^[^ ]/ { print; next; } {print "\n";}' in > temp
diff -w -B temp input > /dev/null
# Check if input done yet
if [ $? -eq 0 ] ; then
   # Check if program still running and input done
   if [ "$(pidof a.out)" ] ; then
      killall -15 a.out > /dev/null 2>&1

So I'm wondering is there a way to kill the process in the strace after input is done? Also I want to be able to keep input and output merged together.

  • What is "when it finishes redirecting the input file" supposed to mean? – Hauke Laging May 6 '13 at 1:19
  • For example when it finishes redirecting the input file to the program. Like I have an input file that contains 1\n2\n3. When it redirects the last character the 3 I want to kill the program – user38315 May 6 '13 at 1:36
  • 1
    If you re-write the entire question, you make all the answers look random. – msw May 8 '13 at 0:29

I think I've got a much easier solution but I'm not sure how portable it is. Consider:

$ cat trunc.c
#include <stdio.h>

int main() {
    int c;
    while(((c = getchar()) != EOF) && ((c & 0xff) != 0xff))
    return 0; 

This exploits the fact that on a two's-complement machine (-1 & 0xff) == 0xff. This catches your program that prints EOF as a character. I did test it:

your_buggy_source_gen < any_ascii_file | trunc

which is equivalent to cat any_ascii_file provided there are no octets of value 0xff in it.

  • Sorry ): I mentioned in the post that I have no access to the program. I can only modify the script that runs the program. Thanks for trying though. – user38315 May 6 '13 at 7:16
  • Right, and when trunc exits so does the upstream member of the pipe. If the program is even more misbehaved then your example shows, then we really can't answer your question based on what you've provided. To restate: this answer doesn't require "access to the program". Did you try it? – msw May 6 '13 at 11:01
  • I don't have a trunc command ): and I also wanted to keep the output with merged input + output. Thanks though! – user38315 May 6 '13 at 20:35
  • The trunc command is the code in my answer. – msw May 7 '13 at 0:25
  • Yea I just noticed lemme try it out – user38315 May 7 '13 at 0:35

"Now I'm wondering is there a way to check if the program goes into an infinite loop before all the input is read. For example if a program goes into an infinite loop, is the only way to stop the program."

We've known the answer to that question for nearly eighty years. The answer is no, there is not a way.

Thanks for giving a question that wasn't even close to your actual question:

"I'm trying to grade student's output. I just want to create a script so I can use for the future. Right now I have it working pretty well using a sleep timer but I want to make the script more robust without a timer"

Simple solution:

# how many seconds of CPU (not wall clock)
# should the student's program get?
# 10 seconds is generous for an intro class
# 60 seconds of CPU would be obscene
$ ulimit -t 10
$ run_student_program

# but since I don't have a student program

$ time dd if=/dev/zero of=/dev/null

real    0m9.998s
user    0m3.080s
sys     0m6.912s
  • Sorry about changing the question. I solved it myself for the question I had at the beginning where I wanted to kill the program after giving all the input out. I then realized that I had to address 1 more issue so I rephrased my whole question. So does ulimit -t just keep the program from running over a certain amount of time? What does time dd if=/dev/zero of=/dev/null do? And thanks for your time – user38315 May 7 '13 at 3:46
  • I decided to just stick with the timer I was using before. Thanks for your time. – user38315 May 7 '13 at 4:09

Not exactly a shell programming tip, but the reality is you need to modify the behaviour of the broken program even if you cannot modify its source code.

One way to do that would be a LD_PRELOAD module which intercepts the "read()" system call, in a way that invokes the actual "read()" system call and then exits when it encounters an end-of-file condition. (Ideally I'd intercept "getchar()" but that's usually a macro rather than a function call, so cannot be intercepted.)

Another way would be to run the code under a debugger, find where it goes into the infinite loop, and then patch the binary, either by modifying the executable file, or again, by applying a LD_PRELOAD module which replaces the errant function.

Or you could do as the previous answer suggests, and pipe the output into something which closes its input when the errant program outputs something that indicates it has reached the end of its input. (Note that this relies on it not trapping SIGPIPE, which is a fair assumption in this case.)

A more sophisticated version of this would be to share the input filedescriptor with another program which doesn't read from it but monitors to see whether it has reached its end. However that is a particularly tricky thing to do, especially if you might be dealing with pipes or sockets or devices.

Assuming your errant program is reading from stdin, wrap it with something like this:

  run-bad-program "$@" &
  perl -MFcntl=:mode -e '
    @S = stat STDIN or die "Invalid input; $!\n";
    S_IFREG($S[2]) or die "Input is not a plain file\n";
    while (sysseek(STDIN,0,1) != $S[7]) { sleep 1 }
  kill $pid
  • Sorry this doesn't do what I want ): Thanks though! I need something that doesn't use sleep and something that also merges the input + output together – user38315 May 7 '13 at 0:32
  • Could you explain what you mean by "merges the input + output together"? Do you mean it copies the input to the output, along with whatever output the program is generating? Where is the input coming from? (A file? pipe? tty?) Where is the output going to? – Martin Kealey May 7 at 6:56
  • If the bad program needs to be given a filename that it will open, then you're right, you can't used this shared filedescriptor cursor approach because (even if you do tricks with /dev/fd/N) it will still get a new filedescriptor with a new cursor. (At least in Linux; YMMV on other OSes.) – Martin Kealey May 7 at 7:08

You really should fix the broken program, but if you can't, you can work around it by having the shell cat the file and pipe it to the program, then sleep for a second or two before exiting, thus closing the pipe and killing the program with SIGPIPE.

(cat file ; sleep 2) | stupid_program
  • I don't want to sleep the program =/ and if I do something like this it won't merge my input + output together ): which is also something I want. Thanks though! – user38315 May 7 '13 at 0:22
  • I can't edit the broken program too ): I'm trying to grade student's output. I just want to create a script so I can use for the future. Right now I have it working pretty well using a sleep timer but I want to make the script more robust without a timer – user38315 May 7 '13 at 0:32
  • @Strawz, if your student's program goes into an infinite loop on hitting eof, that would qualify as failing the test. – psusi May 7 '13 at 0:35
  • Yup well this is their first programming class in college so most of them probably don't realize that their program is buggy. – user38315 May 7 '13 at 0:56
  • @Strawz, as their teacher it is your job to teach them that. – psusi May 7 '13 at 1:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy