[英]Why are lines, output by this program, duplicated?
好的,因此,我目前正在致力於實現簡單外殼歷史記錄。 要求的一部分是將所有命令輸出到一個文件中,每行對它們進行編號。
目前,我的所有其他功能都運行良好,但是文件的輸出給了我一個非常具體的問題。 它正在保存命令並將它們放入編號文件中,但是每個命令給我大約3個重復副本。 例如:
1 hello
1 hello
1 hello
2 there1 hello
2 there3 how1 hello
2 there3 how4 are1 hello
2 there3 how4 are5 you
我已經搜索了該站點上的先前主題,並閱讀了文件I / O。 據我所知,我做得正確:
#include "parser.h"
#include "shell.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct node {
char info[MAXINPUTLINE]; //Struct setup for linked list.
struct node *link;
} *start;
void create(char[]); //Function prototypes.
void insert_end(char[]);
void last_cmd(char[]);
int main(void) {
char input[MAXINPUTLINE];
int count = 2;
start=NULL;
char data[MAXINPUTLINE];
FILE *file;
file = fopen(".simpleshell_history", "w");
char *history="history";
char *last="!!";
signal_c_init();
printf("Welcome to the sample shell! You may enter commands here, one\n");
printf("per line. When you're finished, press Ctrl+D on a line by\n");
printf("itself. I understand basic commands and arguments separated by\n");
printf("spaces, redirection with < and >, up to two commands joined\n");
printf("by a pipe, tilde expansion, and background commands with &.\n\n");
printf("\nclsh$ ");
fgets(data, sizeof(data), stdin);
create(data);
fprintf(file, "%d ", 1);
fprintf(file, "%s", data);
parse(data);
printf("\nclsh$ ");
while (fgets(input, sizeof(input), stdin)) {
stripcrlf(input);
parse(input);
insert_end(input);
fprintf(file, "%d ", count);
fprintf(file, "%s", input);
count++;
printf("\nclsh$ ");
}
fclose(file);
return 0;
}
//Functions below.
//Creation Function.
void create(char data[])
{
struct node *temp;
temp = (struct node *)malloc(sizeof(struct node));
if (start == NULL)
{
strcpy(temp->info, data);
temp->link=NULL;
start=temp;
}
}
//Insertion function
void insert_end(char data[])
{
struct node *ptr, *tempnode;
ptr = start;
while(1)
{
if(ptr->link != NULL)
{
ptr=ptr->link;
}
else
break;
}
tempnode=(struct node *)malloc(sizeof(struct node));
strcpy(tempnode->info, data);
tempnode->link=NULL;
ptr->link=tempnode;
}
//Last command repeat function
void last_cmd(char input[])
{
struct node *ptr;
ptr = start;
int i;
int length=0;
char temp[MAXINPUTLINE];
while (ptr!=NULL)
{
ptr=ptr->link;
length++;
}
ptr=start;
for (i=0; i<length-1; i++)
{
ptr=ptr->link;
strcpy(temp, ptr->info);
}
printf("%s", temp);
parse(temp);
}
我知道我以前的一些問題沒有得到很好的回答,但是我在此問題上已盡了最大的努力,只是作為最后的手段問這個問題。 我希望我做到的方式不會令人討厭或違反任何規則,並且可以在這里提高我的聲譽。
謝謝。
編輯:好的,所以我將問題隔離到解析輸入的文件中。 這造成了行的重復,但是我現在還不知道怎么做。 感謝您的幫助,但我會以任何一種方式堅持下去,再次感謝您提供的所有提示和建議。
分析器:
#include "parser.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pwd.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/wait.h>
/* Static Variables Referenced only in this file */
static int pipefd[2];
static int background;
void parse(char *cmdline)
{
char *cmdpart[2];
pipefd[0] = PARSE_NOPIPE; /* Init: default is no pipe */
background = checkbackground(cmdline);
/* Separate into individual commands if there is a pipe symbol. */
if (strstr(cmdline, "|"))
pipefd[0] = PARSE_USEPIPE;
/* Must do the strtok() stuff before calling parse_cmd because
strtok is used in parse_cmd or the functions parse_cmd calls. */
cmdpart[0] = strtok(cmdline, "|");
cmdpart[1] = strtok((char *)NULL, "|");
parse_cmd(cmdpart[0]);
if (cmdpart[1]) parse_cmd(cmdpart[1]);
}
/* parse_cmd will do what is necessary to separate out cmdpart and run
the specified command. */
void parse_cmd(char *cmdpart)
{
int setoutpipe = 0; /* TRUE if need to set up output pipe
after forking */
int pid; /* Set to pid of child process */
int fd; /* fd to use for input redirection */
char *args[MAXARGS + 5];
char *filename; /* Filename to use for I/O redirection */
splitcmd(cmdpart, args);
if (pipefd[0] == PARSE_USEPIPE) {
pipe(pipefd);
setoutpipe = 1;
}
pid = fork();
if (!pid) { /* child */
if (setoutpipe) {
dup2(pipefd[1], 1); /* connect stdout to pipe if necessary */
}
if (!setoutpipe && (pipefd[0] > -1)) {
/* Need to set up an input pipe. */
dup2(pipefd[0], 0);
}
filename = parseredir('<', args);
if (filename) { /* Input redirection */
fd = open(filename, O_RDONLY);
if (!fd) {
fprintf(stderr, "Couldn't redirect from %s", filename);
exit(255);
}
dup2(fd, 0);
}
if ((filename = parseredir('>', args))) { /* Output redirection */
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (!fd) {
fprintf(stderr, "Couldn't redirect to %s\n", filename);
exit(255);
}
dup2(fd, 1);
}
if (!args[0]) {
fprintf(stderr, "No program name specified.\n");
exit(255);
}
execvp(args[0], args);
/* If failed, die. */
exit(255);
} else { /* parent */
if ((!background) &&
(!setoutpipe))
waitpid(pid, (int *)NULL, 0);
else
if (background)
fprintf(stderr, "BG process started: %d\n", (int) pid);
if (pipefd[0] > -1) { /* Close the pipe if necessary. */
if (setoutpipe)
close(pipefd[1]);
else
close(pipefd[0]);
}
} /* if (!pid) */
freeargs(args);
} /* parse_cmd() */
/* splitcmd() will split a string into its component parts.
Since splitcmd() uses strdup, freeargs() should be called on the
args array after it is not used anymore. */
void splitcmd(char *cmdpart, char *args[])
{
int counter = 0;
char *tempstr;
tempstr = strtok(cmdpart, " ");
args[0] = (char *)NULL;
while (tempstr && (counter < MAXARGS - 1)) {
args[counter] = strdup(expandtilde(tempstr));
args[counter + 1] = (char *)NULL;
counter++;
tempstr = strtok(NULL, " ");
}
if (tempstr) { /* Broke out of loop because of num of args */
fprintf(stderr, "WARNING: argument limit reached, command may be truncated.\n");
}
}
/* expandtilde() will perform tilde expansion on str if necessary. */
char *expandtilde(char *str)
{
static char retval[MAXINPUTLINE];
char tempstr[MAXINPUTLINE];
char *homedir;
char *tempptr;
int counter;
if (str[0] != '~') return str; /* No tilde -- no expansion. */
strcpy(tempstr, (str + 1)); /* Make a temporary copy of the string */
if ((tempstr[0] == '/') || (tempstr[0] == 0))
tempptr = (char *)NULL;
else { /* Only parse up to a slash */
/* strtok() cannot be used here because it is being used in the function
that calls expandtilde(). Therefore, use a simple substitute. */
if (strstr(tempstr, "/"))
*(strstr(tempstr, "/")) = 0;
tempptr = tempstr;
}
if ((!tempptr) || !tempptr[0]) { /* Get user's own homedir */
homedir = gethomedir();
} else { /* Get specified user's homedir */
homedir = getuserhomedir(tempptr);
}
/* Now generate the output string in retval. */
strcpy(retval, homedir); /* Put the homedir in there */
/* Now take care of adding in the rest of the parameter */
counter = 1;
while ((str[counter]) && (str[counter] != '/')) counter++;
strcat(retval, (str + counter));
return retval;
}
/* freeargs will free up the memory that was dynamically allocated for the
array */
void freeargs(char *args[])
{
int counter = 0;
while (args[counter]) {
free(args[counter]);
counter++;
}
}
/* Calculates number of arguments in args */
void calcargc(char *args[], int *argc)
{
*argc = 0;
while (args[*argc]) {
(*argc)++; /* Increment while non-null */
}
(*argc)--; /* Decrement after finding a null */
}
/* parseredir will see if it can find a redirection operator oper
in the array args[], and, if so, it will return the parameter (filename)
to that operator. */
char *parseredir(char oper, char *args[])
{
int counter;
int argc;
static char retval[MAXINPUTLINE];
calcargc(args, &argc);
for (counter = argc; counter >= 0; counter--) {
fflush(stderr);
if (args[counter][0] == oper) {
if (args[counter][1]) { /* Filename specified without a space */
strcpy(retval, args[counter] + 1);
argsdelete(args + counter);
return retval;
} else { /* Space seperates oper from filename */
if (!args[counter+1]) { /* Missing filename */
fprintf(stderr, "Error: operator %c without filename", oper);
exit(255);
}
strcpy(retval, args[counter+1]);
argsdelete(args + counter + 1);
argsdelete(args + counter);
return retval;
}
}
}
return NULL; /* No match */
}
/* Argsdelete will remove a string from the array */
void argsdelete(char *args[])
{
int counter = 0;
if (!args[counter]) return; /* Empty argument list: do nothing */
free(args[counter]);
while (args[counter]) {
args[counter] = args[counter + 1];
counter++;
}
}
編輯:好的,所以我明白了。 parse(input)和stripcrlf(input)搞砸了。 如何確定,我不確定,但這不重要。 最后,我需要做的不僅是打開文件並保持打開狀態,直到在程序結束之前將其關閉。 我必須這樣做:fopen(“。simpleshell_history”,“ a”); fprintf(file,“%d”,count); fprintf(file,“%s \\ n”,輸入); FCLOSE(文件); 基本上,我必須打開文件,將文件放入,然后在解析器之前將其關閉,這樣可能會起作用。 現在的輸出是完美的。 感謝大家的幫助。
我從問題代碼中刪除了不相關的行:
#include <stdio.h>
#include <string.h>
#define MAXINPUTLINE 100
int main(void) {
char input[MAXINPUTLINE];
int count = 2;
// start=NULL;
char data[MAXINPUTLINE];
FILE *file;
我將文件名從“ .simpleshell_history”修改為“ simpleshell_history”,以便不隱藏該文件(出於測試目的)。
file = fopen("simpleshell_history", "w");
// char *history="history";
// char *last="!!";
// signal_c_init();
printf("Welcome to the sample shell! You may enter commands here, one\n");
printf("per line. When you're finished, press Ctrl+D on a line by\n");
printf("itself. I understand basic commands and arguments separated by\n");
printf("spaces, redirection with < and >, up to two commands joined\n");
printf("by a pipe, tilde expansion, and background commands with &.\n\n");
printf("\nclsh$ ");
fgets(data, sizeof(data), stdin);
// create(data);
fprintf(file, "%d ", 1);
fprintf(file, "%s", data);
// parse(data);
printf("\nclsh$ ");
while (fgets(input, sizeof(input), stdin)) {
// stripcrlf(input);
// parse(input);
// insert_end(input);
fprintf(file, "%d ", count);
fprintf(file, "%s", input);
我添加了以下兩行,以提供一種突破循環的方法。
if(0 == strncmp("quit", input, 4))
break;
count++;
printf("\nclsh$ ");
}
fclose(file);
return 0;
}
我運行了程序:
> ./test
Welcome to the sample shell! You may enter commands here, one
per line. When you're finished, press Ctrl+D on a line by
itself. I understand basic commands and arguments separated by
spaces, redirection with < and >, up to two commands joined
by a pipe, tilde expansion, and background commands with &.
clsh$ hello
clsh$ there
clsh$ QUIT
clsh$ QUIT
clsh$ quit
文件“ simpleshell_history”的內容:
1 hello
2 there
3 QUIT
4 QUIT
5 quit
該代碼似乎按預期執行,沒有意外的“輸出文件內容重復”。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.