Execve ( "/bin/sh", 0, 0); в трубе

У меня есть следующая примерная программа:

#include <stdio.h>

int
main(int argc, char ** argv){
    char buf[100];

    printf("Please enter your name: ");
    fflush(stdout);
    gets(buf);
    printf("Hello \"%s\"\n", buf);

    execve("/bin/sh", 0, 0);
}

I, и когда я бегу без какой-либо трубы, он работает так, как должен, и возвращает sh promt:

bash$ ./a.out
Please enter your name: warning: this program uses gets() which is unsafe.
testName
Hello "testName"
$ exit
bash$

Но это не работает в трубе, я думаю, что я знаю, почему это так, но я не могу найти решение. Пример ниже.

bash$ echo -e "testName\npwd" | ./a.out
Please enter your name: warning: this program uses gets() which is unsafe.
Hello "testName"
bash$

Я полагаю, что это связано с тем, что gets пуст ет stdin таким образом, что /bin/sh получает EOF и быстро завершает работу без сообщения об ошибке.

Но как мне обойти это (не изменяя программу, если это возможно, и не удалять gets, если нет), так что я получаю promt, даже если я предоставляю вход через канал?

P.S. Я запускаю это на машине FreeBSD (4.8) D.S.

Ответ 1

Вы можете запускать свою программу без каких-либо изменений:

(echo -e 'testName\n'; cat ) | ./a.out

Таким образом вы гарантируете, что ваш стандартный ввод не закончится после выхода echo. Вместо этого cat продолжает подавать ввод в вашу программу. Источником этого последующего ввода является ваш терминал, так как он читает cat.

Вот пример сеанса:

bash-3.2$ cc stdin_shell.c 
bash-3.2$ (echo -e 'testName\n'; cat ) | ./a.out 
Please enter your name: warning: this program uses gets(), which is unsafe.
Hello "testName"
pwd
/home/user/stackoverflow/stdin_shell_question
ls -l
total 32
-rwxr-xr-x  1 user  group  9024 Dec 14 18:53 a.out
-rw-r--r--  1 user  group   216 Dec 14 18:52 stdin_shell.c
ps -p $$
  PID TTY           TIME CMD
93759 ttys000    0:00.01 (sh)
exit

bash-3.2$

Обратите внимание, что поскольку стандартный ввод оболочки не подключен к терминалу, sh считает, что он не выполняется интерактивно и, следовательно, не отображает подсказку. Однако вы можете вводить свои команды.

Ответ 2

Использование execve("/bin/sh", 0, 0); - жестокое и необычное наказание для оболочки. Это не дает никаких аргументов или среды вообще - даже не его собственное имя программы, и даже такие обязательные переменные окружения, как PATH или HOME.

Ответ 3

Не на 100% уверен в этом (точная оболочка используется, и ОС может немного отбросить эти ответы, я полагаю, что FreeBSD по умолчанию использует GNU bash как /bin/sh?), но

  • sh может обнаруживать, что его вход не является tty.

или

  • Ваша версия sh может перейти в неинтерактивный режим, например, если вызывается как sh, ожидая, что login добавит к ней - на argv[0]. Настройка execve ("/bin/sh", { "-sh", NULL}, NULL) может убедить ее, что она запускается как оболочка входа.