I'm writing a program that acts as a simple shell. Users call the program from the command line and are prompted to enter commands that are sent to the OS for completion. It should run until the user enters "done", at which point the program should break. I'm running into a problem with entering done - the program quits as it should, but prints
sh: -c: line 0: syntax error near unexpected token `done'
sh: -c: line 0: `done'
to the terminal before finishing execution. Here's the code I've written that applies:
char isDone[] = "done\n"; //fgets stores the new line character from the command line
do {
printf(">>"); //print the prompt each time through the loop
fgets(command, 50, stdin); //get a line from the terminal
system(command);
} while (strcmp(command, isDone) != 0); //break the loop if the user types in done
I think the error has to do with the fact "done" is not a valid UNIX command, but I'm not sure how to deal with this error. I tried solving this problem with the following fix:
if(system(command) == -1){
printf("The OS doesn't recognize this command");
}
else
system(command);
But that didn't solve the problem or printing the errors to the screen, and created a second problem of printing the commands/errors twice - once in the if conditional block, and once in the else block. How can I solve this problem?
EDIT
This is a homework question the requires using a do-while. Is there a solution that uses a do-while?
The do...while construct executes its body before the loop condition is checked. So by the time the loop "realizes" that the user entered done, it has already tried to execute that input as a command inside the loop body.
The clearest way to fix this is to use break:
while (1)
{
fgets(command, 50, stdin);
if (!strcmp(command, isDone)) break;
system(command);
}
The reason to structure it this way is that each iteration consists of both actions that should be done before the condition (reading in the user input) and actions that should be done after the condition (executing the command with system()). Because of this, neither a do...while or simple while will allow you to structure your code intuitively. The break keyword gives you a way to put the loop's termination condition in the middle of the loop body.
The order of execution is:
fgets(command, 50, stdin); //get a line from the terminal
system(command);
strcmp(command, isDone) != 0
So the line "done" is read, sent to system (which tries to execute it as a shell command, printing an error) and only then it checks it.
You can try something like that:
for(;;){
printf(">>"); //print the prompt each time through the loop
fgets(command, 50, stdin); //get a line from the terminal
if(!strcmp(command, isDone)) break; //break the loop
system(command);
}
Edit: if you want to keep the do-while:
printf(">>"); //print the prompt each time through the loop
fgets(command, 50, stdin); //get a line from the terminal
do {
system(command);
printf(">>"); //print the prompt each time through the loop
fgets(command, 50, stdin); //get a line from the terminal
} while (strcmp(command, isDone) != 0); //break the loop if the user types in done
But the break version is clearly more readable.
After the fgets(), do the system() call inside an if statement:
if ( strcmp( isDone, command) != 0 ) {
system( command );
}
Related
I'm iterating through a loop. On the first iteration, i can write on the stdin and get the data I want. on the second operation: name is skipped, and it asks me for name2. Why is it skipping name?
for (i = 0; TRUE; i++) {
printf("> nom :");
fgets(items[i].name, 15, stdin);
printf("nom: %s\n", items[i].name);
if (items[i].name[0] == '.') break;
printf("> prenom : ");
fgets(items[i].name2, 15, stdin);
printf("name2: %s\n", items[i].name2);
}
The code you presented does not prompt for the name, even on the first iteration of the loop. If you get any prompting for it at all then that is happening before entering the loop. In contrast, the code presented does prompt for name2.
Each iteration of the loop begins by reading the next name (without prompting) and printing it. If you see different behavior then you are running different code.
I am writing a program that asks the user for a linux bash command and then stores them in pointer arrays (kind of like char *argv[]). The program must then make a check if this command is a normal bash command or a cd (change directory) command. If its a cd command then it should use something like chdir(). If the command is anything else I wanna use some variation of the exec() system call to execute that command.
However I am not succeeding with the first part (chdir()).
int ii=-1
printf("Enter the command: ");
fgets(command, 100, stdin);
command[strlen(command)-1]=0;
printf("Command = %s\n", command);
if (command[0]=='c' && command[1]=='d' && command[2]==' ')
{
printf("I am inside CD now.\n");
cd_dump[0] = strtok(command," ");
while(sub_string[++ii]=strtok(NULL, " ") != NULL)
{
printf("%s\n", sub_string[0]);
}
chdir(sub_string[0]);
}
Edit:
I have also tried the following if statement without luck.
if (command[0]=='c' && command[1]=='d' && command[2]==' ')
{
printf("I am inside CD now.\n");
chdir(command+3);
}
Sadly the program isnĀ“t doing what I want it to, and even after hours trying to solve the issue I have no idea why. What have I done wrong? Also if I input cd /home/ why does the output result in sub_string[0] end up with an extra "Enter key" on the output? Does strtok save the Enter key into the string?
Any help on the subject is very much appreciated.
Calling chdir() only affects the current process, not its parent process.
If you chdir() and exit immediately, it is pointless - the shell you call it from keeps its old cwd. That's why cd is always a shell builtin.
Use
char buffer[PATH_MAX];
if (getcwd(buffer, sizeof buffer) >= 0) {
printf("Old wd: %s\n", buffer);
}
chdir(command+3);
if (getcwd(buffer, sizeof buffer) >= 0) {
printf("New wd: %s\n", buffer);
}
to verify chdir() works correctly.
I think I'd do something like this:
if (command[0]=='c' && command[1]=='d' && command[2]==' ')
{
for(i=2, i++, command[i]!=' '); /* Skip to nonspace */
chdir(command+i);
}
Sorry for the bad title, but i didn't know a better one!
Target: I'm trying to make an command handler. So I'm printing out via printf("cmd: ") and listing on stdin via fgets(). If theres an Input I'm check on commands via if .. else if. So my Problem now: If there is no input on stdin it should repeat the function and print cmd!
int cmd_handler()
{
printf("cmd: ");
char command[LINE_MAX];
fgets(command, LINE_MAX, stdin);
if(command != NULL)
{
if(strcmp(command, "xyz"))
{
xyz();
}
}
return 0;
}
I really don't know how i can arrange that. simple call cmd_handler() on else isn't working. Maybe someone can give me a tip how to solve it.
EDIT:
It should look like this, if there is no input(2x for example) on stdin:
cmd:
cmd:
cmd:
THIS CODE ISN'T THE REAL ONE!
regards
You need a loop. I would suggest a while loop with an exit condition, perhaps set by an "exit" command.
int run = 1;
while (run) {
printf("cmd: ");
...
else if (strcmp(command, "exit") == 0) {
run = 0;
}
}
You need to check whether fgets() returns NULL, not whether command is NULL:
if (fgets(command, LINE_MAX, stdin) != NULL)
Then you can add an 'else' clause to handle the error condition, as you were trying to do.
The way you check for empty string is not going to work.
if(command != NULL)
This condition will always evaluate to true as command, in the expression, gets converted to a pointer and is always non-null.
To check if there are any alpha-numeric characters, use isalnum() from <ctype.h> and ensure there's no whitespace characters in command. You seem to want to use recursion whereas a loop is probably more suited.
char command[LINE_MAX];
int alnum=0;
while(1) {
alnum=0;
printf("cmd: ");
fgets(command, LINE_MAX, stdin);
for(i=0;i<strlen(command);i++)
if(isalnum(command[i])) {
alnum=1;
break;
}
if(!alnum) continue;
if(strcmp(command, "xyz"))
{
xyz();
}
....
break;
}
This way, you can ensure it handles any whitespace you may input. But rest of your strcmp(command"xyy") will fail if user inputs " xyz". So it may suffice to check whether user simply hits ENTER:
if(command[0] == '\n') continue;
instead of the above check using alnum().
So the specific part of my program looks like this:
printf("Please input command:\n>");
While 1 {
if ((int c = read(STDIN_FILENO, input, Buffer_size) == 0) {
break;
}
rest of the program uses strtok to break the input
down and store in array. Then pass it to a function which checks for
various commands and prints whatever was the command
suppose to do or gives syntax error for incorrect commands
printf(">"); //last line
}
So here's what happens:
Please input command:
addperson Batman
>person added
blahblah
Error: incorrect syntax
For some reason it doesn't print: ">". Also everytime I enter anything after that it says the same thing always even with right commands.
But if I use this:
printf("Please input command:\n>");
while 1 {
if (fgets(input, Buffer_size, stdin) == NULL) {
break;
}
...
printf(">");
}
I get:
Please input command:
> add_person Batman
person added
> blahbagwa
Incorrect syntax
> add_person Superman
person added
Notice how ">" appears in each output? I don't really know why read isn't working properly; perhaps my understand of read isn't very good. Does anyone have any idea?
read() will block until it has received enough input to fill the entire buffer where as fgets() will return a buffer for each entered line.
I'm writing my own shell in C and I need to detect EOF (for when I run ./myshell < commands.txt)
commands.txt contains:
ls
pwd
These both run fine separately from within the program. But when I run it with the text file, I get an infinite loop.
In my while(1) loop for the shell, the first thing I do is this:
if (feof(stdin)) { my_exit(); }
my_exit is simply:
void my_exit() {
printf("End of file! Bye\n");
exit(0);
}
Doesn't exit(0) end the program (and the loop)? Why am I getting
End of File! ByeEnd of File! ByeEnd of File! ByeEnd of File! ByeEnd of File! ByeEnd of File! Bye.... etc
I have also tried doing the fgets == NULL way. Same loop
The problem is that feof() tells you if the LAST input operation ran into EOF. But you're not checking this until the next iteration. So when you're at EOF, you do fgets() and then try to use the empty result that it returned.
What's happening is that you fork() a child process, and then call execvp() with an empty command name. This is failing, so the child process returns to the beginning of the loop, and does the same thing. Meanwhile, the parent process calls my_exit(). So each child process forks another child of its own, and then exits.
The way feof works is that it returns false as long as no read has hit EOF. feof by itself does not check the stream, but checks if EOF indicator has been set, which happens when something like fgets fails.
It really is not a very good practice to use feof in the control loop.
An example of validating EOF can be this:
while( fgets(line, sizeof(line), fp) != NULL )
fputs(line, stdout);
You can try like this...
int a=1;
while(a)
{
if (feof(stdin)) {
a = 0;
}
}
printf("End of file! Bye\n");
exit(0);