I'm writing a program that will read a list of commands from a file name cmdfile.txt and execute them in the terminal.
cmdfile.txt contain:
whoami
cal 4 2020
echo The time is:
date
I'm facing the issue that when the file only contain 1 single command line, the program works correctly. However, when the file contain more than 1 command lines, the command failed to execute or produce unknown command. Below is my working code in progress:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdbool.h>
#include <string.h>
void parse(char *line, char **argv)
{
while (*line != '\0') { /* if not the end of line ....... */
while (*line == ' ' || *line == '\t' || *line == '\n')
*line = '\0'; /* replace white spaces with 0 */
*argv = line; /* save the argument position */
while (*line != '\0' && *line != ' ' &&
*line != '\t' && *line != '\n')
line ; /* skip the argument until ... */
}
*argv = '\0'; /* mark the end of argument list */
}
void execute(char **argv)
{
pid_t pid;
int status;
pid = fork();
if (pid < 0) { /* fork a child process */
perror("The error for fork() is: ");
exit(1);
}
else if (pid == 0) { /* for the child process: */
if (execvp(*argv, argv) < 0) { /* execute the command */
perror("The error is: ");
exit(1);
}
}
else { /* for the parent: */
while (wait(&status) != pid) /* wait for completion */
;
}
}
void main(void)
{
char line[1024]; /* the input line */
char *argv[64]; /* the command line argument */
bool running = 1;
FILE *fp = fopen("cmdfile.txt", "r");
if(fp == NULL)
{
perror("Unable to open file!");
exit(1);
}
while (fgets(line, 1024, fp)) { /* repeat until done .... */
strtok(line, "\n");
parse(line, argv);
execute(argv);
}
}
My first guess is because of fgets didn't behave as I expected due to the separation of new lines? I have tried to print the line out to terminal to trace the error but I don't know what's wrong with the program. Can anyone please help me to point out the error I made?
My output when running the above code:
The error is: : No such file or directory
cal: not a valid year 2020
The time is:
The error is: : No such file or directory
The time is:
The error is: : No such file or directory
The error is: : No such file or directory
CodePudding user response:
The cmdfile.txt
file comes from a Windows machine and was copied to a Linux machine without translating the line endings. Therefore, each line ends with CRLF — "\r\n"
. Your code carefully zaps the '\n'
with a null byte, but that leaves the '\r'
in the string. When you pass "whoami\r"
to execvp()
, it cannot find the command; it would find "whoami"
but not the other. It seems that cal
doesn't like numbers that end with \r
either. And so on. (The echo
command isn't fussed about the '\r'
.)
There are many ways to fix it. In the context of your code, the simplest is to change the strtok()
line in the loop in main()
to read:
strtok(line, "\r\n");
The sequence of the characters in the string doesn't matter.