前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Unix-Linux编程实践教程-chapter09-shell

Unix-Linux编程实践教程-chapter09-shell

作者头像
零式的天空
发布2022-03-02 16:58:04
9140
发布2022-03-02 16:58:04
举报
文章被收录于专栏:零域Blog

第9章 可编程的shell,shell变量和环境:编写自己的shell

Unix shell 运行一种成为脚本的程序.一个shell脚本可以运行程序,接受 用户输入,使用变量和使用复杂的控制逻辑

if..then 语句依赖于下属惯例:Unix程序返回0以表示成功.shell使用 wait来得到程序的退出状态

shell编程语言包括变量.这些变量存储字符串,他们可以在任何命令中使用.shell 变量是脚本的局部变量

每个程序都从调用它的进程中继承一个字符串列表,这个列表被称为环境. 环境用来保存会话(session)的全局设置和某个程序的参数设置,shell允许 用户查看和修改环境

shell是有个编程语言解释器,这个解释器解释从键盘输入的命令,也解释 存储在脚本中的命令序列

shell包括两类变量:局部变量和环境变量 对变量的操作: 赋值 var=value 引用 $var 删除 unset var 输入 read var 列出变量 set 全局化 export var

code

代码语言:javascript
复制
/*
 * smsh4.c small-shell version 4
 *
 * small shell that supports command line parsing
 * and if..then..else.fi logic(by calling process())
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include "smsh.h"

#define DFL_PROMPT ">"

int main()
{
    char * cmdline, *prompt, ** arglist;
    int result;
    void setup();

    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;
}

void setup()
/*
 * purpose: initialize shell
 * returns: nothing. calls fatal() if trouble
 */
{
    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);
}
代码语言:javascript
复制
// smsh.h

#define YES 1
#define NO 0

char *next_cmd();
char ** splitline(char *);
void freelist(char **);
void * emalloc(size_t);
void * erealloc(void *, size_t);
int execute(char **);
void fatal(char *, char *, int);
代码语言:javascript
复制

/*
 * builtin.c
 * contains the switch and the functions for builtin commands
 */

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "smsh.h"
#include "varlib.h"

int assign(char *);
int okname(char *);

int builtin_command(char **args, int *resultp)
/*
 * purpose: run a builtin command
 * returns: 1 if args[0] is builtin, 0 if not
 * details: test args[0] against all known built-ins. Call functions
 */
{
    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;
}

int assign(char *str)
/*
 * purpose: execute name = val AND ensure that name is legal
 * returns: -1 for illegal lval, or result of VLstore
 * warning: modifies the string, but restores it to normal
 */
{
    char *cp;
    int rv;

    cp = strchr(str, '=');
    *cp = '\0';
    rv = (okname(str) ? VLstore(str, cp+1) : -1);
    *cp = '=';
    return rv;
}

int okname(char *str)
/*
 * purpose: determines if a string is a legal variable name
 * returns: 0 for no, 1 for yes
 */
{
    char *cp;

    for (cp = str; *cp; cp++)
    {
        if ((isdigit(*cp) && cp == str) || !(isalnum(*cp)) || *cp == '_')
            return 0;
    }

    return (cp != str);
}
代码语言:javascript
复制
/*
 * controlflow.c
 *
 * "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 {SUCESS, FAIL};

static int if_state = NEUTRAL;
static int if_result = SUCESS;
static int last_stat = 0;

int syn_err(char *);

int ok_to_execute()
/*
 * purpose: determine the shell should execute a command
 * returns: 1 for yes, 0 for no
 * details: if in THEN_BLOCK and if_result was SUCESS then yes
 *          if in THEN_BLOCK and if_result was FAIL then no
 *          if in WANT_THEN then syntax error(sh is different)
 */
{
    int rv = 1;

    if (if_state == WANT_THEN)
    {
        syn_err("then expected");
        rv = 0;
    }
    else if (if_state == THEN_BLOCK && if_result == SUCESS)
        rv = 1;
    else if (if_state == THEN_BLOCK && if_result == FAIL)
        rv = 0;
    return rv;
}

int is_control_command(char *s)
/*
 * purpose: boolean to report if the command is a shell control command
 * returns: 0 or 1
 */
{
    return (strcmp(s, "if") == 0 || strcmp(s, "then") == 0 ||
            strcmp(s, "fi") == 0);
}

int do_control_command(char **args)
/*
 * purpose: Porcess "if", "then", "fi" - change state or detect error
 * returns: 0 if ok, -1 for syntax error
 */
{
    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 ? SUCESS: FAIL);
            if_state = WANT_THEN;
            rv = 0;
        }
    }
    else if (strcmp(cmd, "then") == 0)
    {
        if (if_state != WANT_THEN)
            rv = syn_err("then unexpected");
        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;
}

int syn_err(char * msg)
/*
 * purpose: handles syntax errors in control structures
 * details: resets state to NEUTRAL
 * returns: -1 in interactive mode. Should call fatal in scripts
 */
{
    if_state = NEUTRAL;
    fprintf(stderr, "syntax error: %s\n", msg);
    return -1;
}
代码语言:javascript
复制
// 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>

int execute(char *argv[])
/*
 * purpose: run a program passing it arguments
 * returns: status returned via wait, or -1 on error
 * errors: -1 on fork() or wait() errors
 */
{
    int pid;
    int child_info = -1;
    extern char ** environ;

    if (argv[0] == NULL)
        return 0;

    if ((pid = fork()) == -1)
        perror("fork");
    else if (pid == 0)
    {
        environ = VLtable2environ();
        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;
}
代码语言:javascript
复制
/*
 * process.c
 * command processing layer
 *
 * The process(char ** arglist) function is called by the main loop
 * It sits in front of the execute() function. This 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 *);
int do_control_command(char **);
int ok_to_execute();

int process(char **args)
/*
 * purpose: process user command
 * returns: result of proceessing command
 * details: if a built-in then call arrprporiate function, if not
 *          execute()
 * errors: arise from subroutines, handled there
 */
{
    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;
}
代码语言:javascript
复制
/*
 * 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"

char * next_cmd(char * prompt, FILE * fp)
/*
 * purpose: read next command line from fp
 * returns: dynamically allocated string holding command line
 * errors: NULL at EOF (not really an error)
 *          calls fatal from emalloc()
 * notes: allocates space in BUFSIZ chunks
 */
{
    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)
    {
        if (pos + 1 >= bufspace)
        {
            if (bufspace == 0)
                buf = emalloc(BUFSIZ);
            else
                buf = erealloc(buf, bufspace+BUFSIZ);
            bufspace += BUFSIZ;
        }

        if (c == '\n')
            break;

        buf[pos++] = c;
    }
    if (c == EOF && pos == 0)
        return NULL;
    buf[pos] = '\0';
    return buf;
}

/*
 * splitline (parse a line into an array of strings)
 */
char ** splitline(char * line)
/*
 * purpose: split a line into array of white - space separated tokens
 * returns: a NULL-terminated array of pointers to copies of the
 *          tokens or NULL if line if no tokens on the line
 * action: travers the array, locate strings, make copies
 * note: strtok() could work, but we may want to add quotes later
 */
{
    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)
        return NULL;

    args = emalloc(BUFSIZ);
    bufspace = BUFSIZ;
    spots = BUFSIZ / sizeof(char *);

    while (*cp != '\0')
    {
        while (isspace(*cp))       // skip leading spaces
            cp++;
        if (*cp == "\0")
            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' && !(isspace(*cp)))
        {
            len++;
        }
        args[argnum++] = newstr(start, len);
    }

    args[argnum] = NULL;
    return args;
}

/*
 * purpose: constructor for strings
 * returns: a string, never NULL
 */
char * newstr(char *s, int l)
{
    char * rv = emalloc(l + 1);

    rv[l] = '\0';
    strncpy(rv, s, l);
    return rv;
}

void freelist(char ** list)
/*
 * purpose: free the list returned by splitline
 * returns: nothing
 * action: free all strings in list and then free the 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;
}
代码语言:javascript
复制
// varlib.h

int VLstore(char *name, char *val);
char * VLlookup(char * name);
int VLexport(char *name);
void VLlist();
int VLenviron2table(char *env[]);
char ** VLtable2environ();
代码语言:javascript
复制
/*
 * varlib.c
 *
 * a simple storage system to store name=value pairs
 * with facility to mark items as part of the environment
 *
 * interface:
 *  VLstore(name, value)    return 1 for ok, 0 for no
 *  VLlookup(name)          return string or NULL if not there
 *  VLlist()                prints out current table
 *
 * environment-related functions
 *  VLexport(name)          adds name to list of env vars
 *  VLtable2environ()       copy from table to environ
 *  VLenviron2table()       copy from environ to table
 *
 * details:
 * the table is stored as an array of structs that 
 * contain a flag for global and a single string of
 * the form name=value. This allows EZ addition to the
 * environment. It makes searching pretty easy, as
 * long as you search for "name="
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "varlib.h"

#define MAXVARS 200         // a linked list would be nicer

struct var
{
    char *str;
    int global;
};

static struct var tab[MAXVARS];     // the table

static char *new_string(char *, char *);
static struct var * find_item(char *, int);

int VLstore(char *name, char *val)
/*
 * traverse list, if found, replace it, else add at end
 * since there is no delete, a blank on is a free one
 * return 1 if trouble, 0 if ok(like a command)
 */
{
    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)
            free(itemp->str);
        itemp->str = s;
        rv = 0;
    }

    return rv;
}

static char * new_string(char *name, char *val)
/*
 * returns new string of form name=value or NULL on error
 */
{
    char * retval;

    retval = malloc(strlen(name) + strlen(val) + 2);
    if (retval != NULL)
        sprintf(retval, "%s = %s", name, val);
    return retval;
}

char * VLlookup(char * name)
/*
 * returns value of var or empty string if not there
 */
{
    struct var * itemp;

    if ((itemp = find_item(name, 0)) != NULL)
        return itemp->str + 1 + strlen(name);
    return "";
}

int VLexport(char *name)
/*
 * marks a var for export, adds it if not there
 * returns 1 for no, 0 for ok
 */
{
    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;
}

static struct var * find_item(char *name, int first_blank)
/*
 * searches table for an item
 * returns ptr to struct or NULL if not found
 * OR if (first_blank) then ptr to first blank one
 */
{
    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] == '=')
        {
            // printf("%s %d\n", name, len);
            return &tab[i];
        }
    }
    if (i < MAXVARS && first_blank)
        return &tab[i];

    return NULL;
}

void VLlist()
/*
 * performs the shell's set command
 * Lists the contents of the variable table, marking each
 * exported variable with the symbol '*'
 */
{
    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);
    }
}

int VLenviron2table(char *env[])
/*
 * initialize the variable table by loading array of strings
 * return 1 for ok, 0 for not ok
 */
{
    int i;
    char *newstring;

    for (i = 0; env[i] != NULL; i++)
    {
        if (i == MAXVARS)
            return 0;
        newstring = 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)
    {
        tab[i].str = NULL;
        tab[i++].global = 0;
    }
    return 1;
}

char ** VLtable2environ()
/*
 * build an array of pointers suitable for making a new environment
 * note: you need to free() this when done to avoid memory leaks
 */
{
    int i,
        j,
        n = 0;
    char **envtab;

    // first, count the number of global variables
    for (i = 0; i < MAXVARS && tab[i].str != NULL; i++)
        if (tab[i].global == 1)
            n++;

    // then, allocate space for that many variables
    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;
            // printf("%d %s\n",i, tab[i].str);
        }
    envtab[j] = NULL;

    return envtab;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016-07-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 第9章 可编程的shell,shell变量和环境:编写自己的shell
  • code
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档