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);
}
Related
Okay so overall im trying to complete a basic CLI C program which will complete functions such as clear, quit, cd, ls, help (bring up the unix man) etc.. i altered my code and so far i have this, im getting segmination error when trying to execute the cd command part of the program, (im very new to c btw);
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
int main (int argc, char *argv[])
{
char input[] = " ";
char *argument;
while(strcmp(input, "quit")!= 0)
{
printf("$");
scanf ("%s", input);
if(strcmp(input,"clear") == 0)
{
printf("\e[1;1H\e[2J");
}
else if(strcmp(argv[1],"cd") == 0)
{
if(chdir(argv[2]) == -1)
{
printf("\n directory does not exists");
}
}
else if(strcmp(input, "echo") == 0)
{
char str[50];
scanf("%[^\n]+", str);
printf(" %s", str);
}
}
}
input is declared as a ' ' (space) character. It will never match 'cd'.
This is probably more along the lines of what you want to achieve, where the first parameter is the command (cd), and the second will be the directory:
int main (int argc, char *argv[])
{
char *argument;
if(strcmp(argv[1],"cd") == 0)
{
if(chdir(argv[2]) == -1)
{
printf("\n directory does not exists");
}
}
Edit Also please note that there is no need for the else satement. If chdir does not return an error, it will change the directory, thus no need to call it again in an else.
Additionally, another tip for using system calls in general, it would be of great help if you print the error number returned by the system upon a failure in system call. This will make things easier when things start going wrong. To do this simply include <errno.h>' and modify the printf to printerrno` which gives specific details about the error:
printf("Chdir error: %d", errno);
For instance chdir() does not only return an error when the directory does not exist, but also for example if you do not have permissions to view the contents of the directory. See the man page for a list of possible errors.
To implement your own shell, you need to take input directly from stdin, not from command-line arguments (argv) from another shell. The basic pattern is like this:
Read input
Execute command
Print results
Loop back to step 1
I am working on a project in C. It is a primitive file system shell and one of commands can either take in a string after it or not. Here is how I am reading in commands from stdin. (This way is specified by my instructor):
fsys* f = newFileSystem();
char cmd[21];
char path[1001];
while(scanf("%s", cmd) > 0){
if(strcmp(cmd, "ls") == 0){
// this is probably the wrong way to go about it but...
if(fgets(path, 1001, stdin)){ // 1001 is max path length
strtok(path, "\n"); // doesn't remove if path is only \n
// fgets reads in \n character and it affects output
// later, so getting rid of it now
strtok(path, " "); // doesn't remove space between ls and path name
fs_ls(f, path);
}
}
else if(strcmp(cmd, "pwd") == 0){
// prints path to cwd beginning at root
fs_pwd(f);
// some other commands in here
else{
printf("Command not recognized.");
}
}
So ls can either take in no path (defaults to current working directory) or can take in a path and list files and directories at that location.
What can I write within the if statement "if(strcmp(cmd, "ls") == 0)" for the system to check if there is a path after ls or not and execute ls correctly?
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 );
}
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().
I am to program a simple shell in C for my project that can implement environment variables. I looked up on how to use the getenv, setenv, putenv. So far so good i've tried to use the getenv to show the shell variables...well... with some succes . But I have a feeling that my reasoning is flawed. I have a char** argv which contains parsed input from the user input. I now check if argv starts with the command "echo" and then if any of the following inputs starts with a $ sign or not. Here's my code:
int executeVariables(char** arguments){
int i = 0;
if(strcmp(arguments[i], "echo") == 0){
char *variable;
for(i = 1; arguments[i] != NULL; i++){
char *str = arguments[i];
if( *(str + 0) == '$'){
variable = getenv(str + 1);
}else{
variable = getenv(str);
}
if(!variable){
//puts("not a variable");
printf("%s ", arguments[i]);
}else{
//puts("a variable");
printf("%s ", variable);
}
}
printf("\n");
exit(0);
}
return 1;
}
I think that normal linux shell finds the $ sign, it expands the variable before invoking the echo command. My shell isn't following this principle, it's expanding variables inside the echo command itself. Any idea as to how I can implement this? Thanks.
EDIT:
A problem I have is: echo $HOME and echo HOME gives me the same result which is wrong.
EDIT:
After various tests everything works well. But to really test it i'll need to create a local variable then echo this value. I tried it using putenv function but it doesn't create the local variable.
i = 0;
char** temp = malloc(sizeof (*temp));
if(strstr(userInput, "=") != NULL){
//puts("we got equals");
puts(userInput);
if(putenv(userInput) == 0){
printf("doing putenv(%s)\n", userInput);
exit(0);
}
else{
puts("couldnt putenv");
exit(1);
}
}
userInput: char *userInput is the input gotten from the command line using fgets()
You're specifically asking the code to do getenv() for the string, even if $ isn't found. That's why it will lookup $HOME or HOME. Just remove the else case for not finding the dollar-sign, and make sure to initialize variable to NULL at its declaration, and put it inside the loop.
Something like so:
// First, perform replacements
int executeVariables(char** arguments){
int i = 0;
for(i = 0; arguments[i] != NULL; i++){
char *str = arguments[i];
if(*str == '$'){
// make sure the result isn't NULL, though I'm not sure a real shell does
char *tmp = getenv(str + 1);
if (tmp != NULL) {
arguments[i] = getenv(str + 1); // save off the argument
}
}
}
// Then actually execute the function. This would be like bash's echo builtin
if (strcmp(arguments[0], "echo") == 0) {
int i;
for (i = 1; arguments[i] != NULL; i++) {
printf("%s ", arguments[i]);
}
printf("\n");
}
// Other functions could go here
return 1;
}
Edit: As far as your methodology goes, why do you specifically check for echo? Why not make it generic, and check all the arguments, including the first one? You actually probably want to substitute all the potential environment variables, so if you had MYECHO=echo somewhere, the following would work. To make it more generic, you'd have this function, which would then execute the stuff based on the expanded variables. You could make it all nice and have separate functions, but to fit it all in here, I've updated the code above accordingly, though I haven't tested it. ;)
Edit: That being said, werewindle's comment about doing this earlier does apply -- replace the arguments, like here, and then use that updated arguments array to have a separate function do whatever it needs to do. :)
> $MYVAR totally awesome $HOME
totally awesome /home/user
Edit: As for the putenv() situation, you'll want something structured like the following. This way, it will set it for the shell, and any other processes you run in the shell.
void do_args_env(char *args[])
{
// do putenv, etc.
}
// inside main loop in shell process
while (1) { // just an example
if (check_args_syntax(args) != 0) {
// error
}
do_args_env(args);
// fork and do other stuff
}
(Hopefully final) Edit: As an explanation, processes generally don't (perhaps can't?) affect the environment of processes above them in their hierarchy; only the other way around. So if you putenv() in a child, that child's siblings (i.e. other processes forked from its parent) won't get the environment change.
Glad I could be of help!
Yes, normal shells expand variables during parsing command line. So you need substitute variables in function, that produces array that "contains parsed input from the user input". In this case code for echo (and other commands) will be much shorter.