1

So I have a program that takes in user input and outputs text based on the input.

EDIT2: I want to create a script that runs a C executable and the script feeds the C program input from a file and redirect output to another file. HOWEVER I want to also print out the input whenever an output was printed for input basically combining/merging the input and output together. So I feed it input line by line in a file and then print output to a file for every input and I keep doing this. I do not want to re-run the program I want the program to still be running until it reaches the end of the input file or until I send it a kill signal from the script. (I'm trying to make a grading script for a class. Students submit their programs and I run this script and I feed input to their programs but I want to keep track of the input + output together so it makes it easier for me to keep track of the input for each of their output) I can figure out how to create the rest of the script myself but I'm just stuck at combining input + output together.

Edit3: I don't think expect will work because I have no way of knowing what the program will output. I only know what it's suppose to take in. For example one student might have a program say "Give me a character:" while another will say "Enter a char-" so I don't know what to expect from these programs.

(Note: I will have many different executable that will have different format but should all ask for same amount of input)

For example, one of the executable could be this:

"Enter a number: " (Wait for user input)
"Give another: "  (Wait for user input
"Total: " (Output based on input)
"Do you want to run again? "(Wait for user input)

EDIT: My main goal of my script is to run the program taking in input from a file line by line. For example for this program I will want it to take in the file "input" as stdin but I also want to keep track of the stdin merged with the stdout.

So I know ./a.out < input > output will output

Enter a number: 
Give another:  
Total:  15
Do you want to run again? 

However it's missing the input which is what I want included also so I can tell what the input was based on the output just by looking at the output file ONLY because the input could be 7 8 N or 5 10 N or anything like that.

4
  • 4
    Are you trying to reinvent expect ?
    – derobert
    May 1, 2013 at 17:03
  • I'm not exactly sure what expect does but I don't think its what I'm looking for. I have no control over the program I want to run. I just want to feed input to that program and merge the input + output together into a file
    – user38315
    May 1, 2013 at 17:51
  • After reading about Expect its not what I'm looking for ): So I'm making a grading script so the student's output are not all the same they might make different sentences for asking for user input like "Enter a character:" or "Please enter a char:" so I can't use expect or else I might have just used my script to get their diff haha but this is a introductory class so thats why I just want to look at their output instead of manually interacting with each of their programs
    – user38315
    May 1, 2013 at 18:04
  • I'm pretty sure you could get expect to do this (maybe by just waiting 1s between input lines?), but I'm not an expect expert at all...
    – derobert
    May 1, 2013 at 21:11

2 Answers 2

3

If I understand correctly, you want to detect when a.out is reading data from standard input, and when it does send it that data and also write that data to the same log file stdout is redirected to to simulate the local echo to the terminal when run interactively?

Then maybe a solution (bash syntax) would be something like:

mkfifo strace.fifo
{
  while read -d, trace; do
    if [[ $trace = *"read(0" ]]; then
      IFS= read -rn1 answer <&3 || break
      answer=${answer:-$'\n'}
      printf %s "$answer" >&2
      printf %s "$answer"
    fi
  done < strace.fifo 3< answers.txt |
    strace -o strace.fifo -e read ./a.out
} > log 2>&1

The idea is to use strace (assuming you're on Linux), to trace the read system calls and whenever there's a read on file descriptor 0, feed one character at a time from answers.txt.

Edit: if the program uses stdio or anything like that. What is likely to happen as the output is redirected to a regular file and is no longer a terminal is that all the prompts it is outputting are buffered and will only be flushed at the end when the program exits.

A work around would be to use stdbuf: replace ./a.out with stdbuf -oL ./a.out. That would tell the application (assuming it's a dynamically linked application and the buffering is due to stdio) to do line buffering on stdout as if it was a terminal. However, what it would still not do is flush stdout upon stdio reads from stdin as it would normally do if stdin/stdout were terminals. So for instance, a prompt not terminated by a newline character would not be displayed until an explicit fflush or until a newline character is eventually written. So best would probably be to use stdbuf -o0 to disable buffering altogether.

If a.out may fork processes or threads, add the -f option to strace.

That approach would not work if the application uses select or poll system calls to check if there's something to read on stdin before actually doing the read. Non-blocking I/O may also cause us to send data too quickly.

As mentioned in comments. expect is the tool to simulate user interaction, it uses a pseudo terminal, so you would automatically get the input echo and would not have the buffered output problem. As an alternative to stdbuf, you could use the unbuffer script that comes with it to wrap a.out in a pseudo terminal. In that case, you may want to add a little delay between detecting a read and sending the answer to allow for expect to reproduce the prompts on its stdout.

10
  • I tried running that but what it does is prints all the input first then output. So it would print 5 10 N then the output. I want it merged with the output Thanks though!!
    – user38315
    May 1, 2013 at 20:16
  • See my edit. If that still doesn't work, can you post the output of strace -f ./a.out somewhere? May 1, 2013 at 20:26
  • Hmm this is really close to what I need maybe I'll have to look into it to fix it around. Right now I'm getting the input printed before the output. For example: 5Enter a number: 10Enter another: Total: 15 NDo you want to run again?
    – user38315
    May 1, 2013 at 21:07
  • Thanks so much for your time (: I'll try to look into using what you gave me to make it work. I would try to up vote up your answer but it says I need 15 rep ):
    – user38315
    May 1, 2013 at 21:08
  • If you have time are you able to help me with getting it to print out the buffer if there is anything and then print out input when its given? If not I'll try to fiddle with it (: This already helped me a lot
    – user38315
    May 1, 2013 at 21:13
0

Use script for this. This is exactly what it was made for.

script /tmp/logfile -c 'your command here'

For example:

>> script /tmp/log -c 'read -p "Value: " value; echo "value=$value"'
Script started, file is /tmp/log
Value: foo
value=foo
Script done, file is /tmp/log

>> cat /tmp/log
Script started on Wed 01 May 2013 01:16:34 PM EDT
Value: foo
value=foo

Script done on Wed 01 May 2013 01:16:35 PM EDT
3
  • That still takes the input from the terminal (i.e., user typing). Its buried in OP's question, but "My main goal of my script is to run the program taking in input from a file line by line." (emphasis added)
    – derobert
    May 1, 2013 at 17:27
  • Yup this isn't what I"m looking for thanks though! I have no control over the program I want to run. I only have control of the input. I want to feed that program the input file I have.
    – user38315
    May 1, 2013 at 17:52
  • @derobert ah, didn't see that part. But in that case it's simple to just redirect from the file to STDIN. I'll add that to the answer. EDIT: actually that doesn't work like I expected it. Which actually has me quite surprised.
    – phemmer
    May 1, 2013 at 17:53

You must log in to answer this question.