cat vs echo или printf для передачи данных на стандартный ввод программы

Предположим, у нас есть следующая программа с именем myprog:

#include <stdio.h>
#include <stdlib.h>

void main(){
        char buffer[32];
        gets(buffer);
        system("/bin/sh");
}
  1. Занимает место для буфера
  2. Он ожидает данных со стандартного ввода и копирует их в буфер до тех пор, пока не будет найдена новая строка.
  3. Он выполняет оболочку как дочерний процесс, затем он будет ждать команд от стандартного ввода

Итак, у нас есть 2 инструкции, которые будут искать данные из stdin.

Предположим, мы хотим передать эти данные, перенаправляя стандартный вывод других программ на стандартный ввод myprog, возьмем, например, echo и cat.

$ cat > cmds
ls
$ { echo "My string" ; cat cmds ; } | ./myprog
file1 file2 cmds myprog 

Пока все хорошо: echo автоматически добавляет символ новой строки в конец «Моя строка», это заставляет gets прекращать чтение и /bin/sh читать из вывода cat.

Но попробуем другие решения:

  1. echo "My string" ls | ./myprog

  2. printf "My string\nls\n" | ./myprog

  3. {echo "My string" ; echo ls ; } | ./myprog

Ни одно из этих решений, похоже, не работает. Также не работает использование одного файла и одного вызова cat:

$ cat > file
 My string
 ls
$ cat file | ./myprog

Почему так происходит? Что именно происходит в каждом из этих случаев?


person TTK    schedule 28.10.2015    source источник
comment
Как вы его скомпилируете? Если неясно, этот скрипт нельзя запустить без его компиляции. Появляются ли сообщения об ошибках?   -  person l0b0    schedule 28.10.2015
comment
Я собираюсь угадать буферизацию по стандартному вводу и времени. Также gets ужасен.   -  person Etan Reisner    schedule 28.10.2015
comment
Что ж, №1 должен потерпеть неудачу; он выводит только одну строку.   -  person chepner    schedule 28.10.2015
comment
Re: time, обратите внимание, что cat - это внешняя программа, и есть небольшая задержка при запуске cat между выводом My string и содержимым файла. Во всех других ваших примерах bash внутренние элементы используются для вывода обеих строк. Это означает, что gets может читать не только первую строку и буферизовать ее для следующего вызова gets, что означает, что /bin/sh не получает ожидаемые данные. Вы можете подтвердить, заменив /bin/sh на cat, чтобы увидеть, что на самом деле находится в стандартном вводе при вызове system.   -  person chepner    schedule 28.10.2015
comment
@ l0b0 Что ты имеешь в виду? Я компилирую программу с помощью простого gcc myprog.c -o myprog @EtanReisner. Я знаю, что это ужасно, это просто пример. @chepner Вы правы, первая явно неверна, моя ошибка. Вы правы и в отношении времени: после использования system("cat") и просмотра содержимого буфера я вижу, что что-то вроде { echo "My string" ; sleep 1 ; echo ls ; } работает правильно. Спасибо. С другой стороны, {echo "My string" ; echo ls ; } заставляет gets сохранять в буфере только "My string". ls все еще идет на вход gets без копирования?   -  person TTK    schedule 28.10.2015
comment
Проблема с синхронизацией возникает из-за того, что ваша ОС выбирает расписание в отношении того, когда различные процессы получают время ЦП. Он может варьироваться от запуска к запуску и в зависимости от текущей загрузки системы.   -  person chepner    schedule 29.10.2015


Ответы (1)


Я думаю, вы немного запутались в своем подходе к программе, которая будет принимать входные данные от stdin. Если я правильно понимаю, что ваша цель состояла в том, чтобы system("/bin/sh"); ожидал "ls" в качестве ввода от stdin, то в случае успеха вы ожидаете увидеть ошибку:

/usr/bin/ls: /usr/bin/ls: cannot execute binary file

Вы видели комментарии к gets - никогда не используйте его. Если ваш учитель предлагает вам использовать его, вы можете сказать ему, что он был исключен из стандартной библиотеки C из-за уязвимостей безопасности. Вместо этого используйте fgets.

Тем не менее, если вы хотите получить ввод от stdin и хотите использовать этот ввод в вызове system, вам нужно создать строку, содержащую команду, которую вы ожидаете от system, включая любые требуемые аргументы. Простой вызов system("/bin/sh"); не сработает, потому что system выполняет свою команду в подоболочке.

Просто создайте строку, которую вы хотите system выполнить. Простым примером может быть:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXC 32

int main (void) {

    char buffer[MAXC] = {0};
    char script[MAXC] = {0};

    /* read line 1 into buffer & print */
    fgets (buffer, MAXC, stdin);
    printf ("\n buffer: %s", buffer);

    /* read line 2 into buffer & print */
    fgets (buffer, MAXC, stdin);
    printf ("\n buffer: %s", buffer);

    /* copy "/bin/sh" to script */
    strncpy (script, "/bin/sh ", strlen ("/bin/sh ") + 1);

    /* concatenate script & buffer ( "/bin/sh + line 2" ) */
    strcat (script, buffer);

    /* execute script */
    system (script);

    return 0;
}

Обычно он берет line 1 из stdin и печатает его, а затем перезаписывает его на line 2, который содержит команду (имя сценария) для вызова /bin/sh. Вы можете использовать простой тестовый сценарий, например:

#!/bin/sh

printf "\n %s executed : %s\n\n" "$0" "hello /bin/sh"

exit 0

Теперь вы можете использовать printf, чтобы передать в вашу программу как строку-1, так и строку-2, примерно так:

$ printf "%s\n%s\n" "buffer - line 1" "myscript.sh"

Которая передает в вашу программу одну строку за раз. Запуск кода, который вы ожидаете увидеть:

$ printf "%s\n%s\n" "buffer - line 1" "myscript.sh" | ./bin/system_buff_stdin

 buffer: buffer - line 1

 buffer: myscript.sh

 myscript.sh executed : hello /bin/sh

Если у вас есть дополнительные вопросы, просто задавайте их.

person David C. Rankin    schedule 28.10.2015
comment
Спасибо за ваш ответ. Поскольку это на самом деле упражнение, я не могу изменить исходный код ... но я полностью согласен с тем, что вы сказали. С другой стороны, system выполняет свою команду в подоболочке ... ну, моя цель - передать входные данные этой подоболочке из внешней программы, это можно сделать, например, используя echo, а затем cat. У меня был вопрос: почему он не работает с любой комбинацией команд, которые выводят 2 строки? - person TTK; 28.10.2015
comment
Мои извинения, я понимаю, что вы говорите. Вы также можете использовать подстановку процесса, чтобы сделать то же самое. (например, ./bin/system_gets < <(echo "my cmds"; cat cmds)) Вот где файл cmds содержит ls, как в вашем примере. Когда вы pipe блок {...} или используете подстановку процесса < <(stuff) (что является бешенством), вы перенаправляете блок (или вывод подоболочки), содержащий две ваши команды, в вашу программу. (gets и system). - person David C. Rankin; 28.10.2015