好的,所以我有这个任务需要我构建一个名为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:
示例:
33009 33011: ls -l /bin > lsOne.txt
33009 33012: ls -l /usr/bin > lsTwo.txt
33009 33013: ls -l /etc > lsThree.txt
pipeCmd,对于每个孩子:
示例:
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;
}