【发布时间】:2014-05-13 03:22:54
【问题描述】:
好的,所以,我目前正在实现一个 simpleshell 历史。部分要求是将所有命令输出到每行编号的文件中。
目前我的其他一切都运行良好,但文件的输出给了我一个非常具体的问题。它正在保存命令并将它们放在编号的文件中,但它给我每个命令大约 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(文件,“%d”,计数); fprintf(文件,“%s\n”,输入); fclose(文件);基本上,我必须打开文件,把东西放进去,然后在解析器之前关闭它,这样可能会产生影响。现在输出很完美。感谢大家的帮助。
【问题讨论】:
-
Starlincr32,恭喜您提交问题。下次,考虑将问题的标题表述为问题。具体来说,“输出文件内容重复”实际上不是一个问题;也许标题应该是“为什么该程序输出的行重复?” (请随时编辑此问题以改进标题。)
-
你没有给出完整的例子。但是,在注释掉所有那些不存在的函数/变量之后,我设法得到了一个工作程序,它按预期工作。所以你的问题的原因很可能在你没有显示的代码中。
-
公平地说,我见过有人发布问题并只是倾倒大量代码的例子,所以我试图给出一个我认为问题所在的截断示例。我会编辑它,让标题更有意义,感谢您的反馈。
-
@StarIncr34 你应该发布一个完整的程序,以便其他人可以编译它并在运行程序时看到问题。
-
好的,我明白了。 parse(input) 和 stripcrlf(input) 搞砸了。如何,我不确定,但那是无关紧要的。最后我需要做的不仅仅是打开文件并让它保持打开状态,直到它在程序结束之前关闭。我必须这样做: fopen(".simpleshell_history", "a"); fprintf(文件,“%d”,计数); fprintf(文件,“%s\n”,输入); fclose(文件);基本上,我必须打开文件,把东西放进去,然后在解析器之前关闭它,这样可能会产生影响。现在输出很完美。感谢大家的帮助。