Unix中一个可执行程序是一个机器指令及其数据序列。进程是程序运行时的内存空间和设置。
shell即“壳”,是一个解释器。 一般作用是:
(1)运行程序
(2)管理输入输出
(3)可编程
一个shell的主循环执行下面的4步:
(1)用户键入可执行文件名
(2)shell建立一个新的进程来运行这个程序
(3)shell将程序从磁盘载入
(4)程序在进程中运行直到结束
第一部分:为了要写一最简单的shell,需要学会
(1)运行一个程序
(2)建立一个进程 fock()
(3)等待exit()
父进程如何等待子进程退出呢? 调用wait(&status)。这个过程是这样的,一个进程创建一个子进程,自己相应的为父进程。父进程调用wait后,父进程挂起直到子进程结束,子进程结束会调用exit(n) (n是0~255) 。然后唤醒父进程同时wait得到返回状态。
wait()函数
wait(int *statuspir) 参数statuspir是子进程的运行结果。 返回值 -1遇到错误;pid 结束进程的id。假如参数statuspir不为NULL,wait将退出状态或者信号序列存储到参数所指的整数中。
第二部分:shell脚本
vi time.sh
编辑脚本
#! /bin/sh
#date server
echo urrnet date is
date
echo my name is
whoami
其中#! /bin/sh 是说此脚本用/bin/sh解释。#后面是注释,但#!是特殊符号,表示它后面的是解释此脚本的shell的路径。echo 是输出
执行此脚本用
sh time.sh
脚本除了命令外还包括:
变量,用户输入,控制,环境。
我们的代码实现解释命令,变量,if-then 控制,环境。
以下是代码
文件smsh4.c
/**
* smsh4.c small-shell version 4
* small shell that supportscommand line parsing
* and if ..then .. else .fi logic(by calling process() )
*
* build: gcc -o smsh4 smsh4.c splitline.c execute.c process.c\
* controflow.c builtin.c varlib.c
*/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
#include<sys/wait.h>
#include "smsh.h"
#define DFL_PROMPT "> "
void setup(void);
int main(void)
{
char *cmdline, *prompt, **arglist;
int result;
prompt = DFL_PROMPT;
setup();
while((cmdline = next_cmd(prompt, stdin)) != NULL)
{
if((arglist = splitline(cmdline)) != NULL)
{
result = process(arglist);
freelist(arglist);
}
free(cmdline);
}
return 0;
}
/**
* purpose: initialize shell
* returns: nothing. calls fatal() if trouble
*/
void setup(void)
{
extern char **environ;
VLenviron2table(environ);
signal(SIGINT, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
}
void fatal(char *s1, char *s2, int n)
{
fprintf(stderr, "Error: %s, %s\n", s1, s2);
exit(n);
}
文件smsh.h
#ifndef _SMSH_H
#define _SMSH_H
#define YES 1
#define NO 0
char *next_cmd(char *prompt, FILE *fp);
char **splitline(char *line);
void freelist(char **list);
void *emalloc(size_t n);
void *erealloc(void *p, size_t n);
int execute(char **argv);
void fatal(char *s1, char *s2, int n);
int ok_to_execute(void);
int builtin_command(char **args, int *resultp);
#endif
文件splitline.c
/**
* splitline.c
* command reading and parsing functions for smsh
*
* char *next_cmd(char *prompt, FILE *fp); - get next command
* char **splitline(char *str); - parse a string
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include "smsh.h"
/**
* purpose: read next command line fp
* return : dynamically allocated string holding command line
* errors : NULL at EOF (not really an error)
* call fatal from emalloc()
* notes: allocates space in BUFSIZ chunks
*/
char *next_cmd(char *prompt, FILE *fp)
{
char *buf; // the buffer
int bufspace = 0; // total size
int pos = 0; // current position
int c; // input char
printf("%s", prompt);
while((c = getc(fp)) != EOF)
{
/* need space? */
if(pos + 1 >= bufspace) // 1 for \0
{
if(bufspace == 0) // y: 1st time
{
buf = emalloc(BUFSIZ);
}
else
{
buf = erealloc(buf, bufspace + BUFSIZ);
}
bufspace += BUFSIZ; // update size
}
if( c == '\n') // end of command?
{
break;
}
buf[pos++] = c; // no, add to buffer
}
if(c == EOF && pos == 0) // EOF and no input
{
return NULL;
}
buf[pos] = '\0';
return buf;
}
#define is_delim(x) ((x) == ' '|| (x) == '\t')
/**
* purpose: split a line into array of white-space tokens
* returns: a NULL-terminated array of pointers to copies of
* the tokens or NULL if line if no tokens in the line
* action : traverse the array , locate strings, make copies
* note : strtok() could work, but we may want to add quotes later
*/
char **splitline(char *line)
{
char *newstr();
char **args;
int spots = 0; // spots in table
int bufspace = 0; // bytes in table
int argnum = 0; // slots used
char *cp = line; // pos in string
char *start;
int len;
if(line == NULL) // handle spacial case
{
return NULL;
}
args = emalloc(BUFSIZ); // initialize array
bufspace = BUFSIZ;
spots = BUFSIZ / sizeof(char *);
while(*cp != '\0')
{
while(is_delim(*cp)) // skip leading spaces
{
cp++;
}
if(*cp == '\0') // quit at end-o-string
{
break;
}
// make sure the array has room(+1 for NULL)
if(argnum + 1 >= spots)
{
args = erealloc(args, bufspace + BUFSIZ);
bufspace += BUFSIZ;
spots += (BUFSIZ / sizeof(char *));
}
// mark start, then find end of word
start = cp;
len = 1;
while(* ++cp != '\0' && ! is_delim(*cp) )
{
len++;
}
args[argnum++] = newstr(start, len);
}
args[argnum] = NULL;
return args;
}
/**
* purpose: constructor for strings
* returns: a string, nuver NULL
*/
char *newstr(char *s, int len)
{
char *rv = emalloc(len + 1);
rv[len] = '\0';
strncpy(rv, s, len);
return rv;
}
/**
* purpose: free the list returned by spitline
* returns: nothing
* action: free all strings in list and then free the list
*/
void freelist(char **list)
{
char **cp = list;
while(*cp)
{
free(*cp++);
}
free(list);
}
void *emalloc(size_t n)
{
void *rv;
if( (rv = malloc(n)) == NULL)
{
fatal("out of memory", "", 1);
}
return rv;
}
void *erealloc(void *p, size_t n)
{
void *rv;
if( (rv = realloc(p, n)) == NULL)
{
fatal("realloc() failed", "", 1);
}
return rv;
}
文件execute.c
/**
* execute.c
* code used by small shell to execute commands
*/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
#include<sys/wait.h>
#include "varlib.h"
/**
* purpose: run a program passing it arguments
* return: status returned via wait, or -1 on error
* errors: -1 on fork() or wait() errors
*/
int execute(char **argv)
{
extern char **environ;
int pid;
int child_info = -1;
if(argv[0] == NULL) // nothing succeeds
{
return 0;
}
if((pid = fork()) == -1)
{
perror("fork");
}
else if(pid == 0)
{
environ = VLtable2environ(); // new line
signal(SIGINT, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
execvp(argv[0], argv);
perror("cannot execute command");
exit(1);
}
else
{
if(wait(&child_info) == -1)
{
perror("wait");
}
}
return child_info;
}
文件process.c
/**
* process.c
* command processing layer
*
* The process(char **arglist) function is called by the main loop
* It sits in front of the execute() function.The layer handles
* two main classes of processing:
* a) built -in functions (e.g exit(), set, = , read, ..)
* b) control structures(e.g. if, while, for)
*/
#include<stdio.h>
#include "smsh.h"
int is_control_command(char *s);
int do_control_command(char **args);
int ok_to_execute(void);
/**
* purpose: process user command
* returns: result of processing command
* details: if a built - in then call appropriate function,
* if not, execute()
* errors: arise from subroutines, handled there
*/
int process(char **args)
{
int rv = 0;
if(args[0] == NULL)
{
rv = 0;
}
else if( is_control_command(args[0]) )
{
rv = do_control_command(args);
}
else if(ok_to_execute() )
{
if( ! builtin_command(args, &rv))
{
rv = execute(args);
}
}
return rv;
}
/**
* "if" processing is done with two state variables
* if_state and if_result
*/
#include<stdio.h>
#include "smsh.h"
enum states{NEUTRAL, WANT_THEN, THEN_BLOCK};
enum results{SUCCESS, FALL};
static int if_state = NEUTRAL;
static int if_result = SUCCESS;
static int last_stat = 0;
int syn_err(char *msg);
/**
* purpose: determine the shell should execute a command
* returns: 1 for yes, 0 for no
* details: if in THEN_BLOCK and if_result was SUCCESS the yes
* if in THEN_BLOCK and if_result was FALL then no
* if in WANT_THEN then syntax error(sh is different)
*/
int ok_to_execute(void)
{
int rv = 1; // default is positive
if(if_state == WANT_THEN)
{
syn_err("then expected");
rv = 0;
}
else if(if_state == THEN_BLOCK && if_result == SUCCESS)
{
rv = 1;
}
else if(if_state == THEN_BLOCK && if_result == FALL)
{
rv = 0;
}
return rv;
}
/**
* purpose: boolean to report if the command is a shell control command
* returns: 0 or 1;
*/
int is_control_command(char *s)
{
return (strcmp(s, "if") == 0 || strcmp(s, "then") == 0
|| strcmp(s, "fi") == 0);
}
/**
* purpose: Process "if", "then", "fi" - change state or detect error
* returns: 0 if ok, -1 for syntax error
*/
int do_control_command(char **args)
{
char *cmd = args[0];
int rv = -1;
if(strcmp(cmd, "if") == 0)
{
if(if_state != NEUTRAL)
{
rv = syn_err("if unexpected");
}
else
{
last_stat = process(args + 1);
if_result = (last_stat == 0) ? SUCCESS : FALL;
if_state = WANT_THEN;
rv = 0;
}
}
else if(strcmp(cmd, "then") == 0)
{
if(if_state != WANT_THEN)
{
rv = syn_err("then unexpeccted");
}
else
{
if_state = THEN_BLOCK;
rv = 0;
}
}
else if(strcmp(cmd, "fi") == 0)
{
if(if_state != THEN_BLOCK)
{
rv = syn_err("fi unexpected");
}
else
{
if_state = NEUTRAL;
rv = 0;
}
}
else
{
fatal("internal error processing:", cmd, 2);
}
return rv;
}
/**
* purpose: handles syntax errors in control structures
* details: resets state to NEUTRAL
* returns: -1 in interactive. Should call fatal in scripts
*/
int syn_err(char *msg)
{
if_state = NEUTRAL;
fprintf(stderr, "syntax error: %s\n", msg);
return -1;
}
文件builtin.c
/**
* builtin.c
* contains the switch and the function for builtin command
*/
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include"smsh.h"
#include"varlib.h"
int assign(char *);
int akname(char *);
/**
* purpose: run a bulitin command
* returns: 1 if args[0] is builtin, 0 if not
* details: test args[0] agsinst all known built ins.
* Call functions
*/
int builtin_command(char **args, int *resultp)
{
int rv = 0;
if(strcmp(args[0], "set") == 0)
{
VLlist();
*resultp = 0;
rv = 1;
}
else if(strchr(args[0], '=') != NULL)
{
*resultp = assign(args[0]);
if(*resultp != -1)
{
rv = 1;
}
}
else if(strcmp(args[0], "export") == 0)
{
if(args[1] != NULL && okname(args[1]))
{
*resultp = VLexport(args[1]);
}
else
{
*resultp = 1;
}
rv = 1;
}
return rv;
}
/**
* purpose: execute name = val AND ensure that name is legal
* returns: -1 for illegal lval, or result of VLstore
* waring: modifies the string, but retores it to normal
*/
int assign(char *str)
{
char *cp;
int rv;
cp = strchr(str, '=');
*cp = '\0';
rv = okname(str) ? VLstore(str, cp + 1) : -1;
*cp = '=';
return rv;
}
/**
* purpose: determines if a string is a legal variable name
* returns: 0 for no, 1 for yes
*/
int okname(char *str)
{
char *cp;
for(cp = str; *cp != '\0'; cp++)
{
if( (isdigit(*cp) && cp == str)
|| !(isalnum(*cp)) || *cp == '_')
{
return 0;
}
}
return (cp != str); // no empty strings, either
}
文件varlib.c
/**
* varlib.c
* a simple storage to store name = value paris
* with facility to mark items as part of the environment
*
* interface:
* VLstore(name, value) returns 1 for ok, 0 for no
* VLlookup(name) returns string or NULL not there
* VLlist() prints out current table
*
* environment-related function
* VLexport(name) adds name to list of env vars
* VLtable2environ() copy from table to environ
* VLenviron2table() copy from environ to table
*
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"varlib.h"
static struct var tab[MAXVARS];
/* private methods */
static char *new_string(char * name, char *val);
static struct var *find_item(char *name, int first_blank);
/**
* traverse list, if found, repalce it, else add at end
* since there is no delete, a blank one is a free one
* return 1 if trouble, 0 if ok (like a command)
*/
int VLstore(char *name, char *val)
{
struct var *itemp;
char *s;
int rv = 1;
/* find spot to put it and make new string */
if( (itemp = find_item(name, 1)) != NULL &&
(s = new_string(name, val)) != NULL)
{
if(itemp->str) // bas a val?
{
free(itemp->str); // y: remove it
}
itemp->str = s;
rv = 0; // ok
}
return rv;
}
/**
* returns new string of form name = value or NULL on error
*/
static char *new_string(char *name, char *val)
{
char *retval;
retval = (char *)malloc(strlen(name) + strlen(val) + 2);
if(retval != NULL)
{
sprintf(retval, "%s=%s", name, val);
}
return retval;
}
/**
* returns value of var or empty string if not there
*/
char *VLlookup(char *name)
{
struct var *itemp;
if( (itemp = find_item(name, 0)) != NULL )
{
return (itemp->str + 1 + strlen(name) );
}
return "";
}
/**
* marks a var for export, adds it if not there
* returns 1 for no, 0 for ok
*/
int VLexport(char *name)
{
struct var *itemp;
int rv = 1;
if( (itemp = find_item(name, 0)) != NULL)
{
itemp->global = 1;
rv = 0;
}
else if(VLstore(name, "") == 1)
{
rv = VLexport(name);
}
return rv;
}
/**
* searches table for an item
* returns ptr to struct or NULL if not found
*
*/
static struct var *find_item(char *name, int first_blank)
{
int i;
int len = strlen(name);
char *s;
for(i = 0; i < MAXVARS && tab[i].str != NULL; ++i)
{
s = tab[i].str;
if(strncmp(s, name, len) == 0 && s[len] == '=')
{
return &tab[i];
}
}
if(i < MAXVARS && first_blank)
{
return &tab[i];
}
return NULL;
}
/**
* performs the shell's set command
* Lists the contens of the varible table, marking each
* exported variable with the symble '*'
*/
void VLlist(void)
{
int i;
for(i = 0; i < MAXVARS && tab[i].str != NULL; ++i)
{
if(tab[i].global)
{
printf("* %s\n", tab[i].str);
}
else
{
printf(" %s\n", tab[i].str);
}
}
}
/**
* initialize the variable table by loading array of strings
* return 1 for ok, 0 for not ok
*/
int VLenviron2table(char *env[])
{
int i;
char *newstring;
for(i = 0; env[i] != NULL; ++i)
{
if(i == MAXVARS)
{
return 0;
}
newstring = (char *)malloc(1 + strlen(env[i]));
if(newstring == NULL)
{
return 0;
}
strcpy(newstring, env[i]);
tab[i].str = newstring;
tab[i].global = 1;
}
while(i < MAXVARS) // we don't need this
{
tab[i].str = NULL; // static globals are nummed
tab[i++].global = 0; // by default
}
return 1;
}
/**
* build an array of pointers suitable for making a new environment note,
* you need to free() this when done to avoid memory leaks
*/
char **VLtable2environ(void)
{
int i;
int j;
int n;
char **envtab; // array of pointers
/* first, count the number of globle variables */
for(i = 0; i < MAXVARS && tab[i].str != NULL; ++i)
{
if(tab[i].global == 1)
{
n++;
}
}
/* then, allocate space for that many variable */
envtab = (char **)malloc( (n + 1) * sizeof(char *) );
if(envtab == NULL)
{
return NULL;
}
/* then, load the array with pointers */
for(i = 0, j = 0; i < MAXVARS && tab[i].str != NULL; ++i)
{
if(tab[i].global == 1)
{
envtab[j++] = tab[i].str;
}
}
envtab[j] = NULL;
return envtab;
}
文件varlib.h
/**
* varlib.h
*/
#ifndef _VARLIB_H
#define _VARLIB_H
#define MAXVARS 200 // a linked list would be nicer
struct var
{
char *str; // name = val string
int global; // a boolean
};
int VLstore(char *name, char *val);
char *VLlookup(char *name);
int VLexport(char *name);
void VLlist(void);
int VLenviron2table(char **env);
char **VLtable2environ(void);
#endif
好了我们编译下
gcc -o smsh4 smsh4.c splitline.c execute.c process.c controflow.c builtin.c varlib.c
参考Unix/Linux编程实践教程
down