2015-11-24 4 views
4

Мне нужно написать оболочку, которая может запускать трубы. Для команд пример, как ls -l | wc -l». Я успешно разобраны команды, заданной пользователем, как показано ниже:Трубы, dup2 и exec()

"LS"= firstcmd

"-l"= frsarg

"туалет"= SCMD

«-l» = secarg

Теперь я должен использовать две вилки, так как команды два и труба. Блочный код, который я написал EXEC команду заключается в следующем:

pid_t pid; 
int fd[2]; 

pipe(fd); 
pid = fork(); 

if(pid==0) 
{   
    dup2(fd[WRITE_END], STDOUT_FILENO); 
    close(fd[READ_END]); 
    execlp(firstcmd, firstcmd, frsarg, (char*) NULL); 
} 
else 
{ 
    pid=fork(); 

    if(pid==0) 
    { 
     dup2(fd[READ_END], STDIN_FILENO); 
     close(fd[WRITE_END]); 
     execlp(scmd, scmd, secarg, (char*) NULL); 
    } 
} 

Так что, когда я запускаю свою оболочку и я вхожу в команду ls -l | wc -l (например) результат от Execs не отображается, но shell работает нормально.

Странно, что результат команды отображается только тогда, когда я завершаю свою оболочку с помощью «exit» или «^ C».

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

+1

Вам необходимо закрыть FD-каналы в родительских процессах. – Barmar

+0

Можете ли вы изменить мой код, чтобы помочь мне понять, что вы имеете в виду, пожалуйста? @Barmar –

+0

** Правило большого пальца **: если вы дублируете один конец трубы на стандартный вход или стандартный вывод, вы должны закрыть оба конца исходного канала перед использованием 'exec *()' функций (или иначе продолжать). Есть исключения; их мало и далеко. Очень редко (на SO и IRL) вы сталкиваетесь с программой, использующей каналы, которые закрывают слишком много дескрипторов; очень часто бывает найти программу, которая не закрывает достаточно дескрипторов. Это особенно часто встречается, если несколько детей и/или несколько труб путают вещи. –

ответ

9

Вы должны закрыть все дескрипторы трубы как в родительском p rocess и дочерний процесс (после дублирования в дочернем процессе). В вашем коде основная проблема заключается в том, что процесс wc не выходит из-за того, что все еще присутствуют авторы (поскольку родительский процесс не закрыл конец записи). Изменения, показанные ниже. Я также добавил waitpid в родительский процесс, чтобы дождаться процесса wc.

pid_t pid; 
int fd[2]; 

pipe(fd); 
pid = fork(); 

if(pid==0) 
{ 
    dup2(fd[WRITE_END], STDOUT_FILENO); 
    close(fd[READ_END]); 
    close(fd[WRITE_END]); 
    execlp(firstcmd, firstcmd, frsarg, (char*) NULL); 
    fprintf(stderr, "Failed to execute '%s'\n", firstcmd); 
    exit(1); 
} 
else 
{ 
    pid=fork(); 

    if(pid==0) 
    { 
     dup2(fd[READ_END], STDIN_FILENO); 
     close(fd[WRITE_END]); 
     close(fd[READ_END]); 
     execlp(scmd, scmd, secarg,(char*) NULL); 
     fprintf(stderr, "Failed to execute '%s'\n", scmd); 
     exit(1); 
    } 
    else 
    { 
     int status; 
     close(fd[READ_END]); 
     close(fd[WRITE_END]); 
     waitpid(pid, &status, 0); 
    } 
} 
+0

О да. Это самый правильный ответ, который я видел. Он работает отлично. Огромное спасибо. Я застрял с ним часами. @ user1969104 –

1

Хм, достаточно близко. Вы пропустите, чтобы обращаться с несколькими файловыми дескрипторами после fork.

Вот некоторые ссылки:

  1. О трубы, http://unixwiz.net/techtips/remap-pipe-fds.html
  2. О дескриптора файла, http://www.usna.edu/Users/cs/aviv/classes/ic221/s14/lec/09/lec.html

Вот мой код:

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

using namespace std; 

#define INPUT_END 1 
#define OUTPUT_END 0 


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

    pipe(fd); 
    pid = fork(); 

    if(pid==0) 
    { 
     close(fd[INPUT_END]); 
     dup2(fd[OUTPUT_END], STDIN_FILENO); 
     close(fd[OUTPUT_END]); 
     execlp("wc", "wc", "-l",(char*) NULL); 
    } 
    else 
    { 
     pid=fork(); 

     if(pid==0) 
     { 
      close(fd[OUTPUT_END]); 
      dup2(fd[INPUT_END], STDOUT_FILENO); 
      close(fd[INPUT_END]); 
      execlp("ls","ls","-l",(char*) NULL); 
     } 

     close(fd[OUTPUT_END]); close(fd[INPUT_END]); 
    } 
} 
+1

В коде также есть проблемы. Первый INPUT_END/OUTPUT_END имеет неправильные значения в соответствии с документацией 'pipe'. Чтобы исправить эту проблему, вы заменили команды 'wc' и' ls'. Наконец, вы не закрыли дескрипторы в родительском процессе. – user1969104

+0

@ user1969104 Я запускаю ваш код, и это сработало, но я нашел другую проблему. Для вашей информации мой код (слишком большой для записи здесь) использует другие execs, если пользователь дает только ** «ls» ** или ** «ls -l» ** или ** «ls -l/bin «**. В общем случае команды с максимум двумя аргументами. Когда пользователь дает команду ** «ls -l | wc -l» ** все в порядке, результат отображается сразу же, но когда после этой команды я пытаюсь только ** «ls» ** или любую другую команду с ни одна труба ничего не происходит. Почему это происходит? –

+0

Я не уверен, что вы имеете в виду, не видя свой код, но, пожалуйста, убедитесь, что перенаправление труб (call pipe, fork, dup2) происходит только тогда, когда в аргументе есть вторая команда. В противном случае, если вы просто вызываете 'execlp (" ls "," ls "," - l ", (char *) NULL)', нет никаких причин, по которым нет вывода. – user1969104