我正在用C编写一个shell,以便在PuTTy上运行。外壳应该能够接受任何通用命令";ls"pwd";,以及随后的潜在论点,例如";ls-l";,我遇到了一个分割错误。我知道,当试图访问程序无法访问的内存时,会出现分段错误。但是,我不确定在以下代码中发生这种情况的位置。分段错误发生在输入不同数量参数的命令时。例如,该程序能够获取";ls-l";并且将正常工作。然而,如果我们从进入";ls〃;然后输入";ls-l";这将引发分段错误。简单地说,当输入具有大量参数的命令时,会出现分段错误。
我已经对代码进行了多次深入分析,以检查我的逻辑。不幸的是,我没有看到我的错误,而且我对C作为一种语言来说还比较新。我相信这个问题发生在";tokenizer;函数,但我可能完全错了。我是否可能需要在某个地方释放内存?
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
//-----Intro function-----//
//This function grabs user
//input and returns it.
//Also prints the shell name
char* intro(){
printf("TamuccShell>> ");
char* userInput = NULL;
static char dataStr[200];
int i = 0;
if(fgets(dataStr, 200, stdin)==NULL) // The statement reads a line
{
printf("Errorn");
return userInput;
}
userInput = dataStr;
return userInput;
}
//-----Tokenizer function-----//
//Variables:Char**, Char*,
//toks is to hold arguments
//data holds user input data
//which are the commands
void tokenizer(char** toks, char* data){
int i = 0;
data[strlen(data) - 1] = ' '; // It replaces newline ch with null
char* token = strtok(data, " "); // Returns first token with delimiter space
// strcpy(command,token);// stores first token in this pointer to seperate our command from our arguments
// Keep extracting tokens
while (token != NULL) {
strcpy(toks[i],token);
token = strtok(NULL, " ");
i+=1;
}
toks[i] = (char *)0;
}
int main(){
char *tokens;//User given tokens fed into tokenizer functions
//--------VARIABLES FOR EXECV FUNCTION-----------------//
int pid;//process ID for child process
int status;//status for child process, this will be an indicator for the parent process to continue
char* folder;//Destination for command to execute
char* argv[10];// reservation for commands argv[0] will be the actual command argv[1] will be arguments ,argv[2] null for termination
folder = (char*)malloc(200); // Allocate 20 character positions
int i;//iterator used with for loop below.
for(i =0; i< 10;i++){//allocating memory for each given argument
argv[i] = (char*)malloc(50);
}
//-------VARIABLE for Tokenizer-------//
char* userInput;// storage space for user input from intro function
//----MAIN LOOP----//
int active = 1;//used for while loop below this line
while(active){
//---FUNCTION CALLS---//
userInput = intro();// Calling introduction program that grabs user input and displays the shell prompt
tokenizer(argv,userInput); // fills command and argument with tokenized values
if(strcmp(argv[0],"exit") == 0) // check for exit call
{
active = 0;
}
// else if(strcmp(argv[0],"hist") == 0){
// commandPrinter(prevComs);
// }
else{
folder = strcpy(folder, "/bin/"); //Destination setup for bin, so we may access commands.
folder = strcat(folder, argv[0]); // Destination specification of command IE "ls"
pid = fork();//creates child process
if(pid == 0){// checks if this process is the child process
execv(folder, argv); // call to the function in child process; execute command and exit from the child process
printf("nCommand Failedn"); //only runs if execv call has failed.
exit(0);
}
else{
wait(&status);//makes parent process wait for the child process to
}
}
}
return 0;
}
如果我们这样做,程序就会工作:
ls -l
但是,如果我们这样做,它就会失败:
ls
ls -l
在main
中,您正在执行:
// allocating memory for each given argument
for (i = 0; i < 10; i++) {
argv[i] = (char *) malloc(50);
}
然后,稍后,您将在循环中调用tokenize
。
对于ls
,tokenize将把toks[1]
设置为NULL
(tokenize
中的最后一条语句(。
这是有效的,但却是错误的。
当我们用ls -l
再次调用它时,ls
转到toks[0]
,而-l
转到toks[1]
。
但是,在上一次调用中,toks[1]
被设置为NULL
,因此您正在[有效地]执行:
strcpy(NULL,token);
这会导致segfault。
要修复:
- 您实际上不需要在
main
中执行malloc
调用。拆除for
回路 - 在
tokenize
中,将strcpy(toks[i],token);
更改为toks[i] = token;
tokenize
应该对照令牌数组的最大限制来检查令牌的数量
以下是重构的代码:
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
//-----Intro function-----//
//This function grabs user
//input and returns it.
//Also prints the shell name
#if 1
#define ARGVMAX 10
#endif
char *
intro()
{
printf("TamuccShell>> ");
char *userInput = NULL;
static char dataStr[200];
int i = 0;
if (fgets(dataStr, 200, stdin) == NULL) // The statement reads a line
{
printf("Errorn");
return userInput;
}
userInput = dataStr;
return userInput;
}
//-----Tokenizer function-----//
//Variables:Char**, Char*,
//toks is to hold arguments
//data holds user input data
//which are the commands
int
tokenizer(char **toks, char *data)
{
int i = 0;
data[strlen(data) - 1] = ' '; // It replaces newline ch with null
char *token = strtok(data, " "); // Returns first token with delimiter space
// strcpy(command,token);// stores first token in this pointer to seperate our command from our arguments
// Keep extracting tokens
#if 1
int err = 0;
#endif
while (token != NULL) {
// NOTE/FIX: check against limit of toks
#if 1
if (i >= (ARGVMAX - 1)) {
printf("too many tokensn");
err = 1;
break;
}
#endif
// NOTE/BUG: toks is a _pointer_ array
#if 0
strcpy(toks[i], token);
#else
toks[i] = token;
#endif
token = strtok(NULL, " ");
i += 1;
}
toks[i] = (char *) 0;
#if 1
return err;
#endif
}
int
main(void)
{
char *tokens; // User given tokens fed into tokenizer functions
// --------VARIABLES FOR EXECV FUNCTION-----------------//
int pid; // process ID for child process
int status; // status for child process, this will be an indicator for the parent process to continue
char *folder; // Destination for command to execute
// reservation for commands argv[0] will be the actual command argv[1] will
// be arguments ,argv[2] null for termination
// NOTE/BUG: don't use hardwired "magic" numbers
#if 0
char *argv[10];
#else
char *argv[ARGVMAX];
#endif
folder = (char *) malloc(200); // Allocate 20 character positions
int i; // iterator used with for loop below.
// NOTE/BUG: these can be blown away by tokenize
#if 0
for (i = 0; i < 10; i++) { // allocating memory for each given argument
argv[i] = (char *) malloc(50);
}
#endif
// -------VARIABLE for Tokenizer-------//
char *userInput; // storage space for user input from intro function
// ----MAIN LOOP----//
int active = 1; // used for while loop below this line
while (active) {
// ---FUNCTION CALLS---//
userInput = intro(); // Calling introduction program that grabs user input and displays the shell prompt
// fills command and argument with tokenized values
#if 0
tokenizer(argv, userInput);
#else
// skip command if parsing error
if (tokenizer(argv, userInput))
continue;
#endif
if (strcmp(argv[0], "exit") == 0) // check for exit call
{
active = 0;
}
// else if(strcmp(argv[0],"hist") == 0){
// commandPrinter(prevComs);
// }
else {
folder = strcpy(folder, "/bin/"); // Destination setup for bin, so we may access commands.
folder = strcat(folder, argv[0]); // Destination specification of command IE "ls"
pid = fork(); // creates child process
if (pid == 0) { // checks if this process is the child process
execv(folder, argv); // call to the function in child process; execute command and exit from the child process
printf("nCommand Failedn"); // only runs if execv call has failed.
exit(0);
}
else {
wait(&status); // makes parent process wait for the child process to
}
}
}
return 0;
}
在上面的代码中,我使用了cpp
条件词来表示旧代码与新代码:
#if 0
// old code
#else
// new code
#endif
#if 1
// new code
#endif
注意:这可以通过unifdef -k
运行文件来清除