我正在用 C 撰寫我自己的 UNIX shell,并且我正在嘗試添加對在引號(即echo "This is a test")內傳遞多字引數的支持。在parseCommandWords您可以在下面看到的當前函式 ( ) 中,我成功地分離了通過輸入引數傳遞給函式的單詞,并通過strsep(). 然而,一旦printf()呼叫運行,并列印正確的值word和input,分段故障被拋出。它永遠不會到達 printf 下面的任何 if 陳述句,在它下面添加任何內容,根本不會運行。我看不出是什么導致了這個問題。使用input = ls例如(簡單命令)對其進行測驗,它word = ls | input = (null)會按您的預期列印。
該parsedWords引數最初是一個 NULL 字串陣列,并且在傳遞給函式之前也會對引數進行驗證。
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
#include "cs345sh.h"
/**
* Counts how many times the given char is present
* in the given string.
* @param input The string in which to look for
* @param lookupChar The char whose occurences to count
* @return The number of occurences of the given char
**/
int countCharOccurences(char *input, char lookupChar)
{
char *str = input;
int count = 0;
int i;
for (i = 0; str[i]; i )
{
if (str[i] == lookupChar)
count ;
}
return count;
}
/**
* Parses the available command words in the given command and places
* them in the given array.
* @param input The initial string to split that contains the command.
* @param parsedWords The final parsed commands.
**/
void parseCommandWords(char *input, char **parsedWords)
{
int i;
for (i = 0; i < MAX_NUM_OF_COMMAND_WORDS; i )
{
char *word = (char *)malloc(100 * sizeof(char)); // max 100 chars
if (!word)
{
perror("Failed to allocate memory!\n");
exit(EXIT_FAILURE);
}
if (input[0] == '\"')
{
char *inptPtr = input;
int charCnt = 0;
do
{
inptPtr ;
charCnt ;
} while (inptPtr[0] != '\"');
charCnt ; // include final "
strncpy(word, input, charCnt);
// check if there are chars left to parse or not
if ( inptPtr != NULL)
{
input = inptPtr; // start after the ending "
}
else
{
input = "";
}
printf("word after loop = %s\ninput = %s\n", word, input);
strcpy(parsedWords[i],word);
free(word);
continue;
}
word = strsep(&input, " ");
printf("word = %s | input = %s\n",word,input);
if (word == NULL)
{
free(word);
break; // there was nothing to split
}
if (strlen(word) == 0)
{
free(word);
i--; // read an empty command, re-iterate
continue;
}
printf("before cpy");
strcpy(parsedWords[i],word);
printf("word = %s | parsedwords[i] = %s\n",word,parsedWords[i]);
free(word);
if(input == NULL) break;
}
printf("exiting parser");
}
/**
* Parses the available commands in the given string and places
* them in the given array.
* @param input The initial string to split that contains the commands.
* @param parsedWords The final parsed commands.
**/
void parseMultipleCommands(char *input, char **parsedCommands)
{
int numOfSemicolons = countCharOccurences(input, ';');
int i;
for (i = 0; i < numOfSemicolons 1; i )
{
char *word = strsep(&input, ";");
if (word == NULL)
break;
if (strlen(word) == 0)
{
i--;
continue;
}
parsedCommands[i] = word;
}
}
char *removeLeadingWhitespace(char *input)
{
while (*input == ' ')
{
input ;
}
return input;
}
/**
* Splits the given string at each pipe char occurance and places
* each command in the given array.
* @param input The initial string to split
* @param inptParsed The final parsed commands split at the pipe chars
* @return Returns 0 if no pipe chars were found or 1 if the operatio was successful.
**/
int splitAtPipe(char *input, char **inptParsed)
{
int numOfPipes = countCharOccurences(input, '|');
int i;
// create a copy of the given input in order to preserver the original
char *inpt = (char *)malloc(MAX_INPUT_SIZE * sizeof(char));
strcpy(inpt, input);
for (i = 0; i < numOfPipes 1; i )
{
char *word = strsep(&inpt, "|");
if (word == NULL)
break;
if (strlen(word) == 0)
{
i--;
continue;
}
word = removeLeadingWhitespace(word);
inptParsed[i] = word;
}
return 1;
}
/**
* Handles the execution of custom commands (i.e. cd, exit).
* @param cmdInfo An array containing the command to execute in the first position, and the arguments
* to execute with in the rest of the array.
* @return Returns 0 if the command couldn't be executed, or 1 otherwise.
**/
int handleCustomCommands(char **cmdInfo)
{
int numOfCustomCommands = 2;
char *customCommands[numOfCustomCommands];
customCommands[0] = "cd";
customCommands[1] = "exit";
int i;
for (i = 0; i < numOfCustomCommands; i )
{
// find the command to execute
if (strcmp(cmdInfo[0], customCommands[i]) == 0)
break;
}
switch (i)
{
case 0:
if (chdir(cmdInfo[1]) == -1)
return 0;
else
return 1;
case 1:
exit(0);
return 1;
default:
break;
}
return 0;
}
/**
* Displays the shell prompt in the following format:
* <user>@cs345sh/<dir>$
**/
void displayPrompt()
{
char *user = getlogin();
char cwd[512]; // support up to 512 chars long dir paths
if (getcwd(cwd, sizeof(cwd)) == NULL)
{
perror("error retrieving current working directory.");
exit(-1);
}
else if (user == NULL)
{
perror("error getting currently logged in user.");
exit(-1);
}
else
{
printf("%s@cs345%s$ ", user, cwd);
}
}
void execSystemCommand(char **args)
{
// create an identical child process
pid_t pid = fork();
if (pid == -1)
{
perror("\nFailed to fork child..");
exit(EXIT_FAILURE);
}
else if (pid == 0)
{
if (execvp(args[0], args) < 0)
{
perror("Could not execute given command..");
}
exit(EXIT_FAILURE);
}
else
{
// wait for the child process to finish
wait(NULL);
return;
}
}
void execPipedCommands(char *input, char **commands)
{
int numOfPipes = countCharOccurences(input, '|');
int fds[2 * numOfPipes]; // two file descriptors per pipe needed for interprocess communication
int i;
pid_t cpid;
// initialize all pipes and store their respective fds in the appropriate place in the array
for (i = 0; i < numOfPipes; i )
{
if (pipe(fds 2 * i) == -1)
{
perror("Failed to create file descriptors for pipe commands!\n");
exit(EXIT_FAILURE);
}
}
for (i = 0; i < numOfPipes 1; i )
{
if (commands[i] == NULL)
break;
char *args[MAX_NUM_OF_COMMAND_WORDS] = {
NULL,
};
parseCommandWords(commands[i], args);
cpid = fork(); // start a child process
if (cpid == -1)
{
perror("Failed to fork..\n");
exit(EXIT_FAILURE);
}
if (cpid == 0)
{ // child process is executing
if (i != 0)
{ // if this is not the first command in the chain
// duplicate the file descriptor to read from the previous command's output
if (dup2(fds[(i - 1) * 2], STDIN_FILENO) < 0)
{
perror("Failed to read input from previous command..\n");
exit(EXIT_FAILURE);
}
}
// if this is not the last command in the chain
if (i != numOfPipes && commands[i 1] != NULL)
{
// duplicate write file descriptor in order to output to the next command
if (dup2(fds[(i * 2 1)], STDOUT_FILENO) < 0)
{
perror("Failed to write output for the next command..\n");
exit(EXIT_FAILURE);
}
}
// close the pipes
int j;
for (j = 0; j < numOfPipes 1; j )
{ // close all copies of the file descriptors
close(fds[j]);
}
// execute command
if (execvp(args[0], args) < 0)
{
perror("Failed to execute given piped command");
return;
}
}
}
// parent closes all original file descriptors
for (i = 0; i < numOfPipes 1; i )
{
close(fds[i]);
}
// parent waits for all child processes to finish
for (i = 0; i < numOfPipes 1; i )
wait(0);
}
void parseInput(char *input)
{
if (strchr(input, '|') != NULL)
{ // possibly piped command(s)
char *commands[MAX_NUM_OF_COMMANDS] = {
NULL,
};
splitAtPipe(input, commands);
execPipedCommands(input, commands);
}
else if (strchr(input, ';') != NULL)
{ // possibly multiple command(s)
char *commands[MAX_NUM_OF_COMMANDS] = {
NULL,
};
parseMultipleCommands(input, commands);
int i;
for (i = 0; i < MAX_NUM_OF_COMMANDS; i )
{
if (commands[i] == NULL)
break;
// single command
char *args[MAX_NUM_OF_COMMAND_WORDS] = {
NULL,
};
parseCommandWords(commands[i], args);
if (handleCustomCommands(args) == 0)
{
execSystemCommand(args);
}
}
}
else
{
// single command
char *args[MAX_NUM_OF_COMMAND_WORDS] = {
NULL,
};
parseCommandWords(input, args);
printf("parsed! arg[0] = %s\n",args[0]);
if (handleCustomCommands(args) == 0)
{
execSystemCommand(args);
}
}
}
int main(int argc)
{
char *inputBuf;
size_t inputLen;
inputBuf = (char *)malloc(MAX_INPUT_SIZE * sizeof(char));
if (!inputBuf)
{
perror("Error allocating input buffer!");
exit(EXIT_FAILURE);
}
while (1)
{
displayPrompt();
if (getline(&inputBuf, &inputLen, stdin) == -1)
{
perror("Error reading input.");
exit(EXIT_FAILURE);
}
if (*inputBuf == '\n')
continue;
else
{
// remove the \n at the end of the read line ()
inputBuf[strcspn(inputBuf, "\n")] = '\0';
parseInput(inputBuf);
}
}
return 0;
}
頭檔案:
#define MAX_NUM_OF_COMMAND_WORDS 50 // usual num of maximum command arguments is 9 (but is system dependent)
#define MAX_NUM_OF_COMMANDS 20 // what could it be hmm
#define MAX_INPUT_SIZE 1000 // num of max chars to read
/**
* Counts how many times the given char is present
* in the given string.
* @param input The string in which to look for
* @param lookupChar The char whose occurences to count
* @return The number of occurences of the given char
**/
int countCharOccurences(char* input, char lookupChar);
/**
* Parses the available command words in the given command and places
* them in the given array.
* @param input The initial string to split that contains the command.
* @param parsedWords The final parsed commands.
**/
void parseCommandWords(char *input, char** parsedWords);
/**
* Parses the available commands in the given string and places
* them in the given array.
* @param input The initial string to split that contains the commands.
* @param parsedWords The final parsed commands.
**/
void parseMultipleCommands(char *input, char **parsedCommands);
/**
* Splits the given string at each pipe char and places
* each command in the given array.
* @param input The initial string to split
* @param inptParsed The final parsed commands split at the pipe chars
* @return Returns 0 if no pipe chars were found or 1 if the operation was successful.
**/
int splitAtPipe(char *input, char** inptParsed);
/**
* Handles the execution of custom commands (i.e. cd, exit).
* @param cmdInfo An array containing the command to execute in the first position, and the arguments
* to execute with in the rest of the array.
* @return Returns 0 if the command couldn't be executed, or 1 otherwise.
**/
int handleCustomCommands(char **command);
/**
* Displays the shell prompt in the following format:
* <user>@cs345sh/<dir>$
**/
void displayPrompt();
void execPipedCommands(char*, char**);
/**
* Removes any trailing whitespace from the given string
* and returns a pointer at the beginning of the new string.
* @param input The string to remove whitespace from
*/
char* removeLeadingWhitespace(char *input) ;
uj5u.com熱心網友回復:
與 相比strcpy,該函式strncpy不一定會向目標陣列添加終止空字符。
由于charCnt似乎小于或等于strlen(input),線
strncpy(word, input, charCnt);
不會將終止空字符寫入word. 因此,該行
printf("word after loop = %s\ninput = %s\n", word, input);
將呼叫未定義的行為,因為%s格式說明符需要一個以空字符結尾的字串。
uj5u.com熱心網友回復:
缺少初始化
inputLengetline()時間不定。初始化它。
*lineptr 可以包含一個指向 malloc(3) 分配的緩沖區 *n 位元組大小的指標。
char *inputBuf;
// size_t inputLen;
size_t inputLen = MAX_INPUT_SIZE;
inputBuf = (char*) malloc(MAX_INPUT_SIZE * sizeof(char));
...
if (getline(&inputBuf, &inputLen, stdin) == -1) {
// Even better
char *inputBuf = NULL;
size_t inputLen = 0;
// Not needed
// inputBuf = (char*) malloc(MAX_INPUT_SIZE * sizeof(char));
...
if (getline(&inputBuf, &inputLen, stdin) == -1) {
也許其他問題
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/348671.html
