编写一个Shell程序的准备工作(一)(全网最详细介绍------初学者上机实验总结)

这里我们默认的前提是:已经安装好了虚拟机和可视化操作界面,这里将以一个实例带大家了解我是如何创建shell程序的过程。

图形化界面:CentOS 7

一、初识Shell

1.1、什么是 shell?

定义 :http://c.biancheng.net/view/706.html
简单概括
初衷:是一个命令解释器,使用命令行操控操作系统的内核。 但是直接操作内核没必要也不安全,创建一个程序(在linux成为shell)直接调用内核接口,安全方便。

1.2、什么是脚本?

(1) 我们会写许多的程序,把这些 程序名字 (叫做shell命令)放到一个文件里面。脚本不需要编译,通过 解释器解释 执行,所以比较慢。
(2) 比如系统定义的 ls命令,我们在控制台输入这个命令就可以查看当前目录下的所有文件。(后文会讲)
再比如我们自定义一个helloworld.c的程序,其实也是一个命令(后文会讲)

1.3、Linux中有哪些脚本解释器?

有dash和bash两种,但dash没有bash功能全面,所以通常使用bash。 可以通过命令来查看系统中的脚本解释器: ls -l /bin/*sh
在这里插入图片描述

1.4、shell命令

以 ls -l /bin/*sh 为例:

  1. ls 是常用的一个命令,它属于目录操作命令,用来列出当前目录下的文件和文件夹。ls 可以附带选项,也可以不带,不带选项的写法为:
[zjh@localhost ~]$ cd demo
[zjh@localhost demo]$ ls
abc          demo.sh    a.out         demo.txt
getsum       main.sh    readme.txt    a.sh
module.sh    log.txt    test.sh       main.c

先执行cd demo命令进入 demo 目录,这是我在自己的主目录下创建的文件夹,用来保存教学使用的各种代码和数据。

接着执行 ls 命令,它列出了 demo 目录下的所有文件,并且进行了格式对齐。

  1. 文件信息打印具有短格式选项和长格式选项,实例提供可视化参考。
    (所以说需要什么东西,以什么格式等等,就按照这样一串子写下来。不同那个字符串了直接搜索查询即可!)
[zjh@localhost demo]$ ls -l
总用量 140
-rwxrwxr-x. 1 zjh zjh 8675 42 15:01 a.out
-rwxr-xr-x. 1 zjh zjh 116 43 09:24 a.sh
-rw-rw-r--. 1 zjh zjh 44 42 16:41 check.sh
-rw-r--r--. 1 zjh zjh 399 311 17:12 demo.sh
-rw-rw-r--. 1 zjh zjh 4 48 17:56 demo.txt

如果加一个-l选项,则可以看到显示的内容明显增多了。-l是长格式(long list)的意思,也就是显示文件的详细信息。

可以看到,选项的作用是调整命令功能。如果没有选项,那么命令只能执行最基本的功能;而一旦有选项,则能执行更多功能,或者显示更加丰富的数据。


基本的概念大概了解了一下,对于shell一定充满了很多疑问,接下来尝试编写一个shell脚本并且运行感受一下。

二、体验自己创建运行shell

2.1 在CentOS文本编辑器创建一个名为 Test.sh 的文件

填写代码:

#!/bin/bash
echo "Hello World !"  #这是一条语句

解释代码内容:
第 1 行的#!是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种 Shell;后面的/bin/bash就是指明了解释器的具体位置。

第 2 行的 echo 命令用于向标准输出文件(Standard
Output,stdout,一般就是指显示器)输出文本。在.sh文件中使用命令与在终端直接输入命令的效果是一样的。

第 2 行的#及其后面的内容是注释。Shell
脚本中所有以#开头的都是注释(当然以#!开头的除外)。写脚本的时候,多写注释是非常有必要的,以方便其他人能看懂你的脚本,也方便后期自己维护时看懂自己的脚本——实际上,即便是自己写的脚本,在经过一段时间后也很容易忘记。

2.2 运行脚本

2.2.1 方式一: 将 Shell 脚本作为程序运行

Shell 脚本也是一种解释执行的程序,可以在终端直接调用(需要使用 chmod 命令给 Shell 脚本加上执行权限),如下所示:

[zjh@localhost ~]$ cd demo                #切换到 test.sh 所在的目录
[zjh@localhost demo]$ chmod +x ./Test.sh  #给脚本添加执行权限
[zjh@localhost demo]$ ./Test.sh           #执行脚本文件
Hello World !                                  #运行结果

chmod +x表示给 test.sh 增加执行权限。
./程序名字 (运行该程序的格式)

2.2.2 方式二: 将 Shell 脚本作为程序运行

你也可以直接运行 Bash 解释器,将脚本文件的名字作为参数传递给 Bash,如下所示:

[zjh@localhost ~]$ cd demo               #切换到 test.sh 所在的目录
[zjh@localhost demo]$ /bin/bash Test.sh  #使用Bash的绝对路径

Hello World !                            #运行结果

通过这种方式运行脚本,不需要在脚本文件的第一行指定解释器信息,写了也没用。


接下来进一步介绍一下:进程相关的东西,实验涉及到的知识

三、进程相关知识

Linux 中的每一个进程都有一个唯一的 ID,称为 PID,查看PID可以跟踪到目的进程。

3.1 fork(),exit(),wait(),execve()…

进程的创建是通过fork()函数完成的,

1)了解什么是fork?: https://zhuanlan.zhihu.com/p/53527981.

2)对fork有了基本的了解,更系统的学习一个fork() 的使用以及必须了解的一些函数吧!https://www.cnblogs.com/dongguolei/p/8098181.html.

3.2 execve()

1)进程的执行代码是execve()加载的
2)exec系列的系统调用是把当前程序替换成要执行的程序
3)在父进程中fork一个子进程,在子进程中调用exec函数启动新的程序。exec函数一共有六个,其中execve为内核级系统调用,其他也是(execl,execle,execlp,execv,execvp)功能一样,参数不同。

链接: https://cloud.tencent.com/developer/article/1592678.

1、execve是Linux的系统函数
2、execve在头文件unistd.h中。
3、execve的定义形式:
int execve(const char *filename, char *const argv[], char *const envp[]);
4、参数说明: const char

*filename:执行文件的完整路径。 char *const argv[]:传递给程序的完整参数列表,包括argv[0],它一般是程序的名。 char *const
envp[]:一般传递NULL,表示可变参数的结尾。

#include <stdio.h>
#include <unistd.h>
 
int main(int argc, char *args[])
{
	// 定义一个字符数组,保存程序的参数信息
	// 数组的第一个元素是程序的名称
	char *buf[] = { "/bin/ls", "-l", NULL };
	// 调用execve函数执行程序
	// 第一个参数是程序的完整路径,第二个参数是字符数组,第三个参数是NULL
	execve("/bin/ls", buf, NULL);
	return 0;
}

3.3 通常运行另一个程序,而同时保留原程序运行的方法是,fork+exec。~~~就是一个完整的进程启动故事!

(3.2的链接最后的案例有提到)

四、零散的有用知识

4.1 perror()

1)C 库函数 void perror(const char *str) 把一个描述性错误消息输出到标准错误 stderr。

2)str – 这是 C 字符串,包含了一个自定义消息,将显示在原本的错误消息之前。

3)实例参考

#include <stdio.h>
int main ()
{
   FILE *fp;
   /* 首先重命名文件 */
   rename("file.txt", "newfile.txt");

   /* 现在让我们尝试打开相同的文件 */
   fp = fopen("file.txt", "r");
   if( fp == NULL ) {
      perror("Error: ");
      return(-1);
   }
   fclose(fp);
      
   return(0);
}
运行结果
Error: : No such file or directory

在这里插入图片描述

可以发现如果只是使用这个函数的话十分简单,当我准备下一个步骤时我看到有人这么定义,我一下看不懂了,不过我认为如果了解了这段话就可以认识到这个函数背后的逻辑,于是我产生许多思考

他是如何找到对应的错误信息文本?是如何打印的呢?打印的信息保存在哪里呢?带着这些问题我继续探索…

后面将为大家解释这段话的意思

4.1.1 标准错误输出设备 stderr()

(4.1中perror概念提到了标准错误输出设备怎么理解呢)

1)stdout, stdin, stderr的中文名字分别是标准输出,标准输入和标准错误。
2)实例参考

fprintf(stdout,"Hello ");
fprintf(stderr,"World!");
运行结果
World!Hello

原因:
在默认情况下,stdout是行缓冲的,他的输出会放在一个buffer里面,只有到换行的时候,才会输出到屏幕。而stderr是无缓冲的,会直接输出,举例来说就是printf(stdout,“xxxx”) 和 printf(stdout, “xxxx\n”),前者会憋住,直到遇到新行才会一起输出。而printf(stderr, “xxxxx”),不管有么有\n,都输出。

3)区别:
stdout – 标准输出设备 (printf(“…”)) 同 stdout。
stderr – 标准错误输出设备
两者默认向屏幕输出。
但如果用转向标准输出到磁盘文件,则可看出两者区别。stdout输出到磁盘文件,stderr在屏幕。

4.1.2 errno()

参考 http://c.biancheng.net/ref/errno.html.
1) 错误代码仅仅是一个数字,并没有额外的结构,要想获取具体的错误信息,一般有两种方案:

  • 使用 perror() 将错误信息(文本)打印到标准输出设备;
  • 使用 strerror() 将错误代码转换成对应的文本信息。

2)使用
默认是0,如果发生错误,就会被改变,成为一个新的int类型数字, strerror()函数中放入这个int类型数字,就会被打印出来。

总结:perro() 之所以实现在屏幕输出错误原因,是因为error找到了错误,stderr提供输出功能

4.2 waitpid()

(前面我们了解了wait(),这里区分一下waitpid(),算是对知识的优化。)
要存在子进程或者其他的信号时候使用

pid_t waitpid(pid_t pid,int *status,int options)
在这里插入图片描述

wait()会暂时停止目前进程的执行, 直到有信号来到或子进程结束. 如果在调用wait()时子进程已经结束, 则wait()会立即返回子进程结束状态值. 子进程的结束状态值会由参数status 返回, 而子进程的进程识别码也会一快返回. 如果不在意结束状态值, 则参数 status 可以设成NULL.


终于来到了上机实验部分
下一部分见!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值