处理自定义参数 shell
Handling arguments in a custom shell
我目前正在为 class 开发一个用 C 语言实现的 shell,我希望随着时间的推移在其基础上构建 运行 并在执行我的论点时遇到问题。我的程序使用 getchar()
将条目解析为参数数组,然后使用 execvp()
执行参数。我遇到的问题是在重复输入参数时,任何后续较短的参数都会与内存中某处留下的字符连接起来。下面的例子。我需要使用 getchar
,这排除了获取参数的替代方法。
//Global Variables
char argument[64];
char **argv;
int main(int argc, char** argv) {
mainloop(); //Loop that calls the commands
return (EXIT_SUCCESS);
}
void prompt(){ //Modular prompt
printf ("?:");
}
void mainloop(){ //Loop that calls the functions
while(1){
prompt();
argv = (char**)malloc(sizeof(char*)*64); //allocate memory for argv
getcommand(argument, argv);
if((strcmp(argv[0],"exit" )) == 0){ //check for exit
return 0;
}
executecommand();
printcommand();
//clearcommand();
//printcommand();
}
}
void getcommand(char* argument, char** argv){ //Parser for the command
int i=0,j=0;
char c;
char* token;
while((c = getchar()) != '\n' ){ //gets char and checks for end of line
argument[i] = c;
i++;
}
token = strtok(argument, " ,."); //tokenize the command
while (token != NULL){
argv[j] = token; //pass command to array of arguments
token = strtok(NULL, " ,.");
j++;
}
//argv[j] = "[=11=]";
}
void executecommand(){ //Function to call fork and execute with errors
pid_t childpid = fork();
int returnStatus;
if(childpid == -1){ //Fail to Fork
printf("failed to fork");
exit(1);
}
else if(childpid == 0){ //Child process
if (execvp(*argv, argv) < 0){
printf("error executing\n");
exit(1);
}
else{ //Execute successful
printf("executed");
}
}
int c=(int)waitpid(childpid, &returnStatus, 0);
if (returnStatus == 0) // Verify child process terminated without error.
{
printf("The child process terminated normally. \n");
}
if (returnStatus == 1)
{
printf("The child process terminated with an error!.\n");
}
//realloc(argv,64);
}
void printcommand(){ //Test function to print arguments
int i = 0;
while(argv[i] != NULL){
printf("Argv%d: %s \n",i, argv[i] );
i++;
}
}
/*void clearcommand(){ //Function to clear up the memory, does not work
int i=0;
argv[0] = " [=11=]";
argv[1] = " [=11=]";
}*/
示例输出:
?: ls -l
//functions as intended
?:echo
//argument returns as echol
任何比前一个条目短的条目都是这种情况。我不明白为什么 exec 在 '[=16=]'
之后继续读取参数,我确信我在这里犯了一个内存错误。非常感谢您的帮助,我已经坚持了几天了。
您需要使用包含空指针的元素来指示 argv
数组的结尾。注释掉的行:
argv[j] = "[=10=]";
应该是:
argv[j] = NULL;
您还需要在 argument
字符串的末尾放置一个空终止符。当您执行 echo
时,您会得到 l
,因为 argument
仍然包含之前的命令行。所以第一行设置 argument
为:
ls -l
然后你用echo
覆盖前4个字符,所以它变成:
echol
所以完整的函数是:
void getcommand(char* argument, char** argv){ //Parser for the command
int i=0,j=0;
char c;
char* token;
while((c = getchar()) != '\n' ){ //gets char and checks for end of line
argument[i] = c;
i++;
}
argument[i] = '[=14=]';
token = strtok(argument, " ,."); //tokenize the command
while (token != NULL){
argv[j] = token; //pass command to array of arguments
token = strtok(NULL, " ,.");
j++;
}
argv[j] = NULL;
}
您也可以使用 fgets()
读取一行输入,而不是自己调用 getchar()
。
您还应该检查输入是否大于 argument
的大小。
在读取输入行的循环之后,您需要使用 NUL 字符终止该行。
while((c = getchar()) != '\n' ){ //gets char and checks for end of line
argument[i] = c;
i++;
}
argument[i] = '[=10=]'; // <<---- terminate the input line
您还需要按照@Barmar 所说的进行操作,但这与您在问题中描述的问题不同。
我目前正在为 class 开发一个用 C 语言实现的 shell,我希望随着时间的推移在其基础上构建 运行 并在执行我的论点时遇到问题。我的程序使用 getchar()
将条目解析为参数数组,然后使用 execvp()
执行参数。我遇到的问题是在重复输入参数时,任何后续较短的参数都会与内存中某处留下的字符连接起来。下面的例子。我需要使用 getchar
,这排除了获取参数的替代方法。
//Global Variables
char argument[64];
char **argv;
int main(int argc, char** argv) {
mainloop(); //Loop that calls the commands
return (EXIT_SUCCESS);
}
void prompt(){ //Modular prompt
printf ("?:");
}
void mainloop(){ //Loop that calls the functions
while(1){
prompt();
argv = (char**)malloc(sizeof(char*)*64); //allocate memory for argv
getcommand(argument, argv);
if((strcmp(argv[0],"exit" )) == 0){ //check for exit
return 0;
}
executecommand();
printcommand();
//clearcommand();
//printcommand();
}
}
void getcommand(char* argument, char** argv){ //Parser for the command
int i=0,j=0;
char c;
char* token;
while((c = getchar()) != '\n' ){ //gets char and checks for end of line
argument[i] = c;
i++;
}
token = strtok(argument, " ,."); //tokenize the command
while (token != NULL){
argv[j] = token; //pass command to array of arguments
token = strtok(NULL, " ,.");
j++;
}
//argv[j] = "[=11=]";
}
void executecommand(){ //Function to call fork and execute with errors
pid_t childpid = fork();
int returnStatus;
if(childpid == -1){ //Fail to Fork
printf("failed to fork");
exit(1);
}
else if(childpid == 0){ //Child process
if (execvp(*argv, argv) < 0){
printf("error executing\n");
exit(1);
}
else{ //Execute successful
printf("executed");
}
}
int c=(int)waitpid(childpid, &returnStatus, 0);
if (returnStatus == 0) // Verify child process terminated without error.
{
printf("The child process terminated normally. \n");
}
if (returnStatus == 1)
{
printf("The child process terminated with an error!.\n");
}
//realloc(argv,64);
}
void printcommand(){ //Test function to print arguments
int i = 0;
while(argv[i] != NULL){
printf("Argv%d: %s \n",i, argv[i] );
i++;
}
}
/*void clearcommand(){ //Function to clear up the memory, does not work
int i=0;
argv[0] = " [=11=]";
argv[1] = " [=11=]";
}*/
示例输出:
?: ls -l
//functions as intended
?:echo
//argument returns as echol
任何比前一个条目短的条目都是这种情况。我不明白为什么 exec 在 '[=16=]'
之后继续读取参数,我确信我在这里犯了一个内存错误。非常感谢您的帮助,我已经坚持了几天了。
您需要使用包含空指针的元素来指示 argv
数组的结尾。注释掉的行:
argv[j] = "[=10=]";
应该是:
argv[j] = NULL;
您还需要在 argument
字符串的末尾放置一个空终止符。当您执行 echo
时,您会得到 l
,因为 argument
仍然包含之前的命令行。所以第一行设置 argument
为:
ls -l
然后你用echo
覆盖前4个字符,所以它变成:
echol
所以完整的函数是:
void getcommand(char* argument, char** argv){ //Parser for the command
int i=0,j=0;
char c;
char* token;
while((c = getchar()) != '\n' ){ //gets char and checks for end of line
argument[i] = c;
i++;
}
argument[i] = '[=14=]';
token = strtok(argument, " ,."); //tokenize the command
while (token != NULL){
argv[j] = token; //pass command to array of arguments
token = strtok(NULL, " ,.");
j++;
}
argv[j] = NULL;
}
您也可以使用 fgets()
读取一行输入,而不是自己调用 getchar()
。
您还应该检查输入是否大于 argument
的大小。
在读取输入行的循环之后,您需要使用 NUL 字符终止该行。
while((c = getchar()) != '\n' ){ //gets char and checks for end of line
argument[i] = c;
i++;
}
argument[i] = '[=10=]'; // <<---- terminate the input line
您还需要按照@Barmar 所说的进行操作,但这与您在问题中描述的问题不同。