[SOLVED] Writing a C shell, how should I execute the command?
ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
Distribution: Debian, Kali Linux, Linux Mint, BackTrack, elementary OS
Posts: 26
Rep:
Writing a C shell, how should I execute the command?
I have written a basic C shell. It prints out a [user@hostname] ~ $ prompt by using getenv(), then reads the input and executes it using system(lineCopy). However, as my friend has pointed out, this is not actually a shell. This is just executing a command through /bin/sh.
So how should I execute a command? I have heard of forking the process ID then calling exec(), but how is that done?
pid_t pid;
int err;
pid=fork();
if (pid == -1)
err=errno;
else if (pid == 0) {
execv(arg0, rest_of_args);
err = errno; // should never get here
}
else
waitpid(pid, &err, 0);
Distribution: Debian, Kali Linux, Linux Mint, BackTrack, elementary OS
Posts: 26
Original Poster
Rep:
Quote:
Originally Posted by millgates
Can you post your code (or, at least, the relevant part of it)?
Yes I can. My code is as follows:
Code:
/*
Credit to Stephen Brennan (http://stephen-brennan.com/2015/01/16/write-a-shell-in-c/) for the split_line function.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#define LINESIZE 1024
#define RL_BUFSIZE 1024
#define TOK_BUFSIZE 64
#define TOK_DELIM " \t\r\n\a"
char **split_line(char *line)
{
int bufsize = TOK_BUFSIZE, position = 0;
char **tokens = malloc(bufsize * sizeof(char*));
char *token, **tokens_backup;
if (!tokens) {
fprintf(stderr, "vsh: allocation error\n");
exit(EXIT_FAILURE);
}
token = strtok(line, TOK_DELIM);
while (token != NULL) {
tokens[position] = token;
position++;
if (position >= bufsize) {
bufsize += TOK_BUFSIZE;
tokens_backup = tokens;
tokens = realloc(tokens, bufsize * sizeof(char*));
if (!tokens) {
free(tokens_backup);
fprintf(stderr, "vsh: allocation error\n");
exit(EXIT_FAILURE);
}
}
token = strtok(NULL, TOK_DELIM);
}
tokens[position] = NULL;
return tokens;
}
/*
int execute(char *command)
{
system(command);
return 1;
}
*/
int main(int argc, char **argv)
{
char *cmd;
int finish = 0;
char *user = getenv("USER");
char hostname[1024];
char *pwd = getenv("PWD");
char line[LINESIZE] = {0};
char lineCopy[LINESIZE] = {0};
char *command = NULL;
char **args;
// Print out a welcome message
printf("Welcome to vsh\n");
// Print out the message of the day
system("/bin/cat /etc/motd");
printf("\n");
gethostname(hostname, 1024);
while (!finish)
{
// Update 'pwd' variable
pwd = getenv("PWD");
// Print out prompt
printf("[");
printf(user);
printf("@");
printf(hostname);
printf("] ");
printf(pwd);
printf(" $ ");
fflush(stdout);
if(NULL == fgets(line, sizeof line, stdin))
{
// If NULL, leave vsh
finish = 1;
printf("\nLeaving vsh\n");
}
else
{
// If there is something there...
printf("The command read was s", line);
printf("\n");
char *newLine = strchr(line, '\n');
if(newLine != NULL)
{
*newLine = '\0';
strcpy(lineCopy, line);
}
command = strchr(line, ' ');
if(command != NULL)
{
*command++ = '\0';
printf("Command= _%s_\n\n", line);
}
// Is the command pre-programmed into vsh?
if(strcmp(line, "cd") == 0)
{
printf("Sorry, CD does not work in this version of vsh Shell. :(\n");
}
else if(strcmp(line,"programmer") == 0)
{
printf("The programmer of vsh is George Gibson\n");
}
else if(strcmp(line,"ver") == 0)
{
printf("The current version is 1.0\n");
}
else if(strcmp(line, "help") == 0)
{
printf("Help for vsh\n\nType a command to run it. The builtin commands are : cd, programmer, ver, help and exit.Their functions are as follows:\n\ncd: Change Directory. Changes the current working directory.\n\nprogrammer: Display the programmers of vsh.\n\nver: Display the version of vsh you are running.\n\nhelp: Launches this program.\n\nexit: Leave vsh Shell.\n\nThese are the only builtins in vsh, but note that if you define a command in /bin with one of these names, your program will not run in vsh unless you use it's explicit name (/bin/yourprogram) rather than \"yourprogram\". vsh 1.0 written and programmed by George Gibson.");
}
else if(strcmp(line,"exit") == 0)
{
finish = 1;
printf("\nLeaving vsh\n");
}
else if(strcmp(line, "scroll") == 0)
{
// Easter egg
printf(" _\n(@)\\__________ _\n|-| \\(@)\n| | VSH! |-|\n|_| | |\n(@)\\__________ |_|\n ~ \\(@)\n ~\n\nWell done for finding the hidden easter egg in vsh!\n\n");
}
// If command is not a vsh built-in, run the command
else
{
args = split_line(line);
pid_t pid;
int err;
pid=fork();
if(pid == -1)
perror("vsh");
else if(pid == 0) {
execv(args[0], args);
perror("vsh"); // Should never get here
}
else {
waitpid(pid, &err, 0);
}
}
}
}
return 0;
}
Sorry about the indentation. Don't know why the clipboard did this (in my editor it was all perfect four-space indents).
Last edited by Kslkgh; 07-28-2015 at 09:50 AM.
Reason: Updated Code
What's the point of calling 'read_line' after 'fgets'? The total input can be found in variable 'line'(okay, you have overwritten the first space with '\0', but that can be undone)
Distribution: Debian, Kali Linux, Linux Mint, BackTrack, elementary OS
Posts: 26
Original Poster
Rep:
Sorry, that was a mistake. Removed that and now it will work, but only if I use an explicit path (/bin/some_command), not how you would usually execute it (some_command). If you try and just do some_command, it will give the error vsh: No such file or directory. Should I just append /bin/ to the start of the command, or is there a better solution?
Sorry, that was a mistake. Removed that and now it will work, but only if I use an explicit path (/bin/some_command), not how you would usually execute it (some_command). If you try and just do some_command, it will give the error vsh: No such file or directory. Should I just append /bin/ to the start of the command, or is there a better solution?
you get PATH from the environment
then you go through paths from it and try them in order
Distribution: Debian, Kali Linux, Linux Mint, BackTrack, elementary OS
Posts: 26
Original Poster
Rep:
I've managed to work around this by using exevp(args[0], args) in order to execute rather than execv(args[0], args). Thanks for all of your help guys, the code can be found at GitHub if you want it. Any optimisations or improvements let me know on this thread.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.