Member Avatar for 1bh

Hi guys,
So my code is below. This is a simplified version of the shell... It does not continue for ever... I guess its actually rather a program to accept a command.. has the fork and the execve etc. Anyways when i compile it and run it in the terminal for some reason only '/bin/echo "enter text here"' works...
But when i try to use a command like /bin/ls "directory" it wont work.. but when i call /bin/ls "directory" in the terminal (not in my half-shell) it works perfectly fine.. So i dont know if this could be a problem with my code.
Thanks
Guys

#include <string.h>
#include <errno.h>
#include <stdio.h>

#define TRUE 1


int main()
{
int n = 0;
int status;
char *argv[100];
char temp[256];

fgets(temp, sizeof(temp), stdin);
argv[n++] = strtok (temp," ");

while (argv[n-1] != NULL)
argv[n++] = strtok (NULL, " ");

// This is merely to print out what got splitted for the tokens////////////////////////////
int b=0;
while (argv[b] != NULL)
{
printf ("%s\n", argv[b]);

b++;
}
//////////////////////////////////////////////////////////////////////////////

if (fork() != 0)
	{
	waitpid(-1, &status, 0);
	}
else
	{
		if (execve(argv[0], argv, 0) == -1) /* execute command */
		printf(": %s\n", strerror(errno));
	}
	

}

Recommended Answers

All 6 Replies

>>char *argv[100];

You need to initialize all those pointers to 0, like this: char *argv[100] = {0}; Why are you typing in /bin/ls? Doesn't it work with just "ls", like you would type it in the shell?

[edit]I tried it just now and it doesn't work either. Not sure what the problem is.

Ancient Dragon: The reason why 'ls' alone won't work is because with a simplified shell such as this one (or the one which I'd written for the same sort of project), it doesn't support a default executable path, so it needs a fully qualified path for any program it runs (the path is part of the argument passed to execve()). Since ls is a separate program, rather than a part of the shell, it has to have the full path, or else the OP would need to add support for a default path (reasonably simple, but brittle, as there are several paths needed to find the commonly used utilities) or a path environment variable which can be set when the shell starts (fairly involved for what is usually a one-week project in a sophomore-to-junior level Systems Programming course).

As for the original problem, I've tested the code and found a number of issues with it. First off, you did not #include either <unistd.h> or <sys/wait.h>, which contain the headers for some of the functions you are using. Also, the code as written requires that you compile with the -std=c99 option - not a problem in itsle,f but something that could cause it to fail if someone else tried to compile it without noticing it. I would finally add that you may want to make sure that you're compiling with -Wall set, just to make sure that you catch any minor problems in the syntax.

I also found that it doesn't not, in fact, run /bin/echo; it only appears to, because you are echoing the command line options on separate lines. If you change the test printf()s to the following it should be clearer:

// This is merely to print out what got splitted for the tokens////////////////////////////
    
    for (int b=0; argv[b] != NULL; b++)
    {
        printf ("%s\t", argv[b]); 
    }
    printf("\n");

I would also recommend changing the test on fork() so that it checks for an error condition:

int child = fork();
    if (child > 0)
    {
        waitpid(-1, &status, 0);
    }
    else if (child == 0)
    {
        if (execve(argv[0], argv, 0) == -1) /* execute command */
            printf(": %s\n", strerror(errno));
    }
    else
    {
        printf(": %s\n", strerror(errno));
    }

You'll note that this version does run /bin/echo properly, if given a suitable argument (it fails if no args are given). It still doesn't run /bin/ls, however, giving the following error messages:

$ ./coosh
/bin/ls /
/bin/ls    /
    
/bin/ls: cannot access /
: No such file or directory

Finally, you would probably want to add some sort of prompt to the beginning of the code; I personally am partial to ']' for historical reasons ;)

fgets() (line 15 of the original post) reads not only the command line, but a trailing newline as well. The newline is never stripped, and after tokenization it is still attached to the last argument. See an extra empty line (line 3):

/bin/ls /
/bin/ls    /
    
/bin/ls: cannot access /
: No such file or directory

That is, /bin/ls tries to access the "/\n" directory, which of course doesn't exist.

/bin/echo obviously doesn't care.

commented: good catch :) +33

Ah, you're right, Nezachem. Fortunately, I found my own old shell program. and was reminded that there is a simple solution:

char whitespace[] = "\n\t ";

    // ...
    argv[n++] = strtok (temp, whitespace);

This causes tabs and newlines to be removed as well as spaces.

Speaking of my own experiences, again: you may find it helpful to factor the code that parses the command line and the code that executes it into separate functions. This makes testing easier, as there are fewer places where they can interact with each other.

Hi I modified the above program some what ...
I tested the program for /bin/ls and /bin/pwd and the program worked. The program makes some assumptions. I will leave them for you to clear out :)

int
main()
{
        char temp[256];
        char *argv[100];
        char *p;
        int i=0;
        int child;


        while(1)
        {
                printf("\n\n$ ");
                fgets(temp,256,stdin);
                {
                        p = strchr(temp,'\n');
                        temp[p-temp] = '\0';

                        argv[i] = strtok(temp," ");

                        while(argv[i] != NULL)
                        {
                                printf("%s\n",argv[i]);
                                i++;
                                argv[i] = strtok(NULL," ");
                        }


                        printf("Before fork\n");
                        child = fork();

                        if(child < 0)
                        {
                                printf("Error\n");
                                exit(0);
                        }

                        if(child == 0)
                        {
                                printf("In child\n");
                                if(execve(argv[0],argv,0) == -1)
                                        printf("Execve error\n");
                        }

                        if(child > 0)
                        {

                        }

                }
        }
        return 0;
}
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.