PART 1: Shell 提示符的实现
这篇文章是《动手写 Shell》系列文章的第 <1> 篇,在这篇文章中,我们先完成一个 Shell 中最基本的功能 - Shell 提示符的实现。在这篇文章中,我会介绍一下实现的思路,以及介绍下用到的系统 API 和一些 C 语言中的库函数。
Shell 提示符
用过 Linux 的人都知道当我们打开终端时,在命令行中会出现一行字,后边会有光标在一直删,那一行字就是 Shell 的提示符。
提示符格式
我们通常看到的 Shell 提示符的格式如下所示:
username@hostname:~/path$
- 1
我们要写的 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. */
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
0x02 得到当前主机名
通过 gethostname()
函数我们可以获得当前主机名
int max_name_len = 256;
char hostname[max_name_len];
gethostname(hostname, max_path_len);
- 1
- 2
- 3
0x03 获取当前路径
通过 getced()
函数我们可以获得当前路径
int max_path_len = 1024;
char pathname[max_path_len];
getcwd(pathname, max_path_len);
- 1
- 2
- 3
0x04 需要处理的其它问题
当前用户目录下的显示
对于在当前目录下的提示符我们采用将用户主目录用 ~
来代替,对于不在当前目录下的提示符我们再使用完整的目录来进行显示,这也是目前 Ubuntu 中的默认终端所采取的提示符显示格式。
实现策略:
- 如果当前目录前面一部分同用户主目录路径不相符,则显示完整目录
- 如果当前目录的长度小于用户主目录路径,则显示完整目录
- 其他情况,将当前目录与用户主目录相同部分用
~
代替
实现方法:
- 获取用户主目录:
我们通过访问 passwd
结构体的方式来获取用户主目录路径:
char home_dir[1024];
pwd = getpwuid(getuid());
home_dir = pwd->pw_dir;
- 1
- 2
- 3
是否是 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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
本文的版权归作者 罗远航 所有,采用 Attribution-NonCommercial 3.0 License。任何人可以进行转载、分享,但不可在未经允许的情况下用于商业用途;转载请注明出处。感谢配合!
文章来源: blog.csdn.net,作者:冰水比水冰,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/luoyhang003/article/details/51112434
- 点赞
- 收藏
- 关注作者
评论(0)