使用自定义命令在C中进行过程控制

时间:2017-12-04 19:34:56

标签: c linux process-control

好的,所以我有这个任务需要我构建一个名为PELL的并行shell工具。它为pell用户提供了执行命令,执行通过管道连接的多步骤进程以及执行独立进程的能力。

输入:     包含conc或pipe命令的文本行:

conc cmd1 args1,cmd2 args2,...         conc使PELL同时执行命令(即并行)         命令之间没有通信,这些只是发生         同时(并行)。

管道cmd1 args1,cmd2 args2         管道导致PELL为每个cmdi创建一个管道和叉子         管道是步骤1的输出和步骤2的输入。此外,         cmd1可以从文件重定向stdin。 cmd2可以有stdout         重定向到文件。

结果:     对于从stdin读取的每个命​​令:

  • 打印令牌

  • 打印命令信息

  • 取决于命令:

concCmd:

  • 打印父PID,子PID和命令

示例:

 33009 33011: ls -l /bin > lsOne.txt
 33009 33012: ls -l /usr/bin > lsTwo.txt
 33009 33013: ls -l  /etc > lsThree.txt

pipeCmd,对于每个孩子:

  • 打印步骤,父PID,子Pid和命令

示例:

 1 33043 33045: ls -l Data            
 2 33043 33046: sort -k5 -n

以下是代码的头文件:

#define TRUE 1
#define FALSE 0
#define MAX_COMMANDS 5
#define MAX_TOKEN_SZ 100
#define MAX_PATH 500
#define MAX_TOKENS 30
#define MAX_ARGS 6
// Cmd - represents a command, its list of arguments and
// subscripts into the token array for redirection of stdin and/or stdout
typedef struct Cmd
{
    int iBeginIdx; // Beginning subscript in tokenM for first arg
    // (this will be 0 when there aren't any arguments)
    int iEndIdx; // Ending subscript in tokenM for last arg. If
    // there is redirection, this subscript would be
    // before that. (this is -1 for no arguments)
    char szCmdNm[MAX_TOKEN_SZ+1]; // command name (e.g., ls)
    int iStdinRedirectIdx; // Subscript in tokenM for the stdin redirect; 0 for no redirect
    int iStdoutRedirectIdx; // Subscript in tokenM for the stdout redirect; 0 for no redirect
}Cmd;
typedef char Token [MAX_TOKEN_SZ+1];
int concCmd (Cmd cmdM[], int iCmdCnt, Token tokenM[], int iTokenCnt);
int pipeCmd (Cmd cmdM[], int iCmdCnt, Token tokenM[], int iTokenCnt);

包含的驱动程序文件:

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include "cs3423p8.h"
#define MAX_BUFFER_SZ 500
void processCommands(FILE *pfileCommand);
void prtCmdList(Cmd cmdM[], int iCmdCnt);
int getCmdList(Cmd cmdM[], Token tokenM[], int iTokenCnt);
int split(Token tokenM[], int iMaxToken, char szInput[], char cDelim);

int main(int argc, char *argv[])
{
    // We don't expect a command argument
    if (argc > 1)
        errExit("Usage: pell < inputFile");
    processCommands(stdin);
    return 0;
}

/******************** processCommands **************************************
    void processCommands(FILE *pfileCommand)
Purpose:
    Reads the Command file to process commands.  There are several types of
    records (see the program header for more information).
Parameters:
    I FILE *pfileCommand    command stream input
Notes:
    This calls:
        split
        getCmdList
        concCmd
        pipeCmd
**************************************************************************/
void processCommands(FILE *pfileCommand)
{
    // variables for command processing
    char szInputBuffer[MAX_BUFFER_SZ+1];    // input buffer for a single text line
    // variables for tokenizing
    int iTokenCnt;
    Token tokenM[MAX_TOKENS];
    // variables to understand the commands in the input text
    int iCmdCnt;
    Cmd cmdM[MAX_COMMANDS];
    // misc
    int rc;

    //  get command data until EOF
    while (fgets(szInputBuffer, MAX_BUFFER_SZ, pfileCommand) != NULL)
    {
        // if the line is just a line feed, ignore it
        if (szInputBuffer[0] == '\n')
            continue;
        // see if the command is a comment
        if (szInputBuffer[0]== '*')
        {
            printf("%s", szInputBuffer);
            continue;       // it was just a comment
        }
        printf(">>> %s", szInputBuffer);
        // split the line based on spaces
        iTokenCnt = split(tokenM, MAX_TOKENS, szInputBuffer, ' ');
        // print the tokens
        int i;
        printf("%3s %s\n", "Seq", "Token");
        for (i = 0; i < iTokenCnt; i++)
            printf("  %3d '%s'\n", i, tokenM[i]);
        if (iTokenCnt <= 0)
            errExit("Command was blank");
        // get the command list for this command
        memset(cmdM, 0, sizeof(cmdM));
        iCmdCnt = getCmdList(cmdM, tokenM, iTokenCnt);
        prtCmdList(cmdM, iCmdCnt);
        // process the particular command
        if (strcmp(tokenM[0], "conc")==0)
        {   // conc command
            rc = concCmd(cmdM, iCmdCnt, tokenM, iTokenCnt);
            if (rc != 0)
                printf("*** concCmd returned %d\n", rc);
        }
        else if (strcmp(tokenM[0], "pipe")==0)
        {   // pipe command
            rc = pipeCmd(cmdM, iCmdCnt, tokenM, iTokenCnt);
            if (rc != 0)
                printf("*** pipeCmd returned %d\n", rc);
        }
        else
            errExit("Invalid command: '%s'", tokenM[0]);
    }
    printf("\n");   // good place for a breakpoint
}

/******************** split **************************************
int split(Token tokenM[], int iMaxToken, char szInput[], char cDelim)
Purpose:
    Tokenizes the input text by splitting it on the specified
    delimiter.
Parameters:
    O Token tokenM[] array of tokens for the input test
    I int iMaxToken  the maximum number of characters in a token (not
                     including zero byte
    I char szInput[] input text to be tokenized
    I char cDelim    delimiter character (e.g., ' ')
Returns:
    Count of number of entries in tokenM.
Notes:
    - Linefeed and '\0' are also used as delimiters for the last token
    - If we encounter two or more adjacnet delimiters, we ignore them.
    - If a token is larger than the specified max token size, we
      truncate the string.
**************************************************************************/
int split(Token tokenM[], int iMaxToken, char szInput[], char cDelim)
{
    int i;                  // used to traverse the input text string
    int iTokenBeg = 0;      // subscript where the token begins
    int iTokenEnd = -1;     // subscript to the delimiter following the token
    int iTokenIdx = 0;      // where to place next token in tokenM
    int iTokenSize;         // size (in bytes) of the token without zero byte
    // We will actually include touching the '\0' or '\n' since that will
    // mark the end of the last token.
    int iLen = strlen(szInput);
    for (i = 0; i <= iLen; i +=1)
    {
        // Is it end of line, line feed or the delim?
        if (szInput[i]== '\0' || szInput[i] == '\n' || szInput[i] == cDelim)
        {
            if (iTokenBeg == i)
            {   // only a delimiter, nothing in token so ignore it
                iTokenBeg = i+1;
                continue;
            }
            // see if the token is too long
            if ( (i-iTokenBeg) > iMaxToken)
                iTokenSize = iMaxToken; // truncate it
            else
                iTokenSize = i-iTokenBeg;
            memcpy(&tokenM[iTokenIdx][0], &szInput[iTokenBeg], iTokenSize);
            tokenM[iTokenIdx][iTokenSize] = '\0'; // terminate it
            iTokenIdx++;
            iTokenBeg = i+1;
        }
    }
    return iTokenIdx;
}
/******************** gettCmdList **************************************
int getCmdList(Cmd cmdM[], Token tokenM[], int iTokenCnt)
Purpose:
    Parse through the token array to determine the commands.  It
    saves the beginning and ending subscripts for each command's arguments.
    It also determines whether the command has a redirected stdin
    and/or stdout.
Parameters:
    O Cmd cmdM[]     array of commands
    I Token tokenM[] array of tokens for the input test
    I int iTokenCnt  number of entries in tokenM
Returns:
    Count of number of entries in cmdM.
Notes:
    - commands are separated by commas
**************************************************************************/
int getCmdList(Cmd cmdM[], Token tokenM[], int iTokenCnt)
{
    int i;              // subscript to current token
    char cChar;         // current character in input text
    int iCmdCnt = 0;    // count of number of entries in cmdM
    // Iterate through the array of tokens.  We actually
    // go to one item beyond the end so that we can process
    // the last token normally.  (We pretend there is an
    // ending token after the last token.)
    for (i = 1; i <= iTokenCnt; i += 1)
    {
        Cmd *pCmd = &(cmdM[iCmdCnt]);
        if (i == iTokenCnt)
            cChar = ',';  // pretend an ending delim
        else
            cChar = tokenM[i][0];
        switch(cChar)
        {
            case ',':  // delimiter between commands
                if (pCmd->iBeginIdx == 0)
                    errExit("no command, cmd arg: %d\n", i);
                // If we haven't yet marked the end of the command's
                // arguments, assume it is right before the comma.
                // Note that redirection also set the iEndIdx.
                if (pCmd->iEndIdx == 0)
                     pCmd->iEndIdx = i-1;
                // Check for no command arguments
                if (pCmd->iBeginIdx > pCmd->iEndIdx)
                {   // no args
                    pCmd->iBeginIdx = 0;
                    pCmd->iEndIdx = -1;
                }
                iCmdCnt += 1;
                break;
            case '<':  // stdin redirection
                if (i+1 >= iTokenCnt)  // need another arg
                    errExit("redirect requires additional arg, cmd arg: %d\n", i);
                pCmd->iStdinRedirectIdx = i+1;
                // If we haven't yet marked the end of the command's
                // arguments, assume it is right before the <.
                if (pCmd->iEndIdx == 0)
                     pCmd->iEndIdx = i-1;
                break;
            case '>':
                if (i+1 >= iTokenCnt)  // need another arg
                    errExit("redirect requires additional arg, cmd arg: %d\n", i);
                pCmd->iStdoutRedirectIdx = i+1;
                // If we haven't yet marked the end of the command's
                // arguments, assume it is right before the >.
                if (pCmd->iEndIdx == 0)
                     pCmd->iEndIdx = i-1;
                break;
            default:
                // check if at the beginning of the command
                if (pCmd->iBeginIdx == 0)
                {   // not comma, <, > if iBeginIdx is 0, we need to record
                    // where the arguments might begin
                    strcpy(pCmd->szCmdNm, tokenM[i]);
                    pCmd->iBeginIdx = i+1;
                }
        }
    }
    return iCmdCnt;
}
/******************** prtCmdList **************************************
void prtCmdList(Cmd cmdM[], int iCmdCnt)
Purpose:
    Prints information for each command in the list of commands.
Parameters:
    I Cmd cmdM[]    array of commands
    I int iCmdCnt   count of number of entries in cmdM.
**************************************************************************/
void prtCmdList(Cmd cmdM[], int iCmdCnt)
{
    int i;
    printf("%-20s  %5s %-5s %-5s %-6s\n"
        , "Command", "Begin", "End", "stdin", "stdout");
    for (i = 0; i < iCmdCnt; i +=1)
    {
        printf("%-20s %5d %5d %5d %6d\n"
            , cmdM[i].szCmdNm, cmdM[i].iBeginIdx, cmdM[i].iEndIdx
            , cmdM[i].iStdinRedirectIdx, cmdM[i].iStdoutRedirectIdx);
    }
}

可以使用一些帮助构建程序的伪代码。我知道主要功能是什么样的,但需要帮助设置concCMD命令和pipeCMD命令的代码。我对c不太熟练。任何帮助将不胜感激。

这是我到目前为止编写的代码,我正在寻找使其工作的方法,以及如何编写管道命令。我收到了一个模糊的输出重定向错误。

//
//  cs3423p8.c
//
//
//  Created by Cesar Benavides on 12/4/17.
//

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include "cs3423p8.h"
int concCmd (Cmd cmdM[], int iCmdCnt, Token tokenM[], int iTokenCnt);
//int pipeCmd (Cmd cmdM[], int iCmdCnt, Token tokenM[], int iTokenCnt);

//prints parents processID Child's ProcessID, the Command and Command arguments
//fork each of the children, Max commands is 5. If any of the commands is redirect or output
//do redirection after forking but before execing
//redirect stdout for each child, and execvp to the particular command for child
int concCmd (Cmd cmdM[], int iCmdCnt, Token tokenM[], int iTokenCnt)
{
    int j;
    int i;
    int count = 0;
    long lForkPid;
    long lWaitPid;
    int iExitStatus = 0;
    char *execArgv[25];
    int fdin, fdout;
    //make children
    lForkPid = fork();
    //children made
    for(i = 0; i < iCmdCnt; i++)
    {
        //count =0;
        switch(lForkPid)
        {
            case -1:
                errExit("fork failed: %s", strerror(errno));
                break;
            case 0://child

                execArgv[0] = cmdM[i].szCmdNm;
                //count = 0;
                for(j = cmdM[i].iBeginIdx; j <= cmdM[i].iEndIdx; j++)
                {

                    execArgv[count + 1] = tokenM[j];
                    count++;
                }
                execArgv[count] = NULL;

                if (cmdM[i].iStdinRedirectIdx !=0)
                {
                    printf("stdin redirect \n \n ");

                    fdin = open(tokenM[cmdM[i].iStdinRedirectIdx], O_RDONLY);
                    dup2(fdin, STDIN_FILENO);
                    close(fdin);
                    fprintf(stderr,"sTDIN REDIRECT Child Process: PID=%ld, PPID=%ld\n" , (long) getpid(), (long) getppid());
                    // execvp(cmdM[i].szCmdNm, execArgv);
                    //errExit("Child process failed to exec: %s", strerror(errno));
                }

                if (cmdM[i].iStdoutRedirectIdx !=0)
                {
                    printf("stdout redirect \n\n");

                    fdout = open(tokenM[cmdM[i].iStdoutRedirectIdx], O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
                    dup2(fdout, STDOUT_FILENO);
                    close(fdout);
                    fprintf(stderr, "sTDOUT REDIRECT Child Process: PID=%ld, PPID=%ld\n", (long) getpid(), (long) getppid());

                }
                execvp(cmdM[i].szCmdNm, execArgv);
                //errExit("Child process failed to exec: %s", strerror(errno));


                // exit(0);
            default://parent
                lWaitPid = wait(&iExitStatus);
                // if(lWaitPid == -1)
                // errExit("wait error: %s", strerror(errno));
        }
    }
    return 0;
}

0 个答案:

没有答案