【Linux进程】Linux Shell编程实战:构建简易脚本示例与技巧详解

📝个人主页🌹:Eternity._
⏩收录专栏⏪:Linux “ 登神长阶 ”
🤡往期回顾🤡:暂无
🌹🌹期待您的关注 🌹🌹

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

❀Linux进程

  • 📒1. 获取输入
    • 🌞获取环境变量
    • ⭐获取用户输入
  • 📚2. 分割字符串
  • 📜3. 检查内建命令
  • 📝4. 执行程序
  • 📖5. 总结


前言:在Linux的浩瀚宇宙中,Shell脚本无疑是连接用户与系统之间的桥梁,它赋予了用户强大的自动化处理能力,使得繁琐的重复性任务变得轻松高效。对于每一位Linux爱好者、系统管理员或是开发人员而言,掌握Shell脚本编写技能无疑是一项不可或缺的宝贵财富

本篇文章旨在通过一系列精心设计的简易Shell脚本示例,引领读者踏入Shell编程的奇妙世界。我们不会深入复杂的语法细节和高级特性,而是聚焦于最基础、最实用的知识点,让初学者也能快速上手,体验到编写Shell脚本的乐趣与便利

让我们一起,在Shell脚本的海洋中扬帆起航,探索更多未知的精彩吧!


📒1. 获取输入

在构建简易Shell的时候我们首先就是要获取输入

  • 获取环境变量:能够像shell一样运行会出现部分环境变量
  • 获取用户输入:获取用户输入的指令

🌞获取环境变量

在运行shell时就会出现一些环境变量,我们自定义构建的shell中,也可以实现这一步

在这里插入图片描述
用户名:pxt
主机名:hecs - 198213
当前目录:myshell

// 获取环境变量 user,hostname,pwd,home
char *homepath()
{char *home= getenv("HOME");if(home) return home;else return (char*)".";
}const char *getUsername()
{const char *name = getenv("USER");if(name) return name;else return "none";                                                                                                                                                                                    
}const char *getHostname()
{const char *hostname = getenv("HOSTNAME");if(hostname) return hostname;else return "none";
}const char *getCwd()
{const char *cwd = getenv("PWD");if(cwd) return cwd;else return "none";
}printf("[%s@%s %s]$ ",getUsername(), getHostname(), getCwd());

在这里插入图片描述
这里我们直接将绝对路径展示了出来,当然没什么影响

这里我们用到了一个函数getenv(),这个函数用于获取环境变量的值,它的头文件是<stdlib.h>,在shell脚本中,获取环境变量的值是通过直接使用变量名来实现的,而不需要特别的函数或方法

在这里插入图片描述
在我们完成最基础的一步之后,我们要开始模拟我们使用的shell的使用方式,接下来一步就是获取用户输入


⭐获取用户输入

获取用户输入时,我们可以创建一个字符数组用来存储用户的输入

#define NUM 1024char usercommand[NUM];

在获取用户输入时,输完一个指令后不换行,进行下一次输入。
但是在我们输入完成时,总会回车确认,因此我们需要修改最后一次输入

getUserCommand:

// 对用户输入进行封装
int getUserCommand(char *command, int num)
{printf("[%s@%s %s]$ ",getUsername(), getHostname(), getCwd());char *r = fgets(command, num, stdin); // 最后还是会输入'\n',回车if(r == NULL) return -1; // TODO// 将最后一次输入修改成'\0'command[strlen(command)-1] = '\0'; // 这里不会越界return strlen(command);                                                                                                                                                                                
}

📚2. 分割字符串

在Shell中,分割字符串是一个常见的操作,它涉及到将一串包含多个子字符串(可能由空格、逗号、冒号等分隔符分隔)的文本分割成单独的部分,以便进行进一步的处理或赋值给不同的变量

在我们完成用户输入指令的读取之后,我们需要将字符串进行分割,让我们的指令能够被正确的读取并且实现出来,通常我们的分隔符是' '(空格)

#define SIZE 64	// 设置argv的大小
#define SEP " " // 分隔符 " "
#define debug 1 // 用来测试是否能够成功分割字符串char *argv[SIZE]; // 用来储存分割后的字符串

而关于分割字符串,我们在C语言的学习中可能提到过一个字符串函数strtok(),它的头文件是<string.h>,用于分割字符串。它基于一系列的分隔符来分割字符串,并返回指向被分割出的字符串的指针

char *strtok(char *str, const char *delim);

在这里插入图片描述
commandSplit:

void commandSplit(char *in, char *out[]) //  in -> usercommand
{										 //  out -> argv	int argc = 0;out[argc++] = strtok(in, SEP);while(out[argc++] = strtok(NULL, SEP)); // 我们只需要将用户输入全部读取即可// 用来测试是否能够成功分割字符串
#ifdef debugint i = 0;for(i = 0; out[i]; i++){printf("%d:%s\n", i, out[i]);} 
#endif
}

在完成分割字符串之后,我们就可以用很久之前学习的进程的知识,让它运行起来的,但是仅仅如此我们的很多命令是根本无法实现的,所以我们还需要进行一步,检查内建命令!


📜3. 检查内建命令

内建命令(也称为内置命令)是由Shell(如Bash)自身提供的命令,而不是文件系统中的某个可执行文件。这些命令通常比外部命令执行得更快,因为它们不需要通过磁盘I/O来加载外部程序,也不需要创建新的进程

我们简单实现3个内建命令:cd,exprot,echo

doBuildin:

char cwd[1024]; // 存储
char enval[1024]; // for testint lastcode = 0; // 存储上一条指令的返回信息void cd(const char *pash)                                                                                                                                                                                  
{chdir(pash);char tmp[1024];getcwd(tmp, sizeof(tmp));sprintf(cwd, "PWD=%s", tmp); // 改变当前的路径putenv(cwd); // 改变环境变量
}int doBuildin(char *argv[])
{if(strcmp(argv[0], "cd") == 0){char *pash = NULL;if(argv[1] == NULL) pash = homepath(); // 当我们cd之后不更任何输入时,//我们直接返回家目录else{pash = argv[1];}cd(pash);return 1;}else if(strcmp(argv[0], "exprot") == 0){if(argv[1] == NULL) return 1;strcpy(enval, argv[1]);putenv(enval); // 用于增加环境变量内容return 1;}else if(strcmp(argv[0], "echo") == 0){if(argv[1] == NULL){printf("\n");return 1;}if(*(argv[1]) == '$' && strlen(argv[1]) > 1){char *val = argv[1]+1; // argv[1]为'$',argv[1]+1则为'$'后的字符if(strcmp(val, "?") == 0){// lastcode 存储上一条指令的返回信息,初始状态为0// 当程序执行时,出错时会改变lastcode的值,打印后,重新赋值为0printf("%d\n", lastcode);lastcode = 0;}else // '$'后不跟"?",则获取环境变量{const char *enval = getenv(val);if(enval) printf("%s\n", enval);else printf("\n");}return 1;}else // 直接输出字符{printf("%s\n",argv[1]);return 1;}}return 0;
}

在内建命令的检查中,我们又会用到一些函数:

  • getcwd:用于获取当前工作目录的绝对路径
  • chdir:用于改变当前工作目录
    chdir命令通常通过cd命令来实现,因为cd是chdir的别名,两者具有相同的功能。不过,在编程语言中(如C、PHP等),chdir则是一个具体的函数,用于在程序中动态改变当前工作目录
  • sprintf:用于将格式化的数据写入字符数组中
  • putenv:用于改变或增加环境变量内容的函数

📝4. 执行程序

在完成前面的铺垫之后,我们就可以创建进程来实现我们的程序了,在之前学习进程时,我们需要用到3个头文件

#include<unistd.h>
#include<sys/types.h>
#include<wait.h>

execute:

int execute(char *argv[])
{pid_t id = fork();if(id < 0) return -1;else if(id == 0){// 子进程execvp(argv[0], argv);exit(1);}else{// 父进程int status = 0;pid_t rid = waitpid(id, &status, 0);if(rid > 0){// 将现在状态提供个lastcodelastcode = WEXITSTATUS(status);}}return 0;                                                                                                                                                                                              
}

在这里插入图片描述

execvp是C语言(特别是在Unix和类Unix系统,如Linux)中用于执行外部程序的一个系统调用函数。这个函数通过查找环境变量(特别是PATH环境变量)来定位并执行指定的文件,同时将参数列表传递给该程序

在这里插入图片描述

以上就是对一些基本操作的封装,让我们看一下主函数main

main:

int main()
{// shell是一个一直循环的程序while(1){char usercommand[NUM];char *argv[SIZE];// 获取输入int n = getUserCommand(usercommand, sizeof(usercommand));// 当获取输入时,返回一个小于0的数时,我们直接continue返回,不用往下继续走了if(n <= 0) continue;// 分割字符串 (strtok)commandSplit(usercommand, argv);// check Buildinn = doBuildin(argv);// 一般内建命令不会出错if(n) continue;// 执行命令execute(argv);}   return 0;
}

在这里插入图片描述

注意:当我们输入指令输出字符时,Backspace键是不能直接删除的,我们需要Ctrl + Backspace 组合键用于删除光标前的一个单词


我们来思考函数和进程之间的相似性

exec/exit就像call/return
一个C程序有很多函数组成。一个函数可以调用另外一个函数,同时传递给它一些参数。被调用的函数执行一定的操作,然后返回一个值。每个函数都有他的局部变量,不同的函数通过call/return系统进行通信

这种通过参数和返回值在拥有私有数据的函数间通信的模式是结构化程序设计的基础。Linux鼓励将这种应用于程序之内的模式扩展到程序之间

在这里插入图片描述

一个C程序可以fork/exec另一个程序,并传给它一些参数。这个被调用的程序执行一定的操作,然后通过exit(n)来返回值。调用它的进程可以通过wait(&ret)来获取exit的返回值


📖5. 总结

在探索和学习编写Linux中简易shell脚本的旅程即将告一段落之际,我们不禁回望这段充满挑战与收获的时光。Shell脚本,作为Linux系统中不可或缺的一部分,以其强大的自动化能力和灵活的语法结构,成为了系统管理员、开发者以及任何希望提高工作效率的用户的得力助手

通过本文的引导,我们一同踏入了shell脚本编程的大门,从基础的语法规则到实际应用中的小技巧,每一步都见证了知识的积累与能力的提升。我们学会了如何创建和运行脚本,掌握了变量、条件判断、循环控制等核心概念,更通过实例演练,亲身体验了脚本在文件处理、自动化任务等方面的广泛应用

最后,让我们以一颗永不停歇的求知之心,继续前行在Linux与shell脚本编程的广阔天地中,共同书写属于我们的精彩篇章!

在这里插入图片描述

希望本文能够为你提供有益的参考和启示,让我们一起在编程的道路上不断前行!
谢谢大家支持本篇到这里就结束了,祝大家天天开心!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/54069.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

俄罗斯方块——C语言实践(Dev-Cpp)

目录 1、创建项目(尽量不使用中文路径) 2、项目复制 3、项目配置 ​1、调整编译器 2、在配置窗口选择参数标签 3、添加头文件路径和库文件路径 4、代码实现 4.1、main.c 4.2、draw.h 4.3、draw.c 4.4、shape.h 4.5、shape.c 4.6、board.h 4.7、board.c 4.8、cont…

iOS - TestFlight使用

做的项目需要给外部人员演示&#xff0c;但是不方便获取对方设备的UDID&#xff0c;于是采用TestFlight 的方式邀请外部测试人员的方式给对方安装测试App&#xff0c;如果方便获取对方设备的UDID&#xff0c;可以使用蒲公英 1.在Xcode中Archive完成后上传App Store Connect之前…

数据结构(2):LinkedList和链表[2]

我们在上一篇文章中着重讨论了单链表的实现。其中我们要注意单链表进行遍历时一步一步走的思想。那么这篇文章我们将继续讨论链表的更多内容&#xff0c;那就让我们开始吧。 1.经典单链表算法题 我们将通过几个经典的题对单链表进行进一步的认识。 (1)反转链表 206. 反转链…

clip论文阅读(Learning Transferable Visual Models From Natural Language Supervision)

目录 摘要训练pre-train model的过程将pre-train model应用于下游任务应用&#xff08;待更新&#xff09; 论文/项目地址&#xff1a;https://github.com/OpenAI/CLIP 提供了clip的pre-trained model的权重&#xff0c;也可安装使用pre-trained model 摘要 使用标签标注的图…

C++过生日(我给我自己做的生日礼物)

&#x1f680;欢迎互三&#x1f449;&#xff1a;程序猿方梓燚 &#x1f48e;&#x1f48e; &#x1f680;关注博主&#xff0c;后期持续更新系列文章 &#x1f680;如果有错误感谢请大家批评指出&#xff0c;及时修改 &#x1f680;感谢大家点赞&#x1f44d;收藏⭐评论✍ 引言…

电脑开机速度慢怎么解决?

电脑开机速度慢怎么解决&#xff1f;电脑开机速度慢的原因可以是多方面的&#xff0c;以下是一些常见的原因&#xff1a; 启动项过多&#xff1a; 许多软件在系统启动时会自动启动&#xff0c;导致启动项过多&#xff0c;从而延长了开机时间。过时的驱动程序&#xff1a; 设备…

wpf触发与模板的使用示例:批量生产工具

批量生产工具 <Window x:Class"WpfM20UpdateFW.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d"http://schemas.microsoft.com/expressio…

2024年Java后端学习路线

思维导图&#xff1a; 必备知识&#xff1a; Java基础 JavaWeb 数据库&#xff1a;MySql&#xff0c;Redis 开发中间件&#xff1a;Maven &#xff0c;Git &#xff0c;Docker&#xff0c;RabbitMQ 开发框架&#xff1a;SSM&#xff0c;spring boot&#xff0c;mybatis-plus、s…

十一、DMSP/OLS、NPP/VIIRS等夜间灯光数据之GDP空间化——新方法理论介绍

一、前言 之前的空间理论方法是将第一产业GDP和第二、三产业GDP分开,第一产业GDP和耕地面积进行反演,第二、三产业GDP和夜间灯光指数进行拟合,或者干脆不划分产业,就是第一、二、三产业gdp数据和夜间灯指数拟合。之前给大家介绍都是这种,那么现在很多文献提出一种新的做法…

JVM 运行时数据区域

目录 前言 程序计数器 java虚拟机栈 本地方法栈 java堆 方法区 运行时常量池 前言 首先, java程序在被加载在内存中运行的时候, 会把他自己管理的内存划分为若干个不同的数据区域, 就比如你是一个你是一个快递员, 一堆快递过来需要你分拣, 这个时候, 你就需要根据投放的目…

[产品管理-23]:NPDP新产品开发 - 21 - 产品创新中的市场调研 - 市场调研对创新产品开发的意义

目录 前言&#xff1a; 一、市场调研概述 1.1 客户与市场的区别 1、定义与范围 2、关注焦点 3、作用与影响 4、总结 1.2 销售与市场的区别 1、对象与范围 2、工作方式 3、工作内容 4、目标 5、考核标准 6、在企业运营中的角色 1.3 什么是市场调研 1、市场调研的…

COMDEL电源维修CLX2500康戴尔射频电源维修

美国COMDEL射频电源维修常见型号包括&#xff1a;CLX2750&#xff1b;CLX2500&#xff1b;CLX-600H&#xff1b;CX600AS&#xff1b;CX-5000S&#xff1b;CX-3500S&#xff1b;CX-2500S&#xff1b;CV500&#xff1b;CDX2000等。 Comdel成立于1966年&#xff0c;总部设在马萨诸…

Golang | Leetcode Golang题解之第406题根据身高重建队列

题目&#xff1a; 题解&#xff1a; func reconstructQueue(people [][]int) (ans [][]int) {sort.Slice(people, func(i, j int) bool {a, b : people[i], people[j]return a[0] > b[0] || a[0] b[0] && a[1] < b[1]})for _, person : range people {idx : pe…

maya-vray渲染蒙版

要用一个叫vrayMulWrapper的材质球&#xff0c;把alpha Conterbution调到-1&#xff0c;勾选matte surface启用蒙版物体。

【GESP】C++一级练习BCQM3005,基本输出语句printf

一道基础练习题&#xff0c;练习基本输出语句printf。 BCQM3005 题目要求 描述 输出表达式1234∗5678的结果。 输入 无 输出 1234∗56787006652 输入样例 无 输出样例 1234 * 5678 7006652 全文详见个人独立博客&#xff1a;https://www.coderli.com/gesp-1-bcqm3005/ 【…

在树莓派上构建和部署 Node.js 项目

探索在Raspberry Pi上构建和部署Node.js项目的最佳实践。通过我们的专业提示和技巧&#xff0c;克服常见挑战&#xff0c;使您的项目顺利运行。 去年圣诞节&#xff0c;我收到了一份极其令人着迷的礼物&#xff0c;它占据了我许多周末的时间&#xff0c;甚至让我夜不能寐。它就…

零基础考过软考信息系统项目管理师经验分享

选择适合的课程&#xff1a;如果你是零基础&#xff0c;建议找一些专门针对新手的课程&#xff0c;讲解通俗易懂。 刷题至关重要&#xff1a;软考的题库很庞大&#xff0c;多做题是必须的。 做好笔记和复习&#xff1a;上课时要做好笔记&#xff0c;课后及时复习&#xff0c;…

网络安全学习(二)初识kali

kali有两种界面模式&#xff0c;为了更好的适应windows用户&#xff0c;需要操作一下。 先更新一下kali&#xff0c;执行命令 sudo apt-get update 然后换界面 sudo apt install kali-desktop-gnome 等待&#xff0c;出现如下界面时&#xff0c;选择gdm3&#xff08;键盘&a…

Mybatis中Like模糊查询三种处理方式

目录 Mybatis中Like模糊查询三种处理方式 1.通过单引号拼接${} 1&#xff09;mapper接口 2&#xff09;Mapper.xml 3&#xff09;测试代码 4) 测试结果 2.通过concat()函数拼接(个人推荐使用这种) 1&#xff09;mapper接口 2&#xff09;Mapper.xml 3&#xff09;测试代码 4) 测…

Requests-HTML模块怎样安装和使用?

要安装和使用Requests-HTML模块&#xff0c;您可以按照以下步骤进行操作&#xff1a; 打开命令行界面&#xff08;如Windows的命令提示符或Mac的终端&#xff09;。 使用pip命令安装Requests-HTML模块。在命令行中输入以下命令并按回车键执行&#xff1a; pip install request…