c++ shell

Last year, I wrote a shell for unix using C++, so I thought I'd post the source code incase anyone can find it useful. The shell is fully featured... it can run all unix/linux commands, and it supports piping and redirection.

You can download a copy of the source here.
// shell.c
// Taco Shell v1.0 (tsh)
// By Brad Pineau

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>

#define PROMPT "Taco Shell"
#define FALSE 0
#define TRUE 1
#define NORMALMODE 0
#define CONCURRENTMODE 1
#define STDIN 0
#define STDOUT 1
#define DEFAULT 2

// structure to hold the command name and arguments
typedef struct command_t {
char *name;
int argc;
char **argv;
} CommandType;

// declaration of functions used in program
char* getCommandPath(char* command);
void trim(char* name);
int runCommand(CommandType* command);
int runConcurrentCommand(CommandType* command);
int runPipedCommands(CommandType* command, CommandType* command2);
int runRedirectedCommand(CommandType* command, char* file, int fileid);


char *path;
char *ptr;

char *input;
int pid;
int status;

int redirectionType;

// returns the full path of a command
// returns NULL if the command is not found on the path
char* getCommandPath(char* command) {

FILE *fpin;
char p[1024];
char *temppath;
char *temp = (char *)calloc(1024, sizeof(char));

path = (char *) getenv("PATH");

strcpy ( p, path );
path = strtok ( p, ":" ); // Find the first PATH
while ( path != NULL ) {
strcpy ( temp, path ); // Save a copy of substring
strcat ( temp, "/" ); // Form a path and file name
strcat ( temp, command );
if ( ( fpin = fopen ( temp, "r" ) ) == NULL ) {
temp[0] = '0'; // Start over again
path = strtok ( ( char * ) 0, ":" ); // Try next path
} else {
break; // Must have a good fpin
}
}
if ( fpin == NULL ) {
return NULL;
} else {
return temp;
}

}


// this function was found on groups.google.com
// there was no author's name with the code...
void trim(char* name) {
char* p = name ;
size_t n ;
while (isspace(*p))
++p ;
if ((n = strlen(p)) > 0)
while (isspace(p[n-1]))
--n ;
((char*)memmove(name, p, n))[n] = '0' ;
}

// the main program...
int main() {

CommandType *command = (CommandType*) malloc(sizeof(CommandType));
CommandType *command2 = (CommandType*) malloc(sizeof(CommandType));
int count;
int runmode;
int piped;
char* redirectionFilename;
char* buffer = (char*)calloc(1024, sizeof(char));

while(TRUE){
piped = FALSE;
redirectionType = DEFAULT;
input = (char *)calloc(1024, sizeof(char));
redirectionFilename = (char *)calloc(1024, sizeof(char));

// prinit the prompt
printf("%s - (%s)> ", PROMPT, getcwd(buffer, 1024));

gets(input);
trim(input);
// start over if a blank line happens
if ((strcmp(input, "") != 0) && (strcmp(input, ".") != 0) && (strcmp(input, "..") != 0)) {
// checks of exit or quit, and terminates program if seen
if ((strcmp(input, "quit")== 0) || (strcmp(input, "exit")== 0)) {
printf("Terminating Shell...n");
return 0;
}

// initialize argv array
command->argv = (char**)calloc(256, sizeof(char*));
command2->argv = (char**)calloc(256, sizeof(char*));

// parses the command line
ptr = strtok(input, " ");
command->name = ptr;
command->argv[0] = ptr;
command->argc = 1;
count = 1;
runmode = NORMALMODE;
ptr = strtok (NULL, " ");

while (ptr != NULL) {
//command->argv[count++] = ptr;
if (strcmp(ptr, "|")==0) {
// pipe found
if (piped) {
printf("Error: Second level piping not supported.n");
} else {
// set up parser to read into the 2nd command
piped = TRUE;
ptr = strtok (NULL, " ");
if (ptr != NULL) {
command2->name = ptr;
command2->argv[0] = ptr;
command2->argc = 1;
count = 1;
} else {
printf("Error: No command to pipe.n");
}
}
} else if (strcmp(ptr, ">")==0) {
ptr = strtok (NULL, " ");
if (ptr != NULL) {
redirectionFilename = ptr;
redirectionType = STDOUT;
ptr = NULL; // used to break while loop
} else {
printf("Error: No file specified for redirection.n");
}

} else if (strcmp(ptr, "<")==0) {
ptr = strtok (NULL, " ");
if (ptr != NULL) {
redirectionFilename = ptr;
redirectionType = STDIN;
ptr = NULL; // used to break while loop
} else {
printf("Error: No file specified for redirection.n");
}

} else {
if (piped) {
command2->argv[count++] = ptr;
command2->argc++;
} else {
command->argv[count++] = ptr;
command->argc++;
}
}
ptr = strtok (NULL, " ");
}

if (piped) {
runPipedCommands(command, command2);
} else if (redirectionType != DEFAULT) {
runRedirectedCommand(command, redirectionFilename, redirectionType);
} else {


if (strcmp(command->argv[command->argc-1],"&")==0) {
// & found, remove it from argv list
runmode = CONCURRENTMODE;
command->argv[command->argc-1] = NULL;
}

// check for built-in commands
if (strcmp(command->argv[0], "echo") == 0) {
// echo the commandline tokens
int i;
for (i = 1; i < command->argc; i++) {
printf("%s ", command->argv[i]);
}
printf("n");
} else if (strcmp(command->argv[0],"cd") == 0) {
// change directory
if (command->argc == 2) {
if (chdir(command->argv[1]) != 0) {
printf("Error: Invalid directory.n");
}
} else {
printf("Error: Invalid use of cd command.n");
}
} else if (strcmp(command->argv[0],"getenv") == 0) {
if (command->argc == 2) {
char* variable = (char *) getenv(command->argv[1]);
if (variable == NULL) {
printf("Error: Invalid environment variable.n");
} else {
printf("%sn", variable);
}
} else {
printf("Error: Invalid use of getenv command.n");
}

} else if (strcmp(command->argv[0],"setenv") == 0) {
if (command->argc == 3) {
char* value = (char*)calloc(1024,sizeof(char));
strcpy(value, command->argv[1]);
strcat(value, "=");
strcat(value, command->argv[2]);
putenv(value);
} else {
printf("Error: Invalid use of getenv command.n");
}


} else {
// run the command as regular command....
if (runmode == NORMALMODE) {
runCommand(command);
} else if (runmode == CONCURRENTMODE) {
runConcurrentCommand(command);
}
}
} // if (piped)
}
}
}

// runs a command normally
int runCommand(CommandType* command) {

char* filename = getCommandPath(command->name);
if (filename == NULL) {
printf("Command not found.n");
return 1;
}

// creates child process
pid = fork();
// checks for error
if (pid < 0) {
printf("Error in the fork process.n");
return 1;
} else if (pid == 0) {
// Child code
execv(filename, command->argv);
return 1;
} else {
// Parent waits
while (wait(&status) != pid);
}
}

// runs a command as a background process
int runConcurrentCommand(CommandType* command) {

char* filename = getCommandPath(command->name);
if (filename == NULL) {
printf("Command not found.n");
return 1;
}

// creates child process
pid = fork();
// checks for error
if (pid < 0) {
printf("Error in the fork process.n");
return 1;
} else if (pid == 0) {
// Child code
execv(filename, command->argv);
return 1;
} else {
// Parent doesn't wait
//while (wait(&status) != pid);
}
}

// pipes one command into another
int runPipedCommands(CommandType* command, CommandType* command2) {

char* filename = getCommandPath(command->name);
if (filename == NULL) {
printf("%s command not found.n", command->name);
return 1;
}

char* filename2 = getCommandPath(command2->name);
if (filename2 == NULL) {
printf("%s command not found.n", command2->name);
return 1;
}

int fd[2];
int pid, pid2;

if (pipe(fd) == -1) {
printf("Error: Pipe failed.n");
return 1;
}
if ((pid = fork()) < 0) {
printf("Error: Fork failed.n");
return 1;
}

if (pid == 0) {
// child process
close(fd[1]);
dup2(fd[0], 0);
close(fd[0]);
execv(filename2, command2->argv);
printf("Error: execv failed.n");
return 1;
} else {
// parent process
// need to fork again, so the shell isn't replaced
if ((pid2 = fork()) < 0) {
printf("Error: Fork failed.n");
return 1;
}
if (pid2 == 0) {
// child process
close(fd[0]);
dup2(fd[1], 1);
close(fd[1]);
execv(filename, command->argv);
printf("Error: execv failed.n");
return 1;
} else {
// parent process (the shell)
close(fd[0]);
close(fd[1]);
while (wait(&status) != pid2);
}
}
return 0;
}

// redirects STDIN or STDOUT for a command
int runRedirectedCommand(CommandType* command, char* file, int fileid) {

char* filename = getCommandPath(command->name);

if (filename == NULL) {
printf("%s command not found.n", command->name);
return 1;
}

int pid;
if ((pid = fork()) < 0) {
printf("Error: Fork failed.n");
return 1;
}
if (pid == 0) {
// child process

int fid;
if (fileid == STDIN) {
fid = open(file, O_RDONLY, 0600);
} else if (fileid == STDOUT) {
fid = open(file, O_CREAT | O_TRUNC | O_WRONLY, 0600);
}
dup2(fid, fileid);
close(fid);

execv(filename, command->argv);
printf("Error: execv failed.n");
return 1;
} else {
// parent process (shell)
while (wait(&status) != pid);
}
}

You can compile the source with the following line:
gcc -o shell shell.c

 

34 Replies

Posted by Khalid on February 5th, 2004 at 11:44 pm AST

Hey Thanks! Your code helped me understand where I was lacking. Thank you for sharing online.

Posted by Sindhi Prem D on February 13th, 2004 at 4:51 am AST [email]

how to make this shell login shell ?

Posted by johan on March 16th, 2004 at 9:46 pm AST [email] [website]

hello!

Posted by Srilakshmi on March 18th, 2004 at 8:09 pm AST

Hello...This is really useful. Thanks For making it online.

Posted by grOLo on March 30th, 2004 at 7:46 pm AST

Edit "/etc/shells" and insert the path of new shell, for example: /bin/newshell

To make this shell login shell for any user: adduser -s PathToShell User (new or older user)
For example to make "/bin/newshell" default shell for user "jane"

adduser -s /bin/newshell jane

-------------
>Posted by Sindhi Prem D on February 13th, 2004 at 4:51 am AST [email]
>how to make this shell login shell ?
-------------

Posted by Adam on April 20th, 2004 at 1:49 pm AST [email]

Thanks for posting this. I have to write a shell program for my class and I'm not quite understanding a lot but your commenting helped me understand piping a bit more. I have one question though if you could answer it:
if ((pid = fork()) < 0) {
printf("Error: Fork failed.n");
return 1;
}

if (pid == 0) {
// child process
close(fd[1]);
dup2(fd[0], 0);
close(fd[0]);
execv(filename2, command2->argv);
printf("Error: execv failed.n");
return 1;
} else {
// parent process
// need to fork again, so the shell isn't replaced
if ((pid2 = fork()) < 0) {
printf("Error: Fork failed.n");
return 1;
}
if (pid2 == 0) {
// child process
close(fd[0]);
dup2(fd[1], 1);
close(fd[1]);
execv(filename, command->argv);
printf("Error: execv failed.n");
return 1;
} else {
// parent process (the shell)
close(fd[0]);
close(fd[1]);
while (wait(&status) != pid2);
}

what if pid > 0 and pid2 > 0? how does the program continue on with execution so that it will go into the code sections for the child processes? i am assuming that it will just wait until the child process ends and it forks again. if i am correct, is it possible that it will continue to fork and repeatedly return a value > 0 thus putting it into an infinite loop?

Posted by Melese Zenawi and Issayas Aforki on April 22nd, 2004 at 5:49 am AST [email]

Thanks for making it available on line. It helps to get some idea

Posted by Leon Mergen on April 22nd, 2004 at 9:43 am AST [email] [website]

Hmmmm, with all due respect, that isn't really C++ ... I mean, wasn't the intention of C++ to be object oriented ? :)

Posted by Leon Mergen on April 22nd, 2004 at 9:44 am AST [email] [website]

Hmmmm, with all due respect, that isn't really C++ ... I mean, wasn't the intention of C++ to be object oriented ? :)

Posted by Bhrama on May 14th, 2004 at 4:15 am AST

Hello Brad,

thanx a lot,i am an engineering student from india & m writin a unix shell .code that you have poste has been of great help to me .Actually i needed a help from you as im not able to impliment the history for my shell i ment the "doskey function in dos" could u help me.

Bhrama

Posted by Rakesh and Chandra Shekar on June 3rd, 2004 at 10:25 am AST [email]

Hello Brad,
Thanks for putting the source code online.It helped us to develope our own shell for our project.Thanks a lot.
Rakesh.
Chandra Shekar

Posted by Chandra Shekar on June 3rd, 2004 at 10:29 am AST [email]

Hi Brad,
We were able to find out that cd command was not working properly.And even the history command is not working.So kindly give suggestions on how to go ahead with our project A.S.A.P.
Thanks.

Posted by zubair on June 16th, 2004 at 5:10 pm AST [email] [website]

i want to get more information that how can i write my own shell

Posted by wolfdeng on June 19th, 2004 at 2:35 pm AST [email]

Hello Brad
Thanks for your source code ,I'm not quite understanding a lot but your commenting helped me understand the code but I have one question though if you could answer it:
((char*)memmove(name, p, n))[n] = '0' ;
What does it mean....?

Posted by Hovo on June 27th, 2004 at 5:47 am AST [email]

Thank You ver much Brad Pineau !!!

This code is really helped me.

Posted by Brad Pineau on June 27th, 2004 at 3:04 pm AST [website]

In reference to the line...
((char*)memmove(name, p, n))[n] = '0' ;


memmove means "move memory block".
The name variable is the target buffer, p is the source buffer, and n is the number of bytes to copy.

This line is found in the trim function above, so that might help you see what it's trying to do. Since you want to get rid of the spaces on either side of the name variable, the memmove(name, p, n) part will get rid of the left spaces, and the ....))[n] = '0' part gets rid of the right spaces.

Posted by Sreenivasulu Y on June 30th, 2004 at 6:24 am AST [email]

Hi Brad,

I want to know how Automatic Name completion works in linux shell?
I mean the code that processes TAB key and brings out a list of
possible options for command names, file names, directory names etc?

Your code looks like processing the arguments given to a program
once the enter key is hit after all arguments are given.

Can you please provide pointers to me?

Regards,
Sreenivasulu Y

Posted by Gundala Srini on September 2nd, 2004 at 6:57 am AST [email]

Hi Brad,

Thanks for posting this. I have to write a shell program for assignment I'm not quite understanding but it helped me in Understanding the basics and gave very good start.

Posted by sheethal on September 21st, 2004 at 9:35 am AST [email]

if u could reduce the num of commands it immplements?

Posted by vivek on September 28th, 2004 at 5:12 am AST [email]

You Rock!!!!!!!!!!!!!

Posted by pradeep on September 28th, 2004 at 7:30 am AST [email]

how to initiate new shell implemention?
how to implement "login"&"password" to be enabled on my shell?

Posted by cathy bell on November 22nd, 2004 at 6:59 am AST [email]

hey whr cud i get the same source code written in C?

Posted by dan the man on November 29th, 2004 at 3:13 pm AST [email]

Can anyone help me with a source code for just a basic unix shell in c++ that reads system calls and executes them?? Please email it to me or post it up, its just for a basic Operating Systems class, and my teacher can't teach. Therefore i don't understand when he gives us hints. Thank you

Dan The Man

Posted by mamtha on December 5th, 2004 at 5:49 am AST [email]

hi,
can anyone plz send me SOURCE CODE FOR LINUX SHELL IN 'C'.
my email-id is mamthasays@yahoo.com
pls send it as soon as possible
thankin u
mamtha

Posted by suma on December 5th, 2004 at 8:02 am AST [email]

thank you.u r code helped me a lot for my project.

Posted by bheemesh on December 7th, 2004 at 4:52 pm AST [email]

can any one please send me a project on implementing a linux shell to execute a set of commands(few 4 to 5 commands) preferably in c lang.

Posted by sowmya on December 8th, 2004 at 7:39 am AST [email]

this code is really useful.Can someone please send the source code for linux shell implemented for a set of few commands written in c. Commands can be one each from each category of shell/linux features.For complicated commands exec()family functions can be used.
please send the same as early as possible i.e within December 19th 2004


thank you.


my e-mail id is-----> somistars@yahoo.co.in

Posted by stephen on December 14th, 2004 at 11:10 am AST [email]

hey thanks it was of great help for my project thanks once again

Posted by Dharm Rajodiya on January 27th, 2005 at 8:52 am AST [email]

Requested Sir,
I am studing in Bechalor Engineering in Information Technology in India.
In this semister I had chooson my final year project topic is "Shell Implementation in LINUX". So,I am new about this linux and it's programming for that I need some one's help. I am requesting to help me by giving me some on line help,free tutorials,or pdf documents and free source codes.
We have a project duration for elevan weeks.We have also four sebject with this project so I ,Dharm Rajodiya, request you to give me guidence for the above project from stating to end.
Thanks Full to You
Dharm Rajodiya




Posted by JRS on March 2nd, 2005 at 12:58 am AST [email]

Code is pretty good. do you know if there is an easy way to pipe multiple commands together?

Thanks,
mentholpimp@hotmail.com

Posted by merche on March 13th, 2005 at 9:20 am AST [email]

hi! i磎 currently working on a uni project that involves a linux shell implementation and i磎 having trouble with the 磍s?command. could anyone please tell me where can i find the 磍s?implementation written in c??
thanks a lot

Posted by Nuno Mota on March 25th, 2005 at 4:35 pm AST [email]

First of all, nice code and nice gesture to show it to us.
Like JRS asked, does anyone knows how to use bidirectional pipes to be able to run multiple commands together? I could really use some help...

Thanks A LOT

Posted by ram on May 23rd, 2005 at 5:33 pm AST [email]

Hi there,

I have a question. After I run this code. How can I use redirection and pipe?? becoz when i typed
ls > rrr.out it said..command not found.


Thank you very mucj=h

Posted by Santo on September 6th, 2005 at 11:53 pm AST [email]

Thanx a lot, just what i was looking for
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值