-
-
Save phase/1a814e0ae9b64e1800e451bc40080b10 to your computer and use it in GitHub Desktop.
Simple shell in C
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Compile with: g++ -Wall –Werror -o shell shell.c */ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <ctype.h> | |
#include <unistd.h> | |
#include <sys/types.h> | |
#include <sys/wait.h> | |
/* The array below will hold the arguments: args[0] is the command. */ | |
static char* args[512]; | |
pid_t pid; | |
int command_pipe[2]; | |
#define READ 0 | |
#define WRITE 1 | |
/* | |
* Handle commands separatly | |
* input: return value from previous command (useful for pipe file descriptor) | |
* first: 1 if first command in pipe-sequence (no input from previous pipe) | |
* last: 1 if last command in pipe-sequence (no input from previous pipe) | |
* | |
* EXAMPLE: If you type "ls | grep shell | wc" in your shell: | |
* fd1 = command(0, 1, 0), with args[0] = "ls" | |
* fd2 = command(fd1, 0, 0), with args[0] = "grep" and args[1] = "shell" | |
* fd3 = command(fd2, 0, 1), with args[0] = "wc" | |
* | |
* So if 'command' returns a file descriptor, the next 'command' has this | |
* descriptor as its 'input'. | |
*/ | |
static int command(int input, int first, int last) | |
{ | |
int pipettes[2]; | |
/* Invoke pipe */ | |
pipe( pipettes ); | |
pid = fork(); | |
/* | |
SCHEME: | |
STDIN --> O --> O --> O --> STDOUT | |
*/ | |
if (pid == 0) { | |
if (first == 1 && last == 0 && input == 0) { | |
// First command | |
dup2( pipettes[WRITE], STDOUT_FILENO ); | |
} else if (first == 0 && last == 0 && input != 0) { | |
// Middle command | |
dup2(input, STDIN_FILENO); | |
dup2(pipettes[WRITE], STDOUT_FILENO); | |
} else { | |
// Last command | |
dup2( input, STDIN_FILENO ); | |
} | |
if (execvp( args[0], args) == -1) | |
_exit(EXIT_FAILURE); // If child fails | |
} | |
if (input != 0) | |
close(input); | |
// Nothing more needs to be written | |
close(pipettes[WRITE]); | |
// If it's the last command, nothing more needs to be read | |
if (last == 1) | |
close(pipettes[READ]); | |
return pipettes[READ]; | |
} | |
/* Final cleanup, 'wait' for processes to terminate. | |
* n : Number of times 'command' was invoked. | |
*/ | |
static void cleanup(int n) | |
{ | |
int i; | |
for (i = 0; i < n; ++i) | |
wait(NULL); | |
} | |
static int run(char* cmd, int input, int first, int last); | |
static char line[1024]; | |
static int n = 0; /* number of calls to 'command' */ | |
int main() | |
{ | |
printf("SIMPLE SHELL: Type 'exit' or send EOF to exit.\n"); | |
while (1) { | |
/* Print the command prompt */ | |
printf("$> "); | |
fflush(NULL); | |
/* Read a command line */ | |
if (!fgets(line, 1024, stdin)) | |
return 0; | |
int input = 0; | |
int first = 1; | |
char* cmd = line; | |
char* next = strchr(cmd, '|'); /* Find first '|' */ | |
while (next != NULL) { | |
/* 'next' points to '|' */ | |
*next = '\0'; | |
input = run(cmd, input, first, 0); | |
cmd = next + 1; | |
next = strchr(cmd, '|'); /* Find next '|' */ | |
first = 0; | |
} | |
input = run(cmd, input, first, 1); | |
cleanup(n); | |
n = 0; | |
} | |
return 0; | |
} | |
static void split(char* cmd); | |
static int run(char* cmd, int input, int first, int last) | |
{ | |
split(cmd); | |
if (args[0] != NULL) { | |
if (strcmp(args[0], "exit") == 0) | |
exit(0); | |
n += 1; | |
return command(input, first, last); | |
} | |
return 0; | |
} | |
static char* skipwhite(char* s) | |
{ | |
while (isspace(*s)) ++s; | |
return s; | |
} | |
static void split(char* cmd) | |
{ | |
cmd = skipwhite(cmd); | |
char* next = strchr(cmd, ' '); | |
int i = 0; | |
while(next != NULL) { | |
next[0] = '\0'; | |
args[i] = cmd; | |
++i; | |
cmd = skipwhite(next + 1); | |
next = strchr(cmd, ' '); | |
} | |
if (cmd[0] != '\0') { | |
args[i] = cmd; | |
next = strchr(cmd, '\n'); | |
next[0] = '\0'; | |
++i; | |
} | |
args[i] = NULL; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment