skip to Main Content

My console program runs under Ubuntu 20.04. When run interactively, the input expression (e.g., a+b below) is displayed next to the prompt followed by the output on a new line as follows. I would like to achieve this same format when providing the input to the program from a bash script.

$ ./rec_desc1
Enter expression: a+b
F --> id
R --> eps
T --> FR
F --> id
R --> eps
T --> FR
Q --> eps
Q --> +TQ
E --> TQ
Success. 

But if I send the input from a bash script via here-string or piping it (as suggested at How do I provide input to a C program from bash?), the input is not displayed and unfortunately the first line of output is shifted up next to prompt as shown below.

cat rec_desc1_unit_test
#!/bin/bash
echo "Test case 1"
./rec_desc1 <<< a+b

$ ./rec_desc1_unit_test
Test case 1
Enter expression: F --> id
R --> eps
T --> FR
F --> id
R --> eps
T --> FR
Q --> eps
Q --> +TQ
E --> TQ
Success. 

Is there a way to pass the input from a bash script so that it displays next to the prompt and is followed by a newline so it matches the interactive case above?

I eventually want to run numerous test cases for different expressions from a bash script and would like to have the self-documentation of the input expression being displayed. I could call set -v prior to invoking the console program in order to display the input if necessary (as shown below). Then I would need to call set +v immediately afterward to avoid displaying the echo statements that describe the next test case. I would like to avoid surrounding each test case with set -v and set +v if possible.

$ cat rec_desc1_unit_test
#!/bin/bash
echo "Test case 1"
set -v
./rec_desc1 <<< a+b

$ ./rec_desc1_unit_test
Test case 1
./rec_desc1 <<< a+b
Enter expression: F --> id
R --> eps
T --> FR
F --> id
R --> eps
T --> FR
Q --> eps
Q --> +TQ
E --> TQ
Success. 

2

Answers


  1. You’ll have to post-process the output; making the whole thing a shell function might be a good idea:

    run_rec_desc1() {
        printf '%sn' "$1" |
        ./rec_desc1 2>&1 |
        awk -F': ' -v input="$1" '
            NF==2 {print $1 FS input; $0 = $2}
            {print}
        '
    }
    
    $ run_rec_desc1 a+b
    Enter expression: a+b
    F --> id
    R --> eps
    T --> FR
    F --> id
    R --> eps
    T --> FR
    Q --> eps
    Q --> +TQ
    E --> TQ
    Success.
    
    Login or Signup to reply.
  2. The fact that rec_desc1 is a C program, and what it does with it’s input is irrelevant to your question assuming you don’t want that to change.

    Here’s a shell script that produces the behavior you describe, i.e. prompts for input and produces some output based on that input (in this case print a series of numbers):

    $ cat rec_desc1
    #!/usr/bin/env bash
    
    printf 'Enter a number: ' >&2
    if IFS= read -r rsp; then
        seq "$rsp"
        printf 'Success.n' >&2
    fi
    

    Here it is being run manually:

    $ ./rec_desc1
    Enter a number: 3
    1
    2
    3
    Success.
    

    Here is the equivalent of your test script calling it:

    $ cat rec_desc1_unit_test
    #!/usr/bin/env bash
    
    printf 'Test case 1n'
    ./rec_desc1 <<< 3
    

    and the output that produces:

    $ ./rec_desc1_unit_test
    Test case 1
    Enter a number: 1
    2
    3
    Success.
    

    Now, here is a modified version of your caller test script:

    $ cat rec_desc1_unit_test
    #!/usr/bin/env bash
    
    run_test() {
        local title="$1" cmd="$2" input="$3" output
        printf '%s: %s %sn' "$title" "$cmd" "$input"
    
        coproc "$cmd"
    
        printf '%sn' "$input" >&"${COPROC[1]}"
        input="$input"$'n'
        while IFS= read -r output; do
            printf '%s%sn' "$input" "$output"
            input=''
        done <&"${COPROC[0]}"
    
        wait "$COPROC_PID"
    
        printf '========n'
    }
    
    run_test 'Test Case 1' ./rec_desc1 3
    
    run_test 'Test Case 2' ./rec_desc1 5
    

    and the output that produces:

    $ ./rec_desc1_unit_test
    Test Case 1: ./rec_desc1 3
    Enter a number: 3
    1
    2
    3
    Success.
    ========
    Test Case 2: ./rec_desc1 5
    Enter a number: 5
    1
    2
    3
    4
    5
    Success.
    ========
    

    That uses a coprocess to run ./rec_desc1 feeding it input from the calling script and reading it’s output into the calling script. Alternatively you could use expect to do the same.

    The above relies on run_desc1 sending prompts and other status messages to stderr as it should. If it doesn’t do that then you need a little different solution to read the prompt, etc. but that’s complicated by the fact that the prompt doesn’t end in a newline while read only reads newline-terminated lines so you’d need to use a different solution than just read to read the output from run_desc1.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search