初识shell编程
1.什么是shell编程
为了搞清楚这个问题,我们先搞清楚编程语言的大体分类。
1.1 编程语言大体上包括哪些?以及涉及到的分类?
编程语言总体分为机器语言、汇编语言、高级语言
- 机器语言:由于计算机内部只能接受二进制代码,因此,用二进制代码0和1描述的指令称为机器指令,全部机器指令的集合构成计算机的机器语言。
- 汇编语言:汇编语言的实质和机器语言是相同的,都是直接对硬件操作,只不过指令采用了英文缩写的标识符,更容易识别和记忆。它同样需要编程者将每一步的操作用命令的形式写出来。汇编程序的每一句指令只能对应实际操作过程中的一个很细微的动作。例如移动、自增,因此汇编程序一般比较冗长、复杂、容易出错,而且使用汇编语言编程需要有更多的计算机专业知识,但汇编语言的优点也是显而易见的,用汇编语言所能完成的操作不是一般高级语言所能够实现的,而且源程序经汇编生成的可执行文件不仅比较小,而且执行速度很快。
- 高级语言
- 高级语言是大多数编程者的选择。和汇编语言相比,它不但将许多相关的机器指令合成单条指令,并且去掉了与具体操作有关但与完成工作无关的细节,例如使用堆栈、寄存器等,这样就大大简化了程序中的指令。同时,由于省略了很多细节,编程者也就不需要有太多的专业知识。
- 高级语言主要是相对于汇编语言而言,它并不是特指某一种具体的语言,而是包括了很多编程语言,像最简单的编程语言PASCAL语言也属于高级语言。
1.2 高级语言又可以分为动态语言和静态语言,是怎么分的?
- 首先高级语言所编制的程序不能直接被计算机识别,必须经过转换才能被执行,按照转换的方式可以将它们分为两类:动态语言和静态语言
- 静态语言又叫做编译类语言,通常是一种变量强类型语言。编译是指应用源程序执行之前,就将程序源代码“翻译成”目标代码(机器语言),因此其目标程序可以脱离其语言环境独立执行(编译后生成的可执行文件,是cpu可以理解的2进制的机器码组成的),使用比较方便、效率较高。但应用程序一旦需要修改,必须先修改源代码,再重新编译生成新的目标文件(*.obj,也就是OBJ文件)才能执行,只有目标文件而没有源代码,修改很不方便。比如:c语言、c++,java,c#等。
- 动态语言又叫做解释类语言,通常是一种变量弱类型语言,依赖于解释器。执行方式类似于我们日常生活中的“同声翻译”,应用源程序一边由相应的语言解释器“翻译”成目标代码(机器语言),一边执行,因此效率比较低,而且不能生成可独立执行的可执行文件,应用程序不能脱离其解释器(想运行,必须先装上解释器,就像跟老外说话,必须有翻译在场),但这种方式比较灵活,可以动态地调整、修改应用程序。如asp、aps.net、php、shell、python、perl、lua,ruby等。
1.3 什么是面向对象,什么是面向过程,二者有什么区别?
- 面向对象:
面向对象(Object Oriented,OO)的思想对软件开发相当重要,它的概念和应用甚至已经超越了程序设计和软件开发,扩展到如数据库系统、交互式界面、应用架构、分布式系统、网络管理、CAD技术、人工智能等领域。面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物。 - 面向过程:
面向过程(Procedure Oriented)是一种以过程为中心的编程思想。这些都是以正在发生为主要目标进行编程,不同于面向对象的是谁在受影响。与面向对象明显不同的就是封装、继承、类。 - 主要是面向对象的语言有JAVA,Python,C++,面向过程的语言有Shell,C,其中perl既有面向对象的特性又有面向过程的特性。
1.4 shell编程是什么?
扯了半天,我们搞清楚了编程语言的分类以及静态和动态语言的区别。那么接下来就好说了:
首先shell是一种面向过程的动态语言,它依赖于各种各样的bash脚本解释器(解释器通常是静态语言开发的),shell编程实际上也就是一种脚本编程。
2.变量
2.1 变量的概念
从本质上来看,变量代表了一段可操作的内存,也可以认为变量是内存的符号化表示(命名的内存空间),内存是编止的存储单元。
一个程序执行起来之后就成了进程,进程所涉及到的所有数据和指令都是存在内存中的。当进程执行过程中需要临时存储一些数据的时候就需要用到变量。
2.1.1 变量类型的概念
变量类型决定了变量存储的大小和格式,该范围内的值都可以存储在内存中,运算符可应用于变量上。
2.1.2 常见的变量类型
- 字符
- 数值
- 整型
- 浮点型:11.23,1.123* 10^1 , 0.1123* 10^2
- 日期 2013/10/10
- 布尔型
- 真、假(逻辑:1+1>2)
2.2 什么是强类型语言,什么是弱类型语言?
这里是学术界的一种较为严格的说法,为了解释强类型,弱类型,我们需要先弄清楚以下概念:
- Program Errors
- trapped errors:导致程序终止执行,如除0,Java中数组越界访问
- untrapped errors:出错后继续执行,但可能出现任意行为。如C里的缓冲区溢出、Jump到错误地址。
- Forbidden Behaviours
语言设计时,可以定义一组forbidden behaviors。它必须包含所有的untrapped errors,但可能包含trapped errors。 - Well behaved、ill behaved
- well behaved:如果程序执行不可能出现forbidden behaviours,则为well behaved
- ill behaved:如果程序执行可能出现forbidden behaviours,则为ill behave
下来就可以讨论强类型和弱类型了:
- 强类型(strongly typed):如果一种语言的所有程序都是well behaved——即不可能出现forbidden behaviors,则该语言为strongly typed。
- 弱类型(weakly typed):如果一种语言可能出现ill behaved——即可能出现forbidden behaviors,则该语言为weakly typed。
强类型语言的变量在使用前,必须事先声明类型,甚至需要进行初始化。
强类型语言通常情况下:每个变量和对象都必须声明类型,他们是在编译的时候就确定类型的数据,在执行时类型不能修改。
弱类型语言的变量随用随声明,甚至不区分类型,一般默认都为字符串(11+b=11b)
2.3 强弱类型与动静态的大致关系
- 通常来说,编译型语言也就是静态语言没有额外处理逻辑,是强类型语言
- 脚本型语言(动态语言)通常有解释器来控制,可以是弱类型语言,shell就是一种弱类型语言。
- 对于大多数静态语言来说声明变量时必须确定变量类型。
- 弱类型语言在执行的时候才会确定类型。
2.4 显式转换与隐式转换
- 显示转换是强制转换,由高级类型转为低级类型,须用户在代码中明确指定
- 隐式转换是编译器自动转换的,一般同一数据类型,由低级转为高级为隐式(如int转为float或double)
如:11+c 通常11和c都会被转换成对应的ASRII码值,然后进行相加 - 编译型语言是在编译时进行转换的,解释型语言是在解释器执行的时候进行解释的
2.5 变量赋值
- 变量赋值的一般形式为:VAR_NAME=VALUE
- 不同语言赋值方式不尽相同
3.逻辑运算
3.1 基本逻辑运算符
- 与运算(&)
进行运算的两个数据,按二进制位进行“与”运算。
规则:- 0&0=0
- 0&1=0
- 1&0=0
- 1&1=1
- 或运算(|)
进行计算的两个数据,按二进制位进行“或”运算。
规则:- 0|0=0
- 0|1=1
- 1|0=1
- 1|1=1
- 非运算(!)
1取0,0取1。
规则:- ~1=0
- ~0=1
- 异或运算(^)
进行计算的两个数据,按二进制位进行“异或”运算。
规则:操作数相同为假,不同则为真- 0^0=0
- 0^1=1
- 1^0=1
- 1^1=0
操作数相同得0,不同得1。
3.2 短路运算
3.2.1 什么&&和||的短路运算?
- 表达式1&&表达式2:只要任意表达式为false,则整个表达式的运算结果为false
- 表达式1||表达式2:只要任意表达式为true,则整个表达式的运算结果为true
3.2.2 &&和||的短路运算有什么用?
如果在前面的表达式的运算过程,通过判断已经明确的知道整个表达式的结果,那么就不会进行后面表达式的运算判断。
4.shell中的变量
4.1 shell的变量分类
需要注意的是:这里是按照变量本身的作用范围,以及变量本身的表示形式来讲,来区分变量类型
4.1.1 局部变量
- 定义在脚本函数中的变量叫做局部变量
- 定义方式:local VARNAME=VALUE
- 局部变量的作用域只对当前代码段有效
4.1.2 本地变量
- 定义在bash或shell脚本中的变量,需要注意:
- 定义在bash中时候,定义后,bash的任何位置都可访问该变量
- 定义在shell脚本中,定义后,shell脚本代码在定义后的代码位置都可访问该变量
- 定义方式:VARNAME=VALUE
- 作用域:整个脚本进程或者整个bash进程
- 注意:变量一定是用在某个程序中的,是程序运行过程中用来存储数据的。而bash就是一种特殊的程序,bash运行起来后就需要使用bash中声明的变量,这个变量和bash进程有关,一旦bash进程结束,那么在bash中声明的变量也就消失了,变量是进程的变量
- 例如:从父shell进入子shell,父shell定义的变量在子shell中并不存在因为父子shell是两个进程
需要注意一点:局部变量不一定是本地变量
4.1.3 环境变量
- 定义方式为export VARNAEM=VALUE的变量就是环境变量
- 定义方式:
- export VARNAME=VALUE
- 也可分为两步:先定义成本地变量,然后再导出(export)
- 作用域:当前shell进程及其子进程
- 这里需要知道:在命令提示符下执行的任何一个脚本,在自己执行时会启动一个子shell进程
- 所以需要注意:
- 命令行中启动的脚本会继承当前shell环境变量
- 系统自行执行的脚本(非命令行启动)就需要自我定义需要的各环境变量
4.1.4 位置变量
$1,$2,… 第一个位置变量,第二个位置变量… 用来引用脚本的参数
4.1.5 特殊变量
这些变量是bash内置的,用来保存某些特殊数据的变量,也可以叫做系统变量
例如:
- $?:上一个命令的执行状态返回值
- 程序执行会有两类返回值:
- 执行结果
- 执行状态返回代码
- 0:正确执行
- 1-255:错误执行,其中1,2,127系统预留;
- 程序执行会有两类返回值:
4.2 如何使用shell变量
- 引用变量方式:${VARNAME},括号有时可省略
- 不能省略的情况:当有引起变量名混淆的情况时,必须加括号来帮助解释器识别变量的边界。
4.3 命令替换中使用变量替换
在命令替换中使用变量替换,也就是在命令替换中使用变量
- 命令替换`` 输入的是命令,拿到的是命令执行的结果
- 在字符串操作中涉及变量替换时,需注意:
- 单引号是强引用,不做变量替换
- 双引号是弱引用,可做变量替换
5.涉及到shell变量的相关操作
5.1 set用来定义变量
- 定义格式:set VARNAME
set可省略,所以set来定义变量是一个很鸡肋的用法,我们基本不用
set 可省略
5.2 撤销变量
- 可撤销环境变量和本地变量(局部变量)
- 撤销格式:unset VARNAME ps:不需要加$
5.3 查看变量
- 查看当前shell中所有变量(包括环境变量和本地变量)
- 命令:set
- 查看当前shell中的环境变量:
- 命令:printenv
- 命令:env
- 命令:export
5.4 字符串变量进行拼接
拼接示例方法如下:
ANIMALS=pig
ANIMALS=$ANIMALS:sheep
- 对shell来讲默认所有的值都是字符串,默认不能做算术运算
6.shell脚本
6.1 什么是脚本?
脚本:是命令的堆砌,按实际需要,结合命令流程控制机制实现的源程序
6.2 shell脚本的格式要求
- 在shell脚本的开头要加上#!/bin/bash
因为Linux内核只能理解ELF文件,只有文件中加上shebang:魔数magic num也就是#!/bin/bash - 想要运行的话,需要加执行权限,不想加权限的话也可以用bash SCRIPTNAME.sh的方式来执行
6.3 shell脚本的注意方法
- 单行注释:以#开头的行就是注释,会被解释器忽略
- 多行注释:
:<<EOF
注释内容...
注释内容...
注释内容...
EOF
EOF也可以使用其他符号:
:<<`
注释内容...
注释内容...
注释内容...
`
:<<!
注释内容...
注释内容...
注释内容...
!