我的家庭作业是创建一个带有历史记录功能(完成)的shell(完成),然后将历史记录写入文件(这是我遇到问题的地方),这样就可以在下次运行时加载它。 / p>
我通常是一个Java人,因此我需要查找几乎每个c函数。无论如何,我有问题将我的字符串数组写入文件。出于某种原因,我依旧根据我使用的编码获得奇怪的符号。 例如,如果我运行程序并立即退出,则历史文件包含:
??_?
如果我运行ls然后退出,则历史文件包含:
ls _?
我删除了无关的程序。提前感谢任何指针。这是脱毛的第3天。我的智慧结束了。
#include
/*Had to use \ so that the includes would show here. */
#include \stdlib.h>
#include \signal.h>
#include \sys/types.h>
#include \unistd.h>
#include \string.h>
#include \errno.h>
/*
* Constant Declarations
*/
#define MAX_LINE 80
#define BUFFER_SIZE 50
#define HIST_SIZE 10
static char buffer[BUFFER_SIZE];
char history[HIST_SIZE][BUFFER_SIZE];
int count = 0;
int caught = 0;
char historyFileLoc[] = "./name.history";
void loadHistory() {
int i;
char histCommand[BUFFER_SIZE];
i = 0;
FILE *hisFile = fopen(historyFileLoc, "r");
if( hisFile ) {
/*If a user edits the history file, only the first ten entries will be loaded */
while(!feof(hisFile)) {
if(fscanf(hisFile, "%s\n", histCommand) == 1){
strcpy(history[i], histCommand);
i++;
count++;
}
}
}
if(hisFile != NULL){
if(fclose(hisFile) != 0) {
perror("History file (r) was not closed correctly");
}
}
}
void saveHistory() {
int i;
char buffer[MAX_LINE];
FILE *hisFile = fopen(historyFileLoc, "w");
for(i=0; i < HIST_SIZE; i++){
if(history[i] != '\0') {
strcpy(buffer, history[i]);
/*fwrite(history[i], 1, strlen(history[i]), hisFile);*/
/* fputs(history[i], hisFile);*/
if(buffer != NULL) {
fprintf(hisFile,"%s\n", buffer);
}
}
}
if(fclose(hisFile) != 0) {
perror("History file was not closed correctly");
}
}
/*
* The main() function presents the prompt "sh>" and then invokes setup(), which waits for the
* user to enter a command. The contents of the command entered by the user are loaded into the
* args array. For example, if the user enters ls Ðl at the COMMAND-> prompt, args[0] will be set
* to the string ls and args[1] will be set to the string Ðl. (By ÒstringÓ, we mean a
* null-terminated, C-style string variable.)
*/
int main(void) {
char inputBuffer[MAX_LINE]; /* buffer to hold the command entered */
int background, status; /* equals 1 if a command is followed by '&' */
char *args[MAX_LINE / 2 + 1]; /* command line arguments */
pid_t pid; /* the process's id */
...
}
历史转储[] []:
./660_Lab04.c.out sh ->ls 660_Lab03_Tests.txt Lab 03 Documentation.docx 660_Lab04.c buffer.h 660_Lab04.c.out name.history 660__Lab01.c main.c 660__Lab03.c main_not_mine.c CSE_660Lab01Documentation.doc This is where the dump begins minus sh-> sh ->ls ls _? ??_?
我的其余代码。 loadHistory和saveHistory在此之前:
void printHistory() {
int i;
int j = 0;
int histcount = count;
printf("\n");
for (i=0; i < HIST_SIZE; i++) {
printf("%d. ",histcount); /* Used to print the correct hitory number */
while (history[i][j] != '\n' && history[i][j] != '\0') {
printf("%c",history[i][j]);
j++;
}
printf("\n");
j=0;
histcount--;
if(histcount == 0) {
break;
}
}
printf("\n");
printf("sh -> ");
}
/* the signal handler function */
void handle_SIGINT() {
write(STDOUT_FILENO,buffer,strlen(buffer));
printHistory();
caught = 1;
}
void setup(char inputBuffer[], char *args[], int *background) {
int length, /* # of characters in the command line */
i, /* loop index for accessing inputBuffer array */
start, /* index where beginning of next command parameter is */
ct, /* index of where to place the next parameter into args[] */
k; /* Generic counter */
ct = 0;
/* read what the user enters on the command line */
length = read(STDIN_FILENO, inputBuffer, MAX_LINE);
if(caught == 1) {
length = read(STDIN_FILENO, inputBuffer, MAX_LINE);
caught = 0;
}
/* checks to see if the command is a history retrieval command. If it isn't then add it to the history */
if((strcmp(inputBuffer, "r\n\0") != 0) && (strncmp(inputBuffer, "r x", 2) != 0) ) {
for(i= (HIST_SIZE - 1); i>0; i--) {
strcpy(history[i], history[i-1]);
}
strcpy(history[0], inputBuffer);
count++;
}
start = -1;
if (length == 0) {
saveHistory();
exit(0); /* ^d was entered, end of user command stream */
} else if ((length < 0) && (errno != EINTR)) {
perror("error reading the command");
saveHistory();
exit(-1); /* terminate with error code of -1 */
}
/* Checks to see if r was entered. If so, it copies the command most recently in the input buffer */
if(strcmp(inputBuffer, "r\n\0") == 0) {
strcpy(inputBuffer,history[0]);
/* Checks to see if r x was entered. If so then it searches for the most recent command that begins with x */
} else if(strncmp(inputBuffer, "r x", 2) == 0) {
for(k=0; k < 10; k++){
if(inputBuffer[2] == history[k][0]) {
strcpy(inputBuffer,history[k]);
break;
}
}
}
length = strlen(inputBuffer);
/* examine every character in the inputBuffer */
for (i = 0; i < length; i++) {
switch (inputBuffer[i]) {
case ' ':
case '\t': /* argument separators */
if (start != -1) {
args[ct] = &inputBuffer[start]; /* set up pointer */
ct++;
}
inputBuffer[i] = '\0';
start = -1;
break;
case '\n': /* should be the final char examined */
if (start != -1) {
args[ct] = &inputBuffer[start];
ct++;
}
inputBuffer[i] = '\0';
args[ct] = NULL; /* no more arguments to this command */
break;
case '&':
*background = 1;
inputBuffer[i] = '\0';
break;
default: /* some other character */
if (start == -1) {
start = i;
}
}
args[ct] = NULL; /* just in case the input line was > 80 */
}
}
/* The main() function presents the prompt "sh->" and then invokes setup(), which waits for the user to enter a command. The contents of the command entered by the user are loaded into the args array. For example, if the user enters ls -l at the COMMAND-> prompt, args[0] will be set to the string ls and args[1] will be set to the string -l. (By string, we mean a null-terminated, C-style string variable.) */
int main(void) {
char inputBuffer[MAX_LINE]; /* buffer to hold the command entered */
int background, status; /* equals 1 if a command is followed by '&' */
char *args[MAX_LINE / 2 + 1]; /* command line arguments */
pid_t pid; /* the process's id */
/* set up the signal handler */
struct sigaction handler;
handler.sa_handler = handle_SIGINT;
sigaction(SIGINT, &handler, NULL);
loadHistory();
while (1) {
background = 0;
printf("\nsh ->");
fflush(0);
setup(inputBuffer, args, &background); /* get next command */
fflush(0);
pid = fork(); /* assign the process id */
if (pid < 0) {
fprintf(stderr, "ERROR: Could not properly fork.");
saveHistory();
exit(-1); /* unsucessful exit because the fork could not be created */
} else if (pid == 0) { /* PID was forked successfully */
status = execvp(*args, args); /* execute the command */
if (status < 0) {
fprintf(stderr, "ERROR: Could not execute %s", args[0]);
saveHistory();
exit(1);
}
} else if (background == 0) { /* if the fork is run in the foreground */
wait(NULL);
}
}
return EXIT_SUCCESS;
}
答案 0 :(得分:3)
问题可能是您在使用前没有初始化history
中的内存。与Java不同,C在声明时不会初始化内存,因此history
指针将指向垃圾,而不是零数组。
C与Java具有非常不同的内存模型,因此在其余代码中可能存在类似的问题。我建议您阅读如何在C中管理内存。
答案 1 :(得分:1)
在第一次运行时,如果未创建历史记录文件,则不会加载历史记录。但是你似乎没有清理history
数组(默认值不是全零,与Java相反),因此它包含一些垃圾。在退出处,当您将值写出来时,您将历史文件放入垃圾箱,然后将其读回。
您应该确保所有字符串都以NULL结尾。总是
请尝试执行以下操作:
main
的开头,初始化history
,以便每行在开头包含NULL strcpy
),或者复制终止NULL。顺便说一下,检查if(history[i] != '\0')
是错误的,因为history[i]
指向已分配的历史缓冲区部分。您是否想要if (history[i][0] == '\0')
(这会检查第i条历史记录行是否为空)?
嗯,固定大小的缓冲区是邪恶的,但这是另一回事。
对于新发布的代码:各处都存在大量问题。
其中之一:(strcmp(inputBuffer, "r\n\0") != 0) && (strncmp(inputBuffer, "r x", 2) != 0)
中的setup
支票完全错误。首先,您不需要显式添加\0
,因为每个字符串文字都由\0
隐式结束。第二部分:第一部分(strcmp(inputBuffer, "r\n\0") != 0)
检查inputBuffer
的内容是r\n
,第二部分(strncmp(inputBuffer, "r x", 2) != 0)
检查inputBuffer
是否以{{1}开头}。显然,这两个条件是互斥的,所以如果你在它们之间放置r x
,结果永远不会成真。
给定代码中的许多代码构造都不是您想要的。我建议你慢慢调试调试器中的代码,检查每个变量和函数调用的任何结果,我打赌你会感到惊讶。
代码最大的问题之一是使用字符串和整体内存管理。请阅读有关此主题的内容:它对C程序至关重要。几点说明:
&&
指针下的内存是否确实包含char*
。char
或strcmp
,可以为您执行此操作。通常这些功能更有效,并且没有错误。strcpy
中找到将数据放入历史记录的代码。有一部分注释“检查命令是否是历史检索命令。如果不是,则将其添加到历史记录中”,但这部分不执行(部分原因是因为它从未执行过错误检查开头,部分是因为里面的代码不会复制从 setup
,只有到它。答案 2 :(得分:-1)
您将很难让人们为您修理作业。学习编程是为了不断地将你的头撞在墙上并重新尝试,即使它感到绝望。不要放弃。