I have a simple shell program in C that needs to be able to run as a command line and take in input from the user and also take in input from a file piped in as < commands.txt
. for the purposes of this project this is done with a make file command make run < commands.txt
When I run the program and enter commands manually, everything works as expected. However, when I run the command with < commands.txt
the command input doesn't print, so it prints my command prompt followed by the command output.
The only obvious way I can think of to resolve this is to print the command input myself, but then it'll just repeat the command input when I'm typing manually.
How can I print the input when it's piped in from a file, but not reprint it when it's being entered manually?
Just to note, I previously had an error where my output was printing out of order that I resolved by using Output of printf out of order, I don't know if that's necessarily the correct thing to do in this case though.
Here's my code:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAX 102
int main (int argc, char* argv[]) {
// here we set whether the prompt is user
// input or our default
char* prompt;
if (argc > 1) {
prompt = argv[1];
}
else {
prompt = "> ";
}
char commandString[MAX];
char exitString[MAX] = "exit";
//setbuf(stdout, NULL);
setvbuf(stdout, NULL, _IONBF, 0);
// this is the main loop that prompts the user successively.
// It will take in a command from the user, run it, and
// return to the prompt.
while (1){
printf("%s", prompt);
// we make sure our command string is completely overwritten with
// null in order to ensure there's always a null terminator whereever
// the command string ends
for (int i = 0; i < MAX; i++){
commandString[i] = '\0';
}
// here we read stdin char by char scanning for
// a newline or an end of file, while also filling out
// our command string character by character for the first
// MAX characters of the input stream. This way we can
// clear the entire input buffer while only saving the portion
// we want to save.
char c;
int counter = 0;
while ((c = getchar()) != '\n' && c != EOF){
if (counter < MAX){
commandString[counter] = c;
}
counter++;
}
if (c == EOF){
printf("EOF found, exiting\n");
break;
}
//printf("\n\nCommand string: %s\n\n", commandString);
// quit if the user entered exit
if (strcmp(commandString, exitString) == 0) break;
// here we break commandString up into tokens and output
// the tokens to an array of arguments, ending the array
// with NULL
char* commandToken = strtok(commandString, " ");
int argsSize = 6;
char* commandArgs[argsSize];
int argsCount = 0;
while(commandToken != NULL && argsCount < argsSize - 1){
commandArgs[argsCount] = commandToken;
argsCount++;
commandToken = strtok(NULL, " ");
}
commandArgs[argsCount] = NULL;
// If we have a command, we fork the process into a parent and child,
// pause the parent process, and execute the command on the child process.
// once the child process returns, we resume the parent process and
// print the child's id and status.
if (commandArgs[0] != NULL){
/*
printf("command name - %s\n\n", commandArgs[0]);
argsCount = 0;
while(commandArgs[argsCount] != NULL){
printf("command arg %i - %s\n\n", argsCount, commandArgs[argsCount]);
argsCount++;
}
*/
pid_t parentProcess = getpid();
pid_t result = fork();
if (result < 0){
printf("Error in fork");
break;
}
// Child
else if (result == 0) {
pid_t childProcess = getpid();
//printf("Child process id - %i\n\n", (int)childProcess);
execvp(commandArgs[0], commandArgs);
printf("%s: command not found\n", commandArgs[0]);
return 2;
}
// Parent
else {
int status;
waitpid(result, &status, 0);
if ( WIFEXITED(status) ) {
int exitStatus = WEXITSTATUS(status);
printf("Child %i exited with status %i\n", result, exitStatus);
}
}
}
}
return 0;
}
As far as I can figure there shouldn't be anything wrong with the fork - exec - wait steps of the code, but maybe I didn't think of something?