PART 1: Shell 提示符的實現

這篇文章是《動手寫 Shell》系列文章的第 <1> 篇,在這篇文章中,我們先完成一個 Shell 中最基本的功能 - Shell 提示符的實現。在這篇文章中,我會介紹一下實現的思路,以及介紹下用到的系統 API 和一些 C 語言中的庫函數。

Shell 提示符

用過 Linux 的人都知道當我們打開終端時,在命令行中會出現一行字,後邊會有光標在一直刪,那一行字就是 Shell 的提示符。

提示符格式

我們通常看到的 Shell 提示符的格式如下所示:

username@hostname:~/path$

我們要寫的 Shell 的第一步就是來實現這個東西。

實現思路

從提示符的格式中我們就知道我們首先需要得到:

  • 用戶名
  • 主機名
  • 當前路徑

我們主要通過調用 Linux 系統 API 的方式來完成上述功能。

所需要的功能

0x00 得到當前用戶名

passwd 結構體

在 Linux 中定義了一個 passwd 結構體,該結構體定義了與用戶有關的信息,在 /usr/include/pwd.h

該結構體定義如下:

/* The passwd structure.  */
struct passwd
{
  char *pw_name;        /* Username.  */
  char *pw_passwd;      /* Password.  */
  __uid_t pw_uid;       /* User ID.  */
  __gid_t pw_gid;       /* Group ID.  */
  char *pw_gecos;       /* Real name.  */
  char *pw_dir;         /* Home directory.  */
  char *pw_shell;       /* Shell program.  */
};

getpwuid() 與 getuid() 函數

  • getuid(): 用來獲取當前用戶的 ID
  • getpwuid(uid_t uid): 根據用戶 ID 來獲取 passwd 結構體

用法:

#include <pwd.h>
#include <sys/types.h>
#include <stdio.h>
int main()
{
    uid_t my_uid;
    structpasswd *my_info;
    my_info =getpwuid(getuid());
    printf( "my name = [%s]\n", my_info->pw_name );
    printf( "my passwd = [%s]\n", my_info->pw_passwd );
    printf( "my uid = [%d]\n", my_info->pw_uid );
    printf( "my gid = [%d]\n", my_info->pw_gid );
    printf( "my gecos = [%s]\n", my_info->pw_gecos );
    printf( "my dir = [%s]\n", my_info->pw_dir );
    printf( "my shell = [%s]\n", my_info->pw_shell );
    return0;
}

0x02 得到當前主機名

通過 gethostname() 函數我們可以獲得當前主機名

int max_name_len = 256;
char hostname[max_name_len];
gethostname(hostname, max_path_len);

0x03 獲取當前路徑

通過 getced() 函數我們可以獲得當前路徑

int max_path_len = 1024;
char pathname[max_path_len];
getcwd(pathname, max_path_len);

0x04 需要處理的其它問題

當前用戶目錄下的顯示

對於在當前目錄下的提示符我們採用將用戶主目錄用 ~ 來代替,對於不在當前目錄下的提示符我們再使用完整的目錄來進行顯示,這也是目前 Ubuntu 中的默認終端所採取的提示符顯示格式。

實現策略:

  • 如果當前目錄前面一部分同用戶主目錄路徑不相符,則顯示完整目錄
  • 如果當前目錄的長度小於用戶主目錄路徑,則顯示完整目錄
  • 其他情況,將當前目錄與用戶主目錄相同部分用 ~ 代替

實現方法:

  • 獲取用戶主目錄:

我們通過訪問 passwd 結構體的方式來獲取用戶主目錄路徑:

char home_dir[1024];
pwd = getpwuid(getuid());
home_dir = pwd->pw_dir;

是否是 root 用戶

對於是 root 用戶的提示符我們將使用 # 來進行表示,對於其他用戶使用 $ 來表示。

實現方法:

我們使用 getuid() 函數來判斷當前用戶是否是 root 用戶, 如果返回值爲 0,則是 root 用戶。

用到的C庫函數

sprintf()

功能

它的功能是把格式化的數據寫入某個字符串緩衝區。

頭文件

stdio.h

原型

int sprintf( char *buffer, const char *format, [ argument] … );

參數列表

  • buffer: char型指針,指向將要寫入的字符串的緩衝區。
  • format: 格式化字符串。
  • [argument]…: 可選參數,可以是任何類型的數據。

strncmp()

功能

這個函數用來比較 s1 和 s2 字符串的前 num 個字符。如果兩個字符串相等的話,strncmp 將返回0。

頭文件

string.h

原型

int strncmp ( const char * str1, const char * str2, size_t num );

參數列表

  • str1: 待比較字符串 1
  • str2: 待比較字符串 2
  • num: 比較的位數

參考代碼

下面貼上實現 Shell 提示符的代碼:

詳見:https://github.com/luoyhang003/linux_kernel_expriment/tree/master/exp2

/*
 *      prompt.c ---- Description
 *------------------------------------------------------------
 * Date: April 8th, 2016
 * Copyright: Written by Jason Luo - [email protected]
 * Function: Promption of the Shell
 *------------------------------------------------------------
 */

#include"lshell.h"

const int max_name_len = 256;
const int max_path_len = 1024;

void get_prompt(char *prompt)
{
    extern struct passwd *pwd;
    char hostname[max_name_len];
    char pathname[max_path_len];
    int prompt_length;

    pwd = getpwuid(getuid());
    getcwd(pathname, max_path_len);

    if(gethostname(hostname, max_path_len) == 0)
    {
        sprintf(prompt, "lshell>%s@%s:", pwd->pw_name, hostname);
    }
    else
    {
        sprintf(prompt, "lshell>%s@unknown:", pwd->pw_name);
    }
    prompt_length = strlen(prompt);


    if(strlen(pathname) < strlen(pwd->pw_dir) || (strncmp(pathname, pwd->pw_dir, strlen(pwd->pw_dir))) != 0)
    {
        sprintf(prompt + prompt_length, "%s", pathname);
    }
    else
    {
        sprintf(prompt + prompt_length, "~%s", pathname + strlen(pwd->pw_dir));
    }
    prompt_length = strlen(prompt);
    if(geteuid() != 0)
    {
        sprintf(prompt + prompt_length, "$");
    }
    else
    {
        sprintf(prompt + prompt_length, "#");
    }
    return;
}

本文的版權歸作者 羅遠航 所有,採用 Attribution-NonCommercial 3.0 License。任何人可以進行轉載、分享,但不可在未經允許的情況下用於商業用途;轉載請註明出處。感謝配合!

發佈了105 篇原創文章 · 獲贊 237 · 訪問量 28萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章