1. 程式人生 > >Linux程式設計實現一個簡單的Shell

Linux程式設計實現一個簡單的Shell

linux中實現一個簡單的命令解釋程式,功能要求:

1)同時支援內部命令和外部命令,內部命令支援兩個(cdexit

2)支援後臺命令

首先解釋一下上面的兩個要求,實際上是三種情況:

1.如果是內部命令cdexit,那麼在父程序裡直接分析執行

2.如果是外部命令,則建立一個子程序,再由子程序從path環境變數中查詢相應的檔案執行,此時shell會阻塞,等待子程序結束。

3.如果是後臺命令,輸入以&結束,它跟外部命令的最大區別是,後臺命令建立了子程序後不需要呼叫wait(*status),也就是不用等待子程序結束,這樣就變成了後臺程序。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>

#define MAXLEN 80

char cwd[MAXLEN];//當前工作目錄
char *prompt;

char* parseCmd(char* cmd, char** argarr, int* argc)
{
    enum states {S_START, S_INTOKEN, S_INQUOTES};
    int numArgs = 0;//引數數目
    int loop = 1;//迴圈標誌
    enum states state = S_START;//當前狀態
    int lastch;

    while(loop)
    {
        switch(state)
        {
        case S_START:
            if(*cmd == '"')
            {
                *argarr++ = cmd + 1;
                numArgs++;
                state = S_INQUOTES;
            }
            else if(*cmd == 0 || *cmd == ';')
                loop = 0;
            else if(*cmd <= ' ')//忽略ascii碼錶中的空格符及空格符前面的字元
                *cmd = 0;
            else
            {
                *argarr++ = cmd;
                numArgs++;
                state = S_INTOKEN;
            }
            break;
        case S_INTOKEN:
            if(*cmd == 0 || *cmd == ';')
                loop = 0;
            else if(*cmd <= ' ')
            {
                *cmd = 0;
                state = S_START;
            }
            break;
        case S_INQUOTES:
            if(*cmd == 0)
                loop = 0;
            else if(*cmd == '"')
            {
                *cmd = 0;
                state = S_START;
            }
            break;
        }
        cmd++;//指向下一個字元
    }
    *argarr = NULL;//最後一個引數指向空

    if(argc != NULL) *argc = numArgs;

    lastch = cmd[-1];
    cmd[-1] = 0;
    return lastch == ';' ? cmd : NULL;// 如果遇到分號則返回剩餘的字串作為下一條命令
}

int main(int argc, char** argv)
{
    char cmd[80];
    char* source = NULL;
    char* arg[20];
    int statval;
    int numArgs;

    while(1)
    {
        if(source == NULL)
        {
            getcwd(cwd,MAXLEN);
            printf("%s$ ",cwd);
            //讀取命令
            if((source = gets(cmd)) == NULL)
                exit(0);
        }

        source = parseCmd(source, arg, &numArgs);
        if(numArgs == 0) continue;

        //是否退出程式
        if(strcasecmp(arg[0], "exit") == 0)
        {
            exit(0);//不管exit後面是否還有引數,直接退出
        }

        if(!strcmp(arg[0],"cd"))//切換父程序的工作目錄
        {
            chdir(arg[1]);
            continue;
        }

        if(!strcmp(arg[numArgs-1],"&"))//後臺命令
        {
            char* a[20];//臨時變數
            int k;
            for(k=0;k<numArgs-1;k++)
            {
                a[k] = malloc(strlen(arg[k]) + 1);
                strcpy(a[k],arg[k]);
                //printf("%s ", a[k]);
            }
            a[k] = NULL;
            if(fork() == 0)
            {
                if(execvp(a[0],a) == -1)// 執行失敗
                    fprintf(stderr, "exec %s failed: %s\n",argv[0],strerror(errno));
                exit(1);//退出子程序
            }
            for(k=0;k<numArgs-1;k++);//釋放記憶體
                free(a[k]);
            continue;
        }

        //建立子程序執行命令
        if(fork() == 0)
        {
            if(execvp(arg[0],arg) == -1)// 執行失敗
                fprintf(stderr, "exec %s failed: %s\n",argv[0],strerror(errno));
            exit(1);//退出子程序
        }
        wait(&statval);
    }
}

下面看一下執行結果:

內部命令:


外部命令:


可以看到,此時shell發生阻塞。

後臺命令:


可以看到,此時shell並沒有阻塞。

下面說一下程式碼結構,程式碼主要分兩塊:

1.解析命令,將命令列引數進行解析儲存。這裡用到跟編譯原理的詞法分析器一樣的有窮自動機進行分析,將單詞從字元流中取出並儲存。

2.執行命令。判斷是三種命令中的哪一種,如果是內部命令,則在父程序中呼叫相應函式;如果是外部命令,則呼叫execlp()執行,並在父程序中呼叫wait()等待子程序結束;如果是後臺命令,則在第二種情況下只去掉wait()函式,不用等待子程序結束。

羅嗦多一句,在實現後臺命令時,如果關閉shell

,也就是父程序結束,子程序也結束了。但是在系統的bash裡面,呼叫後臺命令後,就算關閉shell,子程序依然可以執行。我猜想是shell建立子程序後,將其父程序改為根程序或某個只要作業系統執行就不會死的程序,所以就算shell關閉子程序也不會結束。



相關推薦

Linux程式設計實現一個簡單Shell

在linux中實現一個簡單的命令解釋程式,功能要求: 1)同時支援內部命令和外部命令,內部命令支援兩個(cd、exit) 2)支援後臺命令 首先解釋一下上面的兩個要求,實際上是三種情況: 1.如果是內部命令cd和exit,那麼在父程序裡直接分析執行 2.如果是外部命令,則

Linux實現一個簡單shell

1.shell原理 shell就是運用程式替換的原理進行實現的。 何為程式替換? 假如作業系統正在執行某一個程式,然後我們利用程式替換函 數指定一個新的程式,讓作業系統去執行我們新指定的程式。也就是這樣一種情形下,我們fork一個程序,如果for

Linux實現一個簡單的進度條【轉】

做的 會有 發現 文件 rsquo 實時 時間 改進 常見 轉自:http://blog.csdn.net/yuehailin/article/details/53999288 說起進度條,其實大家常常見到,比如說你在下載視頻或文件的時候,提示你當前下載進度的就是我們今天

**c++基於tcp協議的socket程式設計實現一個簡單伺服器**

c++基於tcp協議的socket程式設計實現一個簡單伺服器 基於tcp的通訊,可以利益socket套接字實現。通訊,顧名思義需要伺服器和客戶端兩者進行資訊互動。 通過流程圖我們可以看到程式設計實現伺服器和客戶端的步驟大致相同,而伺服器則更為複雜一些。本文之給出了一個簡單的伺服器程式設計和

實現一個簡單shell(支援重定向)

5.16更新:支援重定向 我們知道對於Linux,shell就是個命令列直譯器,當我們輸入相關的命令,會去執行相關的操作。 比如當我們輸入ls -a -l命令,shell就會打印出當前目錄的內容,這是如何實現的?shell自己就是一個程序,當我們輸入ls之類

實現一個簡單shell

程式碼的詳細解釋都在裡面了~~~~ //自己實現一個簡單的shell #include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<errno.h> #include<str

LINUX網路程式設計---實現TCP簡單通訊

#include<stdio.h> #include<errno.h> #include<string.h> #include<strings.h> #include<unistd.h> #include<stdlib.h> #inclu

Linux下模擬實現一個微型shell

首先我們先看一下shell的執行過程:               shell從使用者讀入字串"ls",並建立一個新的程序,在那個程序中執行ls程式並等待那個程序結束。然後shell讀取新的一行輸入,建立一個新的程序,在這個程序中執行程式並等待這個程序結束。 所以我

linux下的一個簡單執行緒安全記憶體池實現

這裡提供一個簡單執行緒安全記憶體池, 基於linux pthread 如下圖: 具體的資料結構: typedef struct LocMap{ char * point;

linux下socket程式設計實現一個伺服器連線多個客戶端

使用socekt通訊一般步驟     1)伺服器端:socker()建立套接字,繫結(bind)並

Linux c實現一個tcp文件服務器和客戶端

repr snippets 功能 stderr strcpy fprintf inet_addr 編寫 create 總體需求:編寫tcp文件服務器和客戶端。客戶端可以上傳和下載文件。 ===========================================

實現一個簡單的lazyman

實現 blog cti name init timeout bin bsp stack function lazyman(name) { return new lazyman.fn.init(name); } lazyman.fn = lazyman.proto

用java實現一個簡單的單用戶登陸功能的思路

get 單用戶 這樣的 簡單的 lock ref 數據庫 清除 一個 引用 所謂“單用戶單賬戶登錄”是指:在同一系統中,一個用戶名不能在兩個地方同時登錄。 我們參照 QQ 實現效果:當某賬號在 A 處登錄後,在未退出的情況下,如果再到 B 處登錄,那麽,系統會擠下 A 處

【Java】Swing+IO流實現一個簡單的文件加密程序

als oncommand override fault 源文件 abs directory imp select EncrytService package com.my.service; import java.io.File; import java

【Java】Swing+IO流實現一個簡單的文件加密程序(較完整版)

move 初始 baidu images 文件選擇器 while login 一個 ktr 留著參考 beans package com.my.bean; import java.io.Serializable; public class

用 C# 實現一個簡單的 Rest Service 供外部調用

message [] operation rem adk www span method title 用 C# 實現一個簡單的 Restful Service 供外部調用,大體總結為4點: The service contract (the methods it o

jQuery實現一個簡單的購物車功能

名稱 展示 -1 set margin for button ans return 最近由於工作需要的原因,開始系統學習jQuery的知識,然後跟著一個視頻教程做了一個購物車的功能,現總結如下。 第一步:準備HTML頁面,代碼如下: <!DOCTYPE html P

實現一個簡單的ConnectionPool

方法 == span sql 需要 動手 cti 修改 使用 看了一圈, 沒看到稍微好用的ConnectionPool, 除了一個aiomysql, 但是這個是異步的, 我暫時沒有用到這麽高版本的Python, 所以就動手造一個輪子. 原理比較簡單, 先造一個線程安全的集

實現一個簡單的虛擬demo算法

child mov 箭頭 內存 架構模式 ren 操作 inpu 設置 假如現在你需要寫一個像下面一樣的表格的應用程序,這個表格可以根據不同的字段進行升序或者降序的展示。 這個應用程序看起來很簡單,你可以想出好幾種不同的方式來寫。最容易想到的可能是,在你的 JavaScr

自己動手實現一個簡單的JSON解析器

pair bool 優點 輕量 結束 pan isdigit 復雜 false 1. 背景 JSON(JavaScript Object Notation) 是一種輕量級的數據交換格式。相對於另一種數據交換格式 XML,JSON 有著諸多優點。比如易讀性更好,占用空間更少等