hugogogo
hugogogo

Reputation: 637

why read in bash seems to write on the standard input

i'm really confuse i don't know what's going on, so i'm not sure what to ask :/

i was going to use this question to send input to the standard input of a program, in a bash script (i want program.c to read on stdin and print what it reads, i do that with readline without trouble, and i want scrit.sh to send to stdin what program.c will read, and i was doing it in a complicated way, when i realized that a while loop with read in the middle of my code was already accomplishing this task, and i don't know why !)

so it works without me sending anything intentionally to the STDIN (i do obviously, but it was not my intention when writing this loop), but i don't understand what's going on

here is how it looks : i use a bash script to call a program.c, which use readline to read the stdin and print it. The script.sh i wrote below should absolutely not work for what i understant (not much), but it does (almost) !

program.c :

#include <unistd.h>
#include <stdlib.h>
#include <readline/readline.h>

int main(void)
{
    char    *line_input;

    line_input = readline("[myPrompt]> ");
    if (!line_input)
    {
        write(1, "exit\n", 5);
        exit(0);
    }
    write(1, line_input, strlen(line_input));
    write(1, "\n", 1);
    return (0);
}

script.sh :

#!/bin/bash

VAR="\
one
two"

while read -r line
do
    echo "line: $line"
    ./program
done < <(echo "$VAR")

and the output :

line: one
[myPrompt]> two
two
[myPrompt]> exit

so the word two is written to the right of [myPrompt]> , i guess the stdin, and it's executed by ./program.c

why ? i never sent it, i just called ./program in the while loop of the script, but it don't send anything to stdin

and why does it start at the second word ?

i can't reproduce this behavior if i don't use read in my loop, so it could be related

Upvotes: 0

Views: 196

Answers (1)

Barmar
Barmar

Reputation: 781200

< <(echo "$VAR") uses process substitution to redirect the standard input of the loop to the output of the echo "$VAR" command. It affects the entire loop, not just the read -r line line.

./program inherits stdin from this, so it also reads from the process substitution. read -r line reads the first line, then readline() in ./program reads the second line.

And because readline() is intended for interactive input, it echoes the input as it's reading it. That's why you see it on the prompt line, where you would see it if you were responding interactively.

If you want the program to read from the terminal rather than the input redirection, you can redirect it to /dev/tty:

#!/bin/bash

VAR="\
one
two"

while read -r line
do
    echo "line: $line"
    ./program </dev/tty
done < <(echo "$VAR")

You can also save the original stdin that the script inherited, and redirect to that.

#!/bin/bash

VAR="\
one
two"

while read -r line
do
    echo "line: $line"
    ./program.c <&3
done 3<&0 < <(echo "$VAR")

BTW, instead of using process substitution, you can use a here-string: <<<"$VAR"

Upvotes: 3

Related Questions