【发布时间】:2019-05-03 17:32:40
【问题描述】:
我正在编写一个简单的 unix shell 程序,但遇到了由 fgets() 调用引起的分段错误的问题。通过调用,我试图从命令行获取用户输入并将其保存以供进一步使用。应该很简单。在 MacOS 上,代码编译时会发出一个警告,但执行得很好。在我的 Ubuntu 虚拟机上进行测试时,我遇到了一个段错误,我目前已将其范围缩小到特定的 fgets() 调用。
我对 DDD 不太熟悉,但我能够使用它和一些简单的打印语句来确定它的 fgets() 调用给我带来了麻烦。我检查了分配调用的指针是否已正确分配。尽管我的两台机器运行不同版本的 gcc,但我很困惑为什么我只在一个系统上而不是在两个系统上都出现段错误。
下面是我的源代码,我遇到问题的具体函数是 parse() 函数。这不是完整或未完成的代码,但我希望不断提示用户输入,从命令行接收输入,保存此输入并将其拆分为令牌以传递给 execute() 以供进一步使用.我已被告知并了解我目前存在内存泄漏和范围错误。我仍然不确定为什么在 Ubuntu 上,我第一次调用 parse() 并使其进入函数内部的 fgets() 调用时出现分段错误。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define PROMPT "MyShell> "
#define MAX_SIZE 256
#define EXIT_CMD "exit"
/**
@brief Takes a pointer as an argument and checks whether or not it is NULL
(hasn't been properly allocated in memory). If the pointer is NULL,
behavior is undefined, so an error message is displayed to the user
and the program is terminated.
*/
void validateMemoryAllocation(char* pointer)
{
if (pointer == NULL)
{
printf("%s", "Fatal Error: Failed to allocate memory to save command input. Exiting...\n");
exit(0);
}
}
/**
@brief Fork a child to execute the command using execvp. The parent should wait for the child to terminate
@param args Null terminated list of arguments (including program).
@return returns 1, to continue execution and 0 to terminate the MyShell prompt.
*/
int execute(char **args)
{
if (strcmp(args[0], "exit") == 0) // Check for exit command.
{
printf("Exit received. Terminating MyShell...\n");
return 1; // Return to main with exit value to terminate the program.
} else // Not exit command, proceed attempting to execute.
{
}
return 0; // Return 0 and continue MyShell.
}
/**
@brief gets the input from the prompt and splits it into tokens. Prepares the arguments for execvp
@return returns char** args to be used by execvp
*/
char** parse(void)
{
char *rawInput, *inputDup, *token;
int validCheck, argCount, i, newLineLocation;
/* Save the entire line of user input. */
rawInput = malloc(sizeof(char) * MAX_SIZE);
validateMemoryAllocation(rawInput);
fgets(rawInput, MAX_SIZE, stdin);
inputDup = strdup(rawInput); /* Duplicate the string for modification. */
/* First loop: Count number of total arguments in user input. */
argCount = 0;
while( (token = strsep(&inputDup, " ")) != NULL)
{
argCount++;
}
/* Create array to hold individual command arguments. */
char* tokenArray[argCount];
/* Second loop: save tokens as arugments in tokenArray. */
for (i = 0; i < argCount; i++)
{
token = strsep(&rawInput, " ");
tokenArray[i] = token;
}
/**
Before returning the arguments, trim the dangling new line
character at the end of the last argument.
*/
tokenArray[argCount - 1] = strtok(tokenArray[argCount - 1], "\n");
return tokenArray;
}
/**
@brief Main function should run infinitely until terminated manually using CTRL+C or typing in the exit command
It should call the parse() and execute() functions
@param argc Argument count.
@param argv Argument vector.
@return status code
*/
int main(int argc, char **argv)
{
int loopFlag = 0;
char** input;
/* Loop to continue prompting for user input. Exits with proper command or fatal failure. */
while (loopFlag == 0)
{
printf("%s", PROMPT); // Display the prompt to the user.
input = parse(); // Get input.
loopFlag = execute(input); // Execute input.
}
return EXIT_SUCCESS;
}
我希望将用户输入保存到字符串指针 rawInput,这在 MacOS 上是这种情况,但在 Ubuntu 上不是。
编辑:如果有帮助,这里是使用的两个系统的一些示例输出。我知道我有一些内存泄漏需要修补。
MacOS
D-10-16-18-145:a1 user$ ./myshell
MyShell> hello
MyShell> darkness
MyShell> my
MyShell> old
MyShell> friend
MyShell> exit
Exit received. Terminating MyShell...
D-10-16-18-145:a1 user$
Ubuntu
MyShell> hello
Segmentation fault (core dumped)
【问题讨论】:
-
您的
rawInput是否以空值结尾?段错误不是由fgets引入的,而是由其他东西引入的 -
我认为
fgets()调用没有问题。问题可能出在其他地方。一旦你做了一些导致未定义行为的事情,它可能会导致程序中其他任何地方的不当行为。 -
@EugeneSh。
fgets()不需要缓冲区以空值终止,甚至不需要初始化。 -
@barmar 但
strdup可以 -
@EugeneSh。
fgets()总是添加一个空终止符。
标签: c segmentation-fault fgets