Используйте здесь-документ с execvp

Я пытаюсь запустить команду "wc -l << END" с помощью execvp, поэтому я разбираю команду и запускаю execvp с "wc -l", но затем она переходит в бесконечный цикл.
Как мне заставить ее работать, чтобы она останавливалась, когда находит ключевое слово (в данном случае END)?

  • я должен использовать execvp
  • команда исходит от пользовательского ввода, который в данном случае "wc -l << END"

редактировать
Вот мой код (много не поможет, но может дать некоторую информацию) [код для здесь-документа находится в последнем операторе else]:

redirect(int proc, char * input){
    char * comm;
    int proc2;
    int append = 0;
    if(proc == 1){ //in
        comm = strsep(&input, "<");
        proc2 = check(input);
    }
    else{ //out
        comm = strsep(&input, ">");
        proc2 = check(input);
        if(proc2 == 2){ //append
            strsep(&input, ">");
            append = 1;
        }
    }

    if(proc2 == 0 || append == 1){ //only one redirection
        if(proc == 1){ //in
            input = trim(input);
            int fd = open(input, O_RDWR);
            close(0);
            dup2(fd, 0);
            close(fd);

            comm = trim(comm);
            char ** words = parse(comm);

            if(!execvp(words[0], words)){   /*exec failed */
                exit(1);
            }
        }
        else{ //out
            input = trim(input);

            int fd;
            if(append == 0){ //create
                fd = open(input, O_CREAT | O_RDWR | O_TRUNC, 
                    S_IRUSR | S_IWUSR);
            }
            else{ //append
                fd = open(input, O_CREAT | O_RDWR | O_APPEND, 
                    S_IRUSR | S_IWUSR);
            }

            dup2(fd, 1);
            close(fd);

            comm = trim(comm);
            char ** words = parse(comm);

            if(!execvp(words[0], words)){   /*exec failed */
                exit(1);
            }
        }
    }
    else{ //more than one redirection/pipe
        if(proc == proc2){ //here-doc
            strsep(&input, "<");
            input = trim(input);

        }

    }
}

Решением было бы сохранить ввод пользователя во временный файл, а затем передать временный файл как стандартный ввод, но я хотел знать, есть ли лучший способ сделать это.


person Mark    schedule 10.12.2014    source источник
comment
execvp() не использует оболочку для запуска программы, поэтому напрямую использовать функции оболочки не получится. Вместо этого вы можете запустить оболочку и передать ей соответствующие аргументы...   -  person Dmitri    schedule 10.12.2014
comment
Похоже, вы пытаетесь просто изменить stdin/stdout и exec() в исходном процессе, а не в дочернем? Возможно, вам придется fork() запустить команду в дочернем процессе и передать ему входные данные от родителя (который будет следить за маркером END).   -  person Dmitri    schedule 11.12.2014
comment
@Dmi: я уже это делаю (не хотел публиковать весь код). вопрос в том, можно ли изменить стандартный ввод на что-то другое, кроме временного файла.   -  person Mark    schedule 11.12.2014
comment
Да... вы можете использовать канал вместо временного файла. Примеры того, как писать в дочерний stdin с помощью каналов, есть везде.   -  person Dmitri    schedule 11.12.2014


Ответы (2)


Вместо того, чтобы использовать временный файл и указывать на него дочерний стандартный ввод, вы можете передавать входные данные дочернему процессу через конвейер либо из родительского, либо из другого дочернего процесса. Затем родительский или второй дочерний процесс позаботится о чтении ввода, отслеживании маркера конца ввода и передаче данных вашему (исходному) дочернему процессу.

В следующем примере показано, как использовать канал и второй дочерний процесс для чтения стандартного ввода, отслеживания маркера конца ввода и передачи ввода исходному дочернему процессу, пока он не будет найден. В примере отсутствует проверка ошибок (которую следует добавить) и используются жестко запрограммированные значения для выполняемой команды и маркер конца ввода. Обратите внимание, что важно, чтобы конец канала для записи был закрыт в родительском объекте, чтобы команда увидела, что он закрыт после выхода из средства чтения ввода.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>

int main(int argc, char *argv[])
{
  pid_t rdr,  /* input reader process */
        cmd,  /* command runner process */
        wres; /* wait() result */
  /* pipe for passing input from rdr to cmd */
  int pipefd[2] = {0};
  /* command and args to pass to execvp() */
  char *command = "wc";
  char *cmdargs[] = { "wc", "-l", NULL };
  /* end of input marker */
  char *endinp = "END\n";

  /* create a pipe: 
     - read end's fd will be pipefd[0], 
     - write end's fd will be pipefd[1] */
  pipe(pipefd);

  cmd = fork();
  if (!cmd) {  /* COMMAND RUNNER PROCESS */
    dup2(pipefd[0],0); /* set stdin to pipe's read end */
    close(pipefd[1]);  /* close pipe's write end */
    /* exec command (will read from stdin) */
    execvp(command, cmdargs);
  }

  rdr = fork();
  if (!rdr) {   /* INPUT READER PROCESS */
    close(pipefd[0]); /* close pipe's read end */

    /* read input from stdin until a line containing only
       the end of input marker is found */
    char buf[1024];
    while (fgets(buf,sizeof(buf),stdin)) {
      /* break when end of input marker is read */
      if (!strcmp(buf,endinp)) break;
      /* write data to pipe */
      write(pipefd[1],buf,strlen(buf));
    }
    return 0;
  }

  /* PARENT PROCESS */

  close(pipefd[0]); /* close pipe's read end */
  close(pipefd[1]); /* close pipe's write end */

  /* wait for both children to exit */
  do {
    wres = wait(NULL);
    if (wres == rdr) rdr = 0;
    if (wres == cmd) cmd = 0;
  } while (rdr || cmd);

  return 0;
}
person Dmitri    schedule 11.12.2014

Обычно вы публикуете свой вопрос вместе со своим кодом, иначе мы не сможем должным образом помочь вам развить ваши навыки программирования. На этот раз я дам вам кусок пирога и дам вам пример кода (который может быть полным ответом).

#include <stdio.h>
#include <unistd.h>
#include <err.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
    pid_t pid;
    int status;


    // you must provide the full path for the executable
    // such as /bin/echo, /usr/bin/wc ...
    if(argc < 2)
    {
        printf("expected format example /usr/bin/wc -l << END\n");
        return (-1);
    }

    // fork your process since EXEC family
    // takes control over your process
    // This way you may do other things
    if((pid = fork()) < 0)
        err(1, "fork() error");

    if(pid == 0) /* child */
    {
        // argv[0] is the name of your program executable
        // first argument to execvp is the target executable
        // with the full path, again /bin/echo
        // segund argument is a list of arguments for your
        // target null terminated. Note that the first
        // argument of that list is the name of the executable/location
        // itself
        // illustrative: execvp("/bin/echo", {"/bin/echo", "example", NULL}) 
        if(execvp(argv[1], &argv[1]) < 0)
            err(1, "execvp() error");
    }
    else /* father */
    {
        printf(">> father waiting child..\n");
        while(wait(&status) != pid);
        printf(">> child finished\n");
    }

    return (0);
}

Вы можете взглянуть на это, если хотите глубже понять эту тему.

person delirium    schedule 10.12.2014