shell
在了解bash之前 我们要先了解shell
Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。
Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。
Ken Thompson 的 sh 是第一种 Unix Shell,Windows Explorer 是一个典型的图形界面 Shell。
可以称之为壳程序,用于用户与操作系统进行交互。用来区别与核,相当于是一个命令解析器,Shell有很多中,这里列出其中几种
Bourne SHell(sh)
Bourne Again SHell(bash)
C SHell(csh)
KornSHell(ksh)
zsh
各个shell的功能都差不太多,在某些语法的下达下面有些区别,Linux预设就是bash。
bash命令是sh命令的超集,大多数sh脚本都可以在bash下运行,bash主要有如下这些功能
bash功能
记录历史命令:bash可以记录曾经的命令,保持在~/.bash_history文件中,只保存上次注销登录之后的命令
tab键自动补全:使用tab见可以自动不全命令或者目录i
alias命令别名:可以使用alias ll='ls -al'来设置命令的别名
工作控制:可以将某些任务放在后台去运行,这里不多种介绍
程序脚本:可以执行shell脚本文件
通配符:在查找相关文件或者执行相关命令时,可以使用通配符*
内建命令type:可以使用type 命令来查看某个命令是否为内建在bash当中的命令「
bash变量
bash中变量有两种,分别是环境变量和自定义变量,可以理解为全局变量和局部变量,在理解他们的区别前,需要知道父程序和子程序,举个例子,当前的bash我们称之为父程序,而在这个bash下执行的任何程序,都称之为子程序。那全局变量和局部变量的区别就是,全局变量在子程序中仍然有效,局部变量只在当前程序中生效。(注意,一旦退出父程序这个bash的话,无论是全局变量还是局部变量都失效了,再次打开bash时,该变量是不存在的)
mybash
我们可以结合bash界面 实现一个用户自定义的mybash
首先我们可以模仿终端界面 实现一个用户自用的mybash
可以看到我们的提示符包含 用户名 设备名 当前路径 权限类别
那么我们可以暂时将这个标识符
printf("ubuntu@localhost : ~ $ ");
fflush(stdout);
这样就会在界面上打印一个静态的标识符
@之前的是用户名 每个人的用户名都是自定义的 然后 @ 到:之前是设备名 ~是当前路径
$是表示当前权限是用户
那么如果我们想实现可以变动的并且可以自适应的标识符应该 怎么办呢
void printf_info(){
char* user_str = "$";
int user_id = getuid();
if(user_id == 0) user_str = "#";
struct passwd* ptr = getpwuid(user_id);
char hostname[128]={0};
gethostname(hostname,128);
char dir[256]={0}; /
getcwd(dir,256);
printf("\033[1;32m%s@%s\033[0m:\033[1;34m:%s\033[0m %s ",ptr->pw_name,hostname,dir,user_str);
fflush(stdout);
}
这里面的函数可以获取到用户的信息
例如getuid()可以获取到权限类别
如果是管理员(超级用户)权限 他的返回值是0 我们这里就默认设计成 初始值为$
然后是 ptr这个结构体 结构体中的pw_name可以获取到用户的用户名
然后gethostname可以获取到设备名
然后我们可以在printf中看到不一样的符号
这个是可以改变字体颜色的 因为在Linux中bash界面我们可以看到 标识符是有颜色的
那么接下来我们就要实现bash的实现命令的功能
首先我们可以考虑到的是 fork+exec系列函数 来实现我们的命令
但是这里我们只能使用 execv系列函数 因为
execv系列函数的 函数参数是用数组存储的 更加利于我们对函数进行调试和修改
char* get_cmd(char* buff,char* myargv[]){
if(buff == NULL || myargv == NULL) return NULL;
char* s = strtok(buff," ");
int i = 0;
while(s != NULL){
myargv[i++] = s;
s = strtok(NULL," ");
}
return myargv[0];
}
char* myargv[ARG_MAX] = {0};
char* cmd = get_cmd(buff,myargv);
if(cmd == NULL) continue;
else if(strcmp(cmd,"cd") == 0){ //["cd"]["/bin"]
if(myargv[1] != NULL && chdir(myargv[1]) == -1){
perror("cd err");
}
}
else if(strcmp(cmd,"exit") == 0){
// exit(0);
break;
}
else{ //????????
//fork + exec
}
}
这样就可以实现了我们只调用系统提供的命令来进行操作
我们也可以应用自己编译完成的可执行程序
例如自己编写的 ls pwd等
void run_cmd(char* name,char*myargv[]){
if(name == NULL || myargv == NULL)
return ;
int len = strlen(name);
if(name[len-1] == '\n')
name[len-1]='\0';
pid_t pid = fork();
if(pid == -1) return;
if(pid == 0){
char pathname[128] = {0};
if(strncmp(name,"/",1) == 0 || strncmp(name,"./",2) == 0){
strcpy(pathname,name);
}
else{
strcpy(pathname,PATH_BIN);
strcat(pathname,name);
}
execvp(pathname,myargv);
perror("exec err");
exit(0);
}
wait(NULL);
}
这里看到我们使用了自己的可执行程序
然后就需要引入自己的环境变量而不是使用系统的环境变量
然后里面要有自己编译的可执行程序
然后下面附上 mybash以及几个可运行程序
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <pwd.h>
#define ARG_MAX 10
#define PATH_BIN "/home/ubuntu/test/mybin/"
void run_cmd(char* name,char*myargv[]){
if(name == NULL || myargv == NULL)
return ;
int len = strlen(name);
if(name[len-1] == '\n')
name[len-1]='\0';
pid_t pid = fork();
if(pid == -1) return;
if(pid == 0){
char pathname[128] = {0};
if(strncmp(name,"/",1) == 0 || strncmp(name,"./",2) == 0){
strcpy(pathname,name);
}
else{
strcpy(pathname,PATH_BIN);
strcat(pathname,name);
}
execvp(pathname,myargv);
perror("exec err");
exit(0);
}
wait(NULL);
}
char* get_cmd(char* buff,char* myargv[]){
char* s = strtok(buff," ");
int i = 0;
while(s != NULL){
myargv[i++] = s;
s = strtok(NULL," ");
}
return myargv[0];
}
void printf_info(){
char* user_str = "$";
int user_id = getuid(); if(user_id == 0) user_str = "#";
struct passwd* ptr = getpwuid(user_id);
char hostname[128]={0};
gethostname(hostname,128);
char dir[256]={0};
getcwd(dir,256);
printf("\033[1;32m%s@%s\033[0m:\033[1;34m:%s\033[0m %s ",ptr->pw_name,hostname,dir,user_str);
fflush(stdout);
}
int main(){
while(1){
//printf("stu@localhost~$");
printf_info();
fflush(stdout);
char buff[128]={0};
char* myargv[ARG_MAX] = {0};
fgets(buff,127,stdin);
char* cmd = get_cmd(buff,myargv);
if(strncmp(cmd , "cd",2) == 0){
char buff[128]={0};
strncpy(buff,myargv[1],strlen(myargv[1])-1);
//
if(buff==NULL||chdir(buff)==-1){
perror("cd err");
}
}
else if(strncmp(cmd,"exit",4)==0){
break;
}
else if(strncmp(cmd,"\n",1) == 0){
continue;
}
else{
//fork+exec
run_cmd(cmd,myargv);
}
}
exit(0);
}
ls
#include <stdio.h>
#include<unistd.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
int main(){
opendir(path)
char path[128]={0};
if(getcwd(path,128) == NULL) exit(1);
DIR* pdir = opendir(path);
if(pdir == NULL) exit(1);
struct dirent* s = NULL;
while((s=readdir(pdir)) != NULL){
if(strncmp(s->d_name,".",1) == 0){
continue;
}
struct stat filestat;
stat(s->d_name,&filestat);
if(S_ISDIR(filestat.st_mode)){
printf("\033[1;34m%s\033[0m ",s->d_name);
}
else{
if(filestat.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) { // 000100 & 0000111 -> 0000100 -> 1 true
printf("\033[1;32m%s\033[0m ",s->d_name);
}
else{
printf("%s ",s->d_name);
}
}
}
printf("\n");
closedir(pdir);
exit(0);
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
char path[128]={0};
if(getcwd(path,128) == NULL){
perror("getcwd err");
exit(1);
}
printf("%s\n",path);
exit(0);
}
pwd
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(){
printf("\033[2J\033[0;0H");
exit(0);
}
clear