簡體   English   中英

如何在Shell程序中實現&>和&>>?

[英]How to implement &> and &>> in a shell program?

我正在實現自己的外殼程序,並且已經設法使I / O重定向與管道一起工作。 但是,我也無法理解重定向stderr所應該做的事情,因此我也可以在代碼中合並>&>>&功能。

另外,實現|&的邏輯是否遵循?

這是我的代碼:

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

void execute(char **, int, char **);
void handle_signal(int);
int parse(char *, char **, char **, int *);
void chop(char *);

#define INPUT_STRING_SIZE 80

#define NORMAL              00
#define OUTPUT_REDIRECTION  11
#define INPUT_REDIRECTION   22
#define PIPELINE            33
#define BACKGROUND          44
#define OUTPUT_APP  55

typedef void (*sighandler_t)(int);

int main(int argc, char *argv[])
{
    int i, mode = NORMAL, cmdArgc;
    size_t len = INPUT_STRING_SIZE;
    char *cpt, *inputString, *cmdArgv[INPUT_STRING_SIZE], *supplement = NULL;
    inputString = (char*)malloc(sizeof(char)*INPUT_STRING_SIZE);

    char curDir[100];

    while(1)
    {
        mode = NORMAL;
        getcwd(curDir, 100);
        printf("%s@%s->", getlogin(),curDir);
        getline( &inputString, &len, stdin);
        if(strcmp(inputString, "exit\n") == 0)
            exit(0);
        cmdArgc = parse(inputString, cmdArgv, &supplement, &mode);
        if(strcmp(*cmdArgv, "cd") == 0)
        {
            chdir(cmdArgv[1]);
        }
        else 
            execute(cmdArgv, mode, &supplement);
    }
    return 0;
}

int parse(char *inputString, char *cmdArgv[], char **supplementPtr, int *modePtr)
{
    int cmdArgc = 0, terminate = 0;
    char *srcPtr = inputString;
    //printf("parse fun%sends", inputString);
    while(*srcPtr != '\0' && terminate == 0)
    {
        *cmdArgv = srcPtr;
        cmdArgc++;
        //printf("parse fun2%sends", *cmdArgv);
        while(*srcPtr != ' ' && *srcPtr != '\t' && *srcPtr != '\0' && *srcPtr != '\n' && terminate == 0)
        {
            switch(*srcPtr)
            {
                case '&':
                    *modePtr = BACKGROUND;
                    break;
                case '>':
                    *modePtr = OUTPUT_REDIRECTION;
                    *cmdArgv = '\0';
                    srcPtr++;
                    if(*srcPtr == '>')
                    {
                        *modePtr = OUTPUT_APP;
                        srcPtr++;
                    }
                    while(*srcPtr == ' ' || *srcPtr == '\t')
                        srcPtr++;
                    *supplementPtr = srcPtr;
                    chop(*supplementPtr);
                    terminate = 1;
                    break;
                case '<':
                    *modePtr = INPUT_REDIRECTION;
                    *cmdArgv = '\0';
                    srcPtr++;
                    while(*srcPtr == ' ' || *srcPtr == '\t')
                        srcPtr++;
                    *supplementPtr = srcPtr;
                    chop(*supplementPtr);
                    terminate = 1;
                    break;
                case '|':
                    *modePtr = PIPELINE;
                    *cmdArgv = '\0';
                    srcPtr++;
                    while(*srcPtr == ' ' || *srcPtr == '\t')
                        srcPtr++;
                    *supplementPtr = srcPtr;
                    //chop(*supplementPtr);
                    terminate = 1;
                    break;
            }
            srcPtr++;
        }
        while((*srcPtr == ' ' || *srcPtr == '\t' || *srcPtr == '\n') && terminate == 0)
        {
            *srcPtr = '\0';
            srcPtr++;
        }
        cmdArgv++;
    }
    /*srcPtr++;
    *srcPtr = '\0';
    destPtr--;*/
    *cmdArgv = '\0';
    return cmdArgc;
}

void chop(char *srcPtr)
{
    while(*srcPtr != ' ' && *srcPtr != '\t' && *srcPtr != '\n')
    {
        srcPtr++;
    }
    *srcPtr = '\0';
}

void execute(char **cmdArgv, int mode, char **supplementPtr)
{
    pid_t pid, pid2;
    FILE *fp;
    int mode2 = NORMAL, cmdArgc, status1, status2;
    char *cmdArgv2[INPUT_STRING_SIZE], *supplement2 = NULL;
    int myPipe[2];
    if(mode == PIPELINE)
    {
        if(pipe(myPipe))                    //create pipe
        {
            fprintf(stderr, "Pipe failed!");
            exit(-1);
        }
        parse(*supplementPtr, cmdArgv2, &supplement2, &mode2);
    }
    pid = fork();
    if( pid < 0)
    {
        printf("Error occured");
        exit(-1);
    }
    else if(pid == 0)
    {
        switch(mode)
        {
            case OUTPUT_REDIRECTION:
                fp = fopen(*supplementPtr, "w+");
                dup2(fileno(fp), 1);
                break;
            case OUTPUT_APP:
                fp = fopen(*supplementPtr, "a");
                dup2(fileno(fp), 1);
                break;
            case INPUT_REDIRECTION:
                fp = fopen(*supplementPtr, "r");
                dup2(fileno(fp), 0);
                break;
            case PIPELINE:
                close(myPipe[0]);       //close input of pipe
                dup2(myPipe[1], fileno(stdout));
                close(myPipe[1]);
                break;
        }
        execvp(*cmdArgv, cmdArgv);
    }
    else
    {
        if(mode == BACKGROUND)
            ;
        else if(mode == PIPELINE)
        {
            waitpid(pid, &status1, 0);      //wait for process 1 to finish
            pid2 = fork();
            if(pid2 < 0)
            {
                printf("error in forking");
                exit(-1);
            }
            else if(pid2 == 0)
            {
                close(myPipe[1]);       //close output to pipe
                dup2(myPipe[0], fileno(stdin));
                close(myPipe[0]);
                execvp(*cmdArgv2, cmdArgv2);
            }
            else
            {
                ;//wait(NULL);
                //waitpid(pid, &status1, 0);
                //waitpid(pid2, &status2, 0);
                close(myPipe[0]);
                close(myPipe[1]);
            }
        }
        else
            waitpid(pid, &status1, 0);
            //wait(NULL);
    }
}

任何幫助將不勝感激!

好的,按照喬納森·勒夫勒的建議,我修改了代碼以包含重定向。 現在,我想看看是否可以通過嘗試使用戶輸入“>&”而不是“&>”並仍然實現“>&”的功能來對其進行一些更改。 “&>>”和“&|”也是如此。

但是,當我嘗試發出命令時,說echo hello>&a.txt,我得到一個名稱為“&”的文件,該文件現在包含字符串hello! 我不確定這里出了什么問題。 @JonathanLeffler-您能看看我可能做錯了什么嗎? 這是更新的代碼:

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


void execute(char **, int, char **);
void handle_signal(int);
int parse(char *, char **, char **, int *);
void chop(char *);

#define INPUT_STRING_SIZE 80

#define NORMAL              00
#define OUTPUT_REDIRECTION  11
#define INPUT_REDIRECTION   22
#define PIPELINE            33
#define BACKGROUND          44
#define OUTPUT_APP  55
#define OUTPUT_REDIRECTION_WITH_ERROR 66
#define OUTPUT_REDIRECTION_WITH_APPEND_ERROR 77
#define PIPELINE_WITH_ERROR 88


typedef void (*sighandler_t)(int);



int main(int argc, char *argv[])
{
    int i, mode = NORMAL, cmdArgc;
    size_t len = INPUT_STRING_SIZE;
    char *cpt, *inputString, *cmdArgv[INPUT_STRING_SIZE], *supplement = NULL;
    inputString = (char*)malloc(sizeof(char)*INPUT_STRING_SIZE);

    char curDir[100];

    while(1)
    {
        mode = NORMAL;
        getcwd(curDir, 100);
        printf("%s@%s->", getlogin(),curDir);
        getline( &inputString, &len, stdin);
        if(strcmp(inputString, "exit\n") == 0)
            exit(0);
        cmdArgc = parse(inputString, cmdArgv, &supplement, &mode);
        if(strcmp(*cmdArgv, "cd") == 0)
        {
            chdir(cmdArgv[1]);
        }
        else 
            execute(cmdArgv, mode, &supplement);
    }
    return 0;
}

int parse(char *inputString, char *cmdArgv[], char **supplementPtr, int *modePtr)
{
    int cmdArgc = 0, terminate = 0;
    char *srcPtr = inputString;
    //printf("parse fun%sends", inputString);
    while(*srcPtr != '\0' && terminate == 0)
    {
        *cmdArgv = srcPtr;
        cmdArgc++;
        //printf("parse fun2%sends", *cmdArgv);
        while(*srcPtr != ' ' && *srcPtr != '\t' && *srcPtr != '\0' && *srcPtr != '\n' && terminate == 0)
        {
            switch(*srcPtr)
            {
            /*  case '&':
                    *modePtr = BACKGROUND;
                    break; */
                case '>':
                    *modePtr = OUTPUT_REDIRECTION;
                    *cmdArgv = '\0';
                    srcPtr++;
                    if(*srcPtr == '>')
                    {
                        *modePtr = OUTPUT_APP;
                        srcPtr++;
                    }
                    else if(*srcPtr == '>&')
                    {
                        *modePtr = OUTPUT_REDIRECTION_WITH_ERROR;
                        srcPtr++;
                    }
                    else if(*srcPtr == '>>&')
                    {
                        *modePtr = OUTPUT_REDIRECTION_WITH_APPEND_ERROR;
                        srcPtr++;
                    }
                    while(*srcPtr == ' ' || *srcPtr == '\t')
                        srcPtr++;
                    *supplementPtr = srcPtr;
                    chop(*supplementPtr);
                    terminate = 1;
                    break;
                case '<':
                    *modePtr = INPUT_REDIRECTION;
                    *cmdArgv = '\0';
                    srcPtr++;
                    while(*srcPtr == ' ' || *srcPtr == '\t')
                        srcPtr++;
                    *supplementPtr = srcPtr;
                    chop(*supplementPtr);
                    terminate = 1;
                    break;
                case '|':
                    *modePtr = PIPELINE;
                    *cmdArgv = '\0';
                    srcPtr++;
                    if(*srcPtr == '|')
                    {
                        *modePtr = PIPELINE;
                        srcPtr++;
                    }
                    else if(*srcPtr == '|&')
                    {
                        *modePtr = PIPELINE_WITH_ERROR;
                        srcPtr++;
                    }
                    while(*srcPtr == ' ' || *srcPtr == '\t')
                        srcPtr++;
                    *supplementPtr = srcPtr;
                    //chop(*supplementPtr);
                    terminate = 1;
                    break;
            }
            srcPtr++;
        }
        while((*srcPtr == ' ' || *srcPtr == '\t' || *srcPtr == '\n') && terminate == 0)
        {
            *srcPtr = '\0';
            srcPtr++;
        }
        cmdArgv++;
    }
    /*srcPtr++;
    *srcPtr = '\0';
    destPtr--;*/
    *cmdArgv = '\0';
    return cmdArgc;
}

void chop(char *srcPtr)
{
    while(*srcPtr != ' ' && *srcPtr != '\t' && *srcPtr != '\n')
    {
        srcPtr++;
    }
    *srcPtr = '\0';
}

void execute(char **cmdArgv, int mode, char **supplementPtr)
{
    pid_t pid, pid2;
    FILE *fp;
    int mode2 = NORMAL, cmdArgc, status1, status2;
    char *cmdArgv2[INPUT_STRING_SIZE], *supplement2 = NULL;
    int myPipe[2];
    if(mode == PIPELINE)
    {
        if(pipe(myPipe))                    //create pipe
        {
            fprintf(stderr, "Pipe failed!");
            exit(-1);
        }
        parse(*supplementPtr, cmdArgv2, &supplement2, &mode2);
    }
    pid = fork();
    if( pid < 0)
    {
        printf("Error occured");
        exit(-1);
    }
    else if(pid == 0)
    {
        switch(mode)
        {
            case OUTPUT_REDIRECTION:
                fp = fopen(*supplementPtr, "w+");
                dup2(fileno(fp), 1);
                break;
            case OUTPUT_REDIRECTION_WITH_ERROR:
                fp = fopen(*supplementPtr, "w+");
                dup2(fileno(fp), 1);
                dup2(2, 1);
                break;
            case OUTPUT_REDIRECTION_WITH_APPEND_ERROR:
                fp = fopen(*supplementPtr, "a");
                dup2(fileno(fp), 1);
                dup2(2, 1);
                break;
            case OUTPUT_APP:
                fp = fopen(*supplementPtr, "a");
                dup2(fileno(fp), 1);
                break;
            case INPUT_REDIRECTION:
                fp = fopen(*supplementPtr, "r");
                dup2(fileno(fp), 0);
                break;
            case PIPELINE:
                close(myPipe[0]);       //close input of pipe
                dup2(myPipe[1], fileno(stdout));
                close(myPipe[1]);
                break;
            case PIPELINE_WITH_ERROR:
                close(myPipe[0]);
                dup2(myPipe[1], 1);
                dup2(2, 1);
                close(myPipe[1]);
                break;
        }
        execvp(*cmdArgv, cmdArgv);
    }
    else
    {
        if(mode == BACKGROUND)
            ;
        else if(mode == PIPELINE)
        {
            waitpid(pid, &status1, 0);      //wait for process 1 to finish
            pid2 = fork();
            if(pid2 < 0)
            {
                printf("error in forking");
                exit(-1);
            }
            else if(pid2 == 0)
            {
                close(myPipe[1]);       //close output to pipe
                dup2(myPipe[0], fileno(stdin));
                close(myPipe[0]);
                execvp(*cmdArgv2, cmdArgv2);
            }
            else
            {
                ;//wait(NULL);
                //waitpid(pid, &status1, 0);
                //waitpid(pid2, &status2, 0);
                close(myPipe[0]);
                close(myPipe[1]);
            }
        }
        else
            waitpid(pid, &status1, 0);
            //wait(NULL);
    }
}

對於所有I / O重定向,這些操作都是對文件描述符的直接操作。 關鍵功能是dup2()

>&2表示法將標准輸出(文件描述符,fd,1)重定向到標准錯誤(fd = 2):

dup2(2, 1);

這使得現有的打開文件描述符2和(不一定是打開的)文件描述符1引用相同的描述符。 (在此答案的第一版中,我的論點重新回到了前面;在下一次對dup2()調用中,也是如此,但最后兩個是正確的。)

&>表示法將標准錯誤重定向到與標准輸出相同的位置:

dup2(1, 2);

使用管道( |& ),您首先需要:

pipe(pair);
…fork()…
…in the writer…
dup2(pair[1], 1);
dup2(1, 2);
close(pair[0]);
close(pair[1]);

使用>>表示法,可以在追加模式下打開文件,然后使用dup2()


可以使用dup()而不是dup2()完成這些操作嗎? 如果是這樣,它將如何完成?

是的,您幾乎可以使用Peano Arithmetic進行數學運算。 很難,為什么要打擾?

區別在於dup()將描述符復制到最低可用描述符。 因此,假設標准輸入(0)和標准輸出(1)已打開,則可以模擬:

dup2(1, 2);

與:

close(2);
dup(1);

只要您只處理最多5個描述符,這是可管理的。 但是, dup2()更易於使用。 即使關閉的描述符少於目標描述符,它也會復制到指定的描述符。


修復更新的代碼

您能否看一下我上面更新的問題,並建議我可能做錯了什么?

當給出這樣的請求時,我要做的第一件事就是在我通常的編譯器警告標志集下將其編譯(從問題中逐字復制),在這種情況下,警告(通過-Werror選項轉換為錯誤)都很多和認真的。 如果您沒有編譯並看到此類警告,則說明您的生活變得比原本需要的更加艱難!

$ gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror    pipes-22585525.c -o pipes-22585525
pipes-22585525.c: In function ‘main’:
pipes-22585525.c:45:9: error: implicit declaration of function ‘strcmp’ [-Werror=implicit-function-declaration]
         if(strcmp(inputString, "exit\n") == 0)
         ^
pipes-22585525.c:34:11: error: unused variable ‘cpt’ [-Werror=unused-variable]
     char *cpt, *inputString, *cmdArgv[INPUT_STRING_SIZE], *supplement = NULL;
           ^
pipes-22585525.c:32:27: error: variable ‘cmdArgc’ set but not used [-Werror=unused-but-set-variable]
     int i, mode = NORMAL, cmdArgc;
                           ^
pipes-22585525.c:32:9: error: unused variable ‘i’ [-Werror=unused-variable]
     int i, mode = NORMAL, cmdArgc;
         ^
pipes-22585525.c:30:14: error: unused parameter ‘argc’ [-Werror=unused-parameter]
 int main(int argc, char *argv[])
              ^
pipes-22585525.c:30:26: error: unused parameter ‘argv’ [-Werror=unused-parameter]
 int main(int argc, char *argv[])
                          ^
pipes-22585525.c: In function ‘parse’:
pipes-22585525.c:84:40: error: multi-character character constant [-Werror=multichar]
                     else if(*srcPtr == '>&')
                                        ^
pipes-22585525.c:84:21: error: comparison is always false due to limited range of data type [-Werror=type-limits]
                     else if(*srcPtr == '>&')
                     ^
pipes-22585525.c:89:40: error: multi-character character constant [-Werror=multichar]
                     else if(*srcPtr == '>>&')
                                        ^
pipes-22585525.c:89:21: error: comparison is always false due to limited range of data type [-Werror=type-limits]
                     else if(*srcPtr == '>>&')
                     ^
pipes-22585525.c:119:40: error: multi-character character constant [-Werror=multichar]
                     else if(*srcPtr == '|&')
                                        ^
pipes-22585525.c:119:21: error: comparison is always false due to limited range of data type [-Werror=type-limits]
                     else if(*srcPtr == '|&')
                     ^
pipes-22585525.c: In function ‘execute’:
pipes-22585525.c:160:43: error: unused variable ‘status2’ [-Werror=unused-variable]
     int mode2 = NORMAL, cmdArgc, status1, status2;
                                           ^
pipes-22585525.c:160:25: error: unused variable ‘cmdArgc’ [-Werror=unused-variable]
     int mode2 = NORMAL, cmdArgc, status1, status2;
                         ^
cc1: all warnings being treated as errors
$

可以通過使用int main(void)來避免有關argcargv的警告,直到您實際解析傳遞給shell的參數為止。 通過包含<string.h>來修復strcmp()的警告。 其他未使用的變量警告也應同樣進行修復。 它們是世俗的問題,但應予以修復,因此代碼可以免費發出警告。

另一組警告示例如下:

pipes-22585525.c: In function ‘parse’:
pipes-22585525.c:84:40: error: multi-character character constant [-Werror=multichar]
                     else if(*srcPtr == '>&')
                                        ^
pipes-22585525.c:84:21: error: comparison is always false due to limited range of data type [-Werror=type-limits]

變量srcPtrchar * ; 一次只能容納一個字符。 多字符常量是允許的,但具有實現定義的值。 您唯一可以確定的是,單個字符永遠不會包含值'>&' ,這是第二個消息告訴您的。 第一條消息提示您應該寫:

                     else if (strncmp(srcPtr, ">&", 2) == 0)

有一個警告。 假設符號為"<<" 在Bash中,還有一個符號"<<<" 在測試"<<" "<<<"之前測試"<<<"至關重要,因為否則您將永遠看不到較長的符號,因為較短的符號將始終匹配。 重定向也是如此; 您需要注意確保沒有任何早期測試排除檢測到后面的符號之一。

解決這些問題,您可能會很好地解決問題。 如果您仍然卡住,請再次Ping。 (哦,我已經看到程序在第一次受到我嚴格的編譯選項時會產生更多警告—這不是不好的代碼。但是由於編譯器可以告訴您您在做什么錯,所以您在浪費時間。 (並且可以說是我的,但這對將來會有所幫助),方法是不讓編譯器告訴您出了什么問題。請記住,它比您更了解C!

我基本上在所有C代碼中都使用了這些編譯器選項或較小的變體。 它的大多數還可以在C ++編譯器下編譯並正確運行,但這是我選擇穿的一件襯衫,而您不必這樣做。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM