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 - luoyhang003@hotmail.com
 * 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。任何人可以进行转载、分享,但不可在未经允许的情况下用于商业用途;转载请注明出处。感谢配合!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值