我一直在用C编写Shell程序。该程序在Linux(Ubuntu 16.04)中按预期工作,但在MacOS(10.14.2 Mojave)中却得到了意外输出。
/* A shell program.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
void input(char* argv[]);
void print_arr(char *argv[]); // For debugging
int
main(void)
{
while (1)
{
pid_t pid;
char *argv[100];
// Display shell prompt
write(1, "(ash) $ ", 8);
// Take user input
input(argv);
// print_arr(argv); // DEBUG STATEMENT
if (argv[0] != NULL)
{
// Exit if exit command is entered
if (strcmp(argv[0], "exit") == 0)
{
exit(0);
}
// Create child process
if ((pid = fork()) > 0)
{
wait(NULL);
}
else if (pid == 0)
{
// print_arr(argv); // DEBUG STATEMENT
execvp(argv[0], argv);
printf("%s: Command not found\n", argv[0]);
exit(0);
}
else
{
printf("Fork Error!\n");
}
}
}
}
/* Takes input from user and splits it in
tokens into argv. The last element in
argv will always be NULL. */
void
input(char* argv[])
{
const int BUF_SIZE = 1024;
char buf[BUF_SIZE];
int i;
buf[0] = '\0';
fgets((void*) buf, BUF_SIZE, stdin);
i = 0;
argv[i] = strtok(buf, " \n\0");
while (argv[i] != NULL)
{
argv[++i] = strtok(NULL, " \n\0");
}
}
/* Print argv for debugging */
void
print_arr(char *argv[])
{
int i = 0;
while (argv[i] != NULL)
{
printf("%d: %s\n", i, argv[i]);
++i;
}
}
在Linux中:
(ash) $ ls
// files and folders are listed
在MacOS中(带有调试语句):
(ash) $ ls
0: p?M??
0: ??M??
: Command not found
(ash) $ ls
0: ls
0: ??M??
: Command not found
(ash) $ ls
0: ls
0: ??M??
我不明白为什么char* argv[]
的{{1}}的内容会被修改?
我也在默认的fork()
编译器和brew的clang
中进行了尝试,结果相同。
答案 0 :(得分:1)
当程序无端地表现出不同的行为时,这是未定义行为的非常好兆头。这也是这里的原因。
数组buf
对于函数input
是局部的,并且在函数退出时不再存在。
解决此问题的一种方法是在buf
中声明main
并将其传递给input
。您还将需要fgets
的缓冲区大小。
void
input(char * argv[], char * buf, size_t size)
{
buf[0] = '\0';
fgets(buf, sizeof buf, stdin);
argv[0] = strtok(buf, " \n\0");
for(int i=0; argv[i] != NULL; i++) argv[i+1] = strtok(NULL, " \n\0");
}
另一种解决方案(尽管我怀疑很多人对此不屑一顾)是将buf
声明为static
,但是随后您需要将BUF_SIZE
更改为#define
或硬编码值,因为您不能拥有静态VLA。
#define BUF_SIZE 1024
void
input(char * argv[])
{
static char buf[BUF_SIZE];
buf[0] = '\0';
fgets(buf, sizeof buf, stdin);
argv[0] = strtok(buf, " \n\0");
for(int i=0; argv[i] != NULL; i++) argv[i+1] = strtok(NULL, " \n\0");
}
我完全删除了对void*
的强制转换。我还将while循环更改为for循环,以使该循环变量在该循环中是本地的。