linux之shell的进阶补充和基础IO流的介绍

1.内建命令的实现

先前基础版中没有对cd这些内建命令的设置,这些内建命令是在父进程中实行的,说白了就是在父进程中调用对应的OS接口

1.cd

复制代码
//移动到path路径
int chdir(const char*path);

if(argv[0] == "cd")
{
    if(argc == 2)
    {
        chdir(argv[1]);
        //lastcode是错误码
        lastcode = 0;
    }
    else
    {
        //应该要分类讨论的
        lastcode = 1;
    }
return true;

补充一点知识:

(1)char* getcwd(char*buf,int size);返回值就是buf的地址,buf存当前路径,size是buf的大小后最大存储空间

(2)putenv("xx=xxx");xx已存在时就覆盖原有的环境变量,没有就创建一个新的,=号是一定要存在的。

(3)有些指令即是内置指令,也是外带指令,也就是一个指令即可能只是shell中的一个函数,也可能在/usr/bin/目录下留有可执行文件。

2.echo

(1)$? ,打印lastcode,其他情况打印对应环境变量名的值

(2)直接打印

复制代码
if(gargv[0] =="echo")
{
    if(gargc == 2)
{
    if(gargv[1][0] == '$')
{
    if(gargv[1][1] == '?')
{    
    printf("%d\n", lastcode);
    lastcode = 0;
}
    else
    {
    //此处还要处理$环境变量名的情况
    }
}
    else
{
    printf("%s\n", gargv[1]);
    lastcode = 0;
}
}
    else
{
    lastcode = 3;
}

3.生成环境变量表

此处我们简化处理使用父进程的环境变量表即可(只是模拟,并没有实际使用)

cpp 复制代码
void InitEnv()
{
extern char **environ;
int index = 0;
while(environ[index])
{
genv[index] = (char*)malloc(strlen(environ[index])+1);
strncpy(genv[index], environ[index], strlen(environ[index])+1);
index++;
}
genv[index] = nullptr;
}

2.基础IO(才是核心)

1.复习c语言与一些之前的知识

操作文件就是操作其的内容与属性。文件由进程打开,因此对文件的操作就是进程对文件的操作。

磁盘的管理者只有OS,我们调用库函数的本质就是间接使用OS提供的接口。

同一时间下被打开的文件可能有很多个,因此OS是采用先描述再组织的方式管理当前所有被打开的文件。

文件分两种:

内存级(已经被打开)文件2.磁盘(未被打开)级文件,后面先介绍内存级文件

函数:

复制代码
fwrite(const void *ptr,size_t size,size_t nmemb,FILE*stream);
//将ptr中size*nmemmb大小的数据存入stream中

feof(FILE*stream);
//判断stream指向的文件是否走到结尾

size_t fread(const void *ptr,size_t size,size_t nmemb,FILE*stream);
返回值是返回nmemb的值

w,a,r,的本质:

复制代码
w:文件会先被清空
//如: > 重定向就是将右边的文件用类似于w的形式打开
a:往先前的文件数据中追加
//如:  >>重定向就是将右边的文件用类似于a的形式打开
r+:以既能写有能读的方式打开文件,但读写共用同一个光标

3.系统的IO调用

复制代码
int open(const char* pathname,int flags);
int open(const char* pathname,int flags,mode_t mode);

flags是一种标识符,例:

采用&来区分是否使用了这个标识符,使用|来同时使用多种标识符。

复制代码
//二进制位0001
int a = 1;
//二进制位0010
int b = 2;
//二进制位0100
int c = 4;
//....

void func(int flags)
{
if(flags&(1 << 0))cout << "1号标识符";
if(flags&(1 << 1))cout << "2号标识符";
if(flags&(1 << 2))cout << "3号标识符";
//.....
}

int main()
{
    func(a | b | c);    
    return 0
}

当filename是一个新文件时,要给其加上初始化的权限设置(会被权限掩码(umask)操作),也就是插入mode参数(例:0666值)来初始化文件权限。

标识符类型:

O_CREAT 打开文件

O_WRONLY 往文件中写入

O_TRONC 清空文件

O_APPEND 追加内容

O_RDONLY 以读的方式打开文件(二进制形式)

复制代码
int close(int fd);
fd就是open的返回值,用于关闭对应的文件

ssize_t write(int fd,const void* buf,size_t count);
把buf前count个字符写入fd对应的文件中,返回值为成功输入的字符数

使用strlen统计buf大小时不用+1,即不用写入/0,因为文件写入时不认识c语言的转义字符。

buf的类型为void*,说明其可以传入任意类型的数据,根本上来讲就是OS其实不管心我们传进来的数据类型,所有的数据本身都是以二进制的方式写入的,而显示屏又叫字符显示屏,因此传入的数据中只有才能正常显示,其余的都是乱码。

因此语言层面上的文本写入或二进制写入的本质都是调用write的封装罢了,实现文本写入的方式为:

复制代码
int func(int a)
{
    char buf[16];
    //提前写成字符的形式了
    sprintf(buf,siseof(buf),a);
    write(fd,buf,strlen(buf));
}

read

复制代码
ssize_t read(int fd,void * buf,size_t count);

将fd对应文件的内容写到buf中,最大写入大小为count,返回值>0时表示写入的字符个数,=0表示已经读取到文件末尾,<0代表读取失败。

read和write调用的对应文件,必须要有对应对应的标识符支持

相关推荐
吴声子夜歌44 分钟前
Shell编程实例——编写安全的shell脚本(二)
linux·运维·shell
悠悠121381 小时前
微服务通信别只看性能:REST 与 gRPC 的 6 个选型判断和一次 502 故障复盘
运维·服务器
xiaoye-duck2 小时前
《Linux 网络编程》深入理解 IP 协议(二):网段划分、私有 IP 与 NAT 地址转换
linux·网络·ip
云老大-阿里云国际站代理商3 小时前
华为云国际站服务代理商:NAT网关怎么配?多台ECS共用公网IP要注意什么
服务器·华为云·云计算
zx_741484813 小时前
【Linux入门】Shell 函数、正则表达式与文本处理:cut 与 awk
linux·运维·正则表达式
Zenova EdgeOS3 小时前
Linux dmesg 工业边缘实战:内核日志过滤、持久化与故障定位
linux·运维·边缘计算·工业边缘·dmesg·内核日志
金士顿4 小时前
System.Text.Json Source Generator 深度解析:为什么 Native AOT 不喜欢反射
linux·asp.net core·arm64·net·native aot
wdfk_prog4 小时前
ROS教程08:从 TransportTCP::connect() 追到 TCPROS Connection Header、序列化与 Socket 数据传输
运维·缓存·docker·容器·ros
做运维的阿瑞5 小时前
Python 标准库汇总:分类速览与常用模块清单
linux·运维·python
百万蹄蹄向前冲5 小时前
密码都对Node.js却连不上Linux数据库
linux·mysql