Bash – Why can’t I open a shell from a pipelined process

bashcommand lineshell

I've condensed my problem into the following code:

#include <stdio.h>

int main(){
    char buffer[256];

    printf("Enter input: ");
    scanf("%s", buffer);

    system("/bin/sh");
    return 0;
}

If I run this program with user input, I get:

user@ubuntu:~/testing/temp$ ./main
Enter input: Test
$ 

With the last line being the shell that the program started.

But if I run the program with input coming from the pipeline:

user@ubuntu:~/testing/temp$ echo 'test' | ./main
Enter input: user@ubuntu:~/testing/temp$

The program doesn't seem to open the shell.


After some tinkering, I realized that if I did this:

user@ubuntu:~/testing/temp$ (python -c "print 'test'" ; echo 'ls') | ./main
a.out  main  main.c
Enter input: user@ubuntu:~/testing/temp

I was able to run the ls command in the opened shell.

So, two questions:

  1. Why doesn't the shell open like it did in the first case?
  2. How can I deal with this? It's very inconvenient to have to decide what commands to run before running the program: I'd much rather have a shell where I can dynamically choose what commands to run.

Best Answer

  1. Why doesn't the shell open like it did in the first case?

In the first case, stdin is a terminal and the shell is interactive. It waits for your commands etc.

In the second case stdin is a pipe, and the shell is non-interactive. Your program consumes the first line on stdin (namely the string test\n), then the shell tries to read stdin and sees EOF. It exits, because that's what programs that get EOF on input are supposed to do.

In the third case the shell again is non-interactive, for the same reason. Your scanf() consumes the first line on stdin (i.e. test\n), then the shell reads ls. The shell runs ls, tries to read more commands, sees EOF, and exits.

  1. How can I deal with this?

If by "dealing with it" you mean running an interactive shell when stdin is connected to a pipe, the solution is to use a pty(7).