1Shell 编程基础与脚本规范
1.1编程基础
1.1.1程序组成
程序:算法+数据结构
- 数据:是程序的核心
- 算法:处理数据的方式
- 数据结构:数据在计算机中的类型和组织方式
1.2 shell 脚本的用途和应用场景用途:
- 自动化常用命令
- 执行系统管理和故障排除
- 创建简单的应用程序
- 处理文本或文件
应用场景:
- 重复性操作
- 交互性任务
- 批量事务处理
- 服务运行状态监控
- 定时任务执行
1.3常见的shell解释器
Linux系统中的Shell 是一个特殊的应用程序,它介于操作系统内核与用户之间,充当一个”命令解释器“的角色,负责接收用户输入的操作指令(命令)并进行解释,将需要执行的操作传递给内核执行,并输出结果。 它是用户使用 Linux 的桥梁。
Shell 既是一种命令语言,又是一种程序设计语言。
常见的 Shell 解释器程序有很多种,使用不同的 Shell 时,其内部指令、命令行提示符等方面会存在一些区别。通过/etc/shell 文件可以了解当前系统所支持的 Shell 脚本种类。
linux 中常见的shell
- bash:基于GUN的框架下发展的shell
- csh:类似C语言的shell
- tcsh:整合了csh 提供了更多功能
- sh:已经被bash替换
- nologin:让用户无法登录
bash(/bin/bash)是目前大多数 Linux 版本采用的默认 shell。
bash特性:
- 支持快捷键:比如 ctrl+c 强制终止进程 、 ctrl+l 清屏 、Tab补齐 等等。
- 支持查看历史命令(history)
- 支持别名(alias)
- 标准输入和标准输出的重定向
- 管道符
- 文件名通配机制
- 支持命令hash
- 支持变量
这些功能都是bash特有的,其他shell环境没有这些功能,或者说没有这么全,所以bash取代sh成为了Linux的默认shell。
2 shell脚本基本结构
2.1 脚本的构成:
- 脚本申明(申明解释器) :第一行开头 “#!/bin/bash”,表示此行以下的代码语句是通过/bin/bash程序来执行。还有其他类型的解释器,比如 #/usr/bin/python、#!/usr/bin/expect 。
- 注释信息: 以“#”开头的语句表示为注释信息,被注释的语句在脚本运行时不会被执行。
- 可执行语句: 如echo命令,用于输出“ ”之间的字符串。
2.2 shell 脚本规范
脚本名称规范:文件名以.sh结尾,方便识别。
脚本代码开头约定:
- 第一行一般为调用使用的语言
- 程序名,避免更改文件名后无法找到正确的文件
- 版本号
- 更改后的时间
- 作者相关信息
- 该程序的作用,及注意事项
- 最后是各版本的更新简要说明
2.3 脚本的执行方式
1、指定路径去执行文件,文件需要有执行权限。
- 绝对路径。如:/root/hello.sh
- 相对路径。如:./hello.sh
2、指定解释器去执行(bash 脚本名),不需要执行权限。
- bash 脚本名。如:bash hello.sh
- “source 脚本名” 或 “ . 脚本名”。 //不会启动子shell环境
注意:
- 执行脚本时的shell环境:
- source和 . 执行脚本时,会在当前shell环境中执行脚本。
- bash、绝对路径、相对路径 执行脚本时,会创建一个子shell环境,并在这个子shell环境中执行脚本。
- 不建议使用source来执行脚本,可能会影响一些资源配置。
- 脚本中的空白行会被解释器忽略。
- 脚本中,除了shebang,余下所有以#开头的行,都回被视作注释行而被忽略。此即为注释行。
脚本错误调试
脚本错误类型:
- 命令错误: 命令错误不会影响接下来的命令,之后的命令会继续执行。
- 语法错误: 会导致后续的命令不执行。造成脚本中一部分命令已执行,一部分未执行。
- 逻辑错误: 执行后的效果不是自己想要的。需要自己去排查。
bash -n 脚本名称 //只检查语法错误,不真正执行脚本。定位的错误行可能不准确。
bash -x 脚本名称 //显示每个命令的执行过程,方便发现逻辑错误
重定向
重定向的意思就是,不通过标准输出到默认屏幕上,而是输出到你指定的位置。
输入重定向
符号 | 作用 |
---|---|
命令 < 文件 | 从指定的文件读取文件,而不是从键盘输入 |
命令 << 分界符 | 从标准输入中读入,直到遇见分界符才停止 |
命令 < 文件1 > 文件2 | 将文件1 作为命令的标准输入并将标准输出到文件 |
输出重定向
符号 | 作用 |
---|---|
命令 > 文件 | 将标准输出结果保存到指定的文件中(覆盖原有内容) |
命令 >> 文件 | 将标准输出结果追加到指定的文件尾部 |
命令 2> 文件 | 将错误输出信息保存到指定文件中(覆盖原有内容) |
命令 2>> 文件 | 将错误输出信息追加到指定文件尾部 |
命令 > 文件 2>&1 | 混合输出,将标准输出与错误输出保存到文同一个文件中 |
命令 2> 文件 1>&2 | 同上 |
命令 &> 文件 | 同上 |
命令 >& 文件 | 同上 |
管道符
管道符的作用是连接两个命令,将第一个命令的标准输出作为第二个命令的标准输入。 同一行命令中可以使用多个管道符。
linux下的管道符使用"竖杠"表示:| 。
格式:命令A | 命令B
Shell 脚本中的变量
变量概述
变量即在程序运行过程中它的值是允许改变的量。
变量是用一串固定的字符串去表示不固定的内容。
变量表示命令的内存空间,将数据放在内存空间中,通过变量名引用,获取数据。
变量的作用:
用来存放系统和用户需要使用的特定参数(值)。
-
变量名:使用固定的名称,由系统预设或用户定义。
-
变量值:能够根据用户设置、系统环境的变化而变化。
变量类型
变量赋值: 变量名=变量值(name=value)
bash变量类型:
1、根据变量的生效范围可分为:
-
环境变量(全局变量):全局生效,在任何bash环境中都可以识别。
-
局部变量:生效范围为当前shell进程。对当前shell之外的其它shell进程,包括当前shell的子shell进程均无效。
-
本地变量:生效范围为当前shell进程中某代码片断,通常指函数。 只能在定义它们的函数/脚本内部中使用。
2、根据是否由系统定义可分为:
- 内置变量:由系统维护,用于设置工作环境。如PS1,PATH,HISTSIZE, HOSTNAME,USER。
- 自定义变量:由用户自己定义、修改和使用。
3、特殊变量:
-
环境变量(全局变量):全局生效,在任何bash环境中都可以识别。:
-
只读变量:read only, 只可以读取不可以更改 。
-
位置变量: 通过命令行给脚本传递参数 。n(n是正整数),n(n是正整数),n(n是正整数),{10} 要加花括号。
-
预定义变量:用来保持脚本程序的执行信息。
- $0 :当前脚本名称(如果是软链接,显示当前软链接文件名)。
- $? :查看上一次命令的执行状态,返回0为正常,非0为错误。
- $* :返回所有参数,当整体返回。
- $@ :返回所有参数,当个体返回。
- $# :获取当前shell命令行中参数的总个数。
- $_ :在此之前执行的命令或脚本的最后一个参数。
$$
:获取当前进程的PID。
变量的命名要求
- 区分大小写
- 不能使程序中的保留字和内置变量名称:如:if, for,hostname。
- 只能使用数字、字母及下划线,且不能以数字开头,注意:不支持短横线 “ - ”,和主机名相反。
- 使用英文时,尽量使用词义通俗易懂,如PATH。
- 大驼峰 StudentFirstName
- 小驼峰 studentFirstName
- 下划线 student_name
变量作用范围
默认情况下,新定义的变量只在当前的shell环境中有效,因此称为局部变量,当进入子程序或新的shell环境中,局部变量将无法再起作用。
可以通过内部命令export将指定的变量为全局变量(即环境变量),使用户定义的变量在所有子shell环境中可以继续使用。
方法:
-
法一,设置全局变量:
- export name=value
- 或 declare -x name=value
-
法二,将局部变量转变为全局变量:export name
环境变量
系统内置环境变量
1、由系统提前创建,用来设置用户的工作环境。
2、使用env命令查看所有环境变量:
需要记住的常用环境变量:
- $PATH 表示可执行文件的默认路径
- $USER 表示用户名称
- $HOME 表示用户的宿主目录
- $LANG 表示语言和字符集
- $PWD 表示当前所在工作目录
环境变量的特性:
- 可以使子进程(包括孙子进程)继承父进程的变量,但是无法让父进程使用子进程的变量。
- 一旦子进程修改从父进程继承的变量,将会新的值传递给孙子进程。
- 一般只在系统配置文件中使用,在脚本中较少使用。
环境变量的配置文件
自定义的环境变量只是临时生效,退出系统后就会失效。需要将自定义的环境变量放入配置文件中,才会永久生效。配置文件可以用来长期变更或设置环境变量。
全局配置文件:
- /etc/profile,修改此文件会作用于所有用户。
用户独立的配置文件:
- ~/.bash_profile,修改这个文件只作用于当前用户。
只读变量
我们在定义shell变量时,默认定义的变量是可以被修改的,但有一种变量是不能修改的,就是只读变量。
只读变量只能被赋值一次。只读变量在取得初始值之后,只能进行读取操作,不能重新赋值或删除。
定义变量:
- 第一步:name=value # 定义一个变量
- 第二步:readonly name # 使用 readonly 来修饰该变量 ,表明只读
位置变量
位置变量也称为位置参数。
使用$n表示,n为数字序列号,且必须为整数。
如:$1
、$2
、…、$9
、${10}
、${11}
。
两位数需要加花括号{},不然$10
会被识别为:$1
和0 。
预定义变量
预定义变量是系统定义好的变量,用来保持脚本程序的执行信息。
可以直接使用这些变量,不能直接为这些变量赋值
变量 | 作用 |
---|---|
$? | 表示前一条命令执行后的返回状态。返回值为 0 表示执行正确,返回任何非 0值均表示执行出现异常。 |
$* | 表示所有位置参数的内容。当整体返回。(将输入的参数作为一个单词) |
$@ | 表示所有位置参数的内容。当个体返回。可作为数组。(将参数内容分割成n份,每份作为独立个体) |
$# | 表示命令行中位置参数的总个数。 |
$0 | 表示当前脚本名称(如果是软链接,显示当前软链接文件名)。 |
$_ | 表示在此之前执行的命令或脚本的最后一个参数。 |
$$ | 获取当前进程的PID。 |
$*
和 $@
的区别:
- 不加双引号的话,
$*
和$@
都代表脚本后面跟的所有参数。 - 加上双引号的话,"
$*
"会把所有参数当作一个整体去看待(此时参数数量为1),"$@
"会把脚本后面跟的每一个参数当作一个个体去看待
read 从键盘输入内容为变量赋值
read可以从键盘输入变量值,即将键盘输入的内容变成变量值。
格式: read 变量名
常用选项:
- -p:后面跟提示信息,即在输入前打印提示信息。
- -s:安静模式,在输入字符时不再屏幕上显示,例如密码。
- -t:后面跟秒数,定义输入字符的等待时间。
- -n:后跟一个数字,定义输入文本的长度。
- -a:后跟一个变量,该变量会被认为是个数组。
四个配置文件的作用
1、/etc/profile 文件
系统级的配置文件,开机时系统自动加载该文件中的配置。
系统每次启动都会自动执行/etc/profile 文件里的命令,这个文件是对全局有效的(所有的shell环境和用户)。
2、~/.bash_ profile 文件
不同用户登录系统时,会自动执行自己家目录中的~/ .bash_ profile 文件中的命令,之后自动执行~/ . bashrc。(因为~/ .bash_ profile文件中规定了,如果~/ . bashrc是个文件,则执行该文件)。
3、~/.bashrc 文件
当前用户每切换一个shell环境都会自动加载~/ . bashrc 文件中的配置 ,之后自动执行/etc/bashrc文件。(因为~/ . bashrc文件中规定了,如果/etc/bashrc是个文件,则执行该文件)。
4、/etc/bashrc 文件
/etc/bashrc针对所有用户,用户每切换一个shell环境都会自动执行。
5、/etc/skel/ 目录
/etc/skel/是用来存放新用户配置文件的目录,当我们添加新用户的时候,这个目录下的所有文件会自动被复制到新添加的用户的家目录下。这个目录下的所有文件都是隐藏文件(以.开头的文件)。
该目录中包括 .bash_logout 、.bash_profile 、.bashrc 这三个配置文件。
重点总结
1、全局变量和局部变量:
- 环境变量(全局变量):在当前shell环境和子shell环境(包括孙子shell环境)中都生效。
- 局部变量:只在当前shell环境中生效,子shell环境中无效。
2、设置、查看和删除变量的命令:
-
变量赋值:
- 方法1:变量名=变量值(name=value)
- 方法2:read 变量名, 之后从键盘输入中获取变量名的值
-
设置环境变量:export name=value 或 export name
-
设置只读变量:readonly name 。只读变量不可删除或重新赋值。
-
查看变量和变量值:
- env:查看所有环境变量。
- set:查看所有变量,包括局部变量和环境变量。
-
删除变量:unset name
3、几个符号的作用:
双引号" ":强引用,识别变量,允许通过$符号引用其他变量值。会把引号的内容当成整体来看待。
单引号' ':弱引用,不会识别变量,禁止引用其他变量值,单引号内的内容都会被认为是普通字符。
反撇号` ` 和 $() :调用命令执行后输出的结果。如果存在嵌套,不能使用反撇号。
花括号{ } :定义变量范围。
Shell中的算术运算
Shell允许在某些情况下对算术表达式进行求值,比如:let和declare 内置命令,(( ))复合命令和算术扩展。求值以固定宽度的整数进行,不检查溢出。
注意:默认情况下bash只支持整数运算,不支持小数运算。
运算表达式:
(1) expr 变量1 运算符 变量2 [运算符 变量3]...
(2) let var=算术表达式
(3) ((var=算术表达式)),和上一个等价
(4) var=$[算术表达式]
(5) var=$((算术表达式))
(6) var=$(expr arg1 arg2 arg3 ...)
(7) var= `expr arg1 arg2 arg3 ...`
(8) echo '算术表达式' | bc
运算符:
- 加法 +
- 减法 -
- 乘法 \ *
- 除法 /
- 取余 (取模)%
运算法则:
- 加法: num1 + num2
- 减法:num1 - num2
- 乘法:num1 \ * num2
- 整除:num1 / num2
- 取余(求模):num1 % num2 (求 num1 除以 num2 的余数)
运算演示
expr
expr命令是一个手工命令行计数器,用于在UNIX/LINUX下求表达式变量的值,一般用于整数值,也可用于字符串。
- 变量和运算符之间,要用空格隔开。
- 用反斜杠 \ 放在 shell 特定的字符前面;
- 对包含空格和其他特殊字符的字符串要用引号括起来
let 和 (( ))
let var=算术表达式 和 ((var=算术表达式)) 效果等同。
两者都不返回结果,需要借助echo命令。
$[ ]
和$(( ))
$[ ]
和 $(( ))
效果相同,且都支持对变量进行运算
小数运算(浮点数运算)
使用bc计算器
Bash内置了对整数运算的支持,但是并不支持浮点运算,而 bc 计算器可以很方便的进行浮点运算。
- 支持交互式和非交互式两种方式。
- 使用scale=n可以指定精度,即小数点后的位数。要写在算术表达式之前。
- 非交互式运算,使用 echo "算术表达式" | bc。 多个算术表达式之间用分号隔开,例如:echo '1+1;2+2" | bc。
使用awk命令
使用awk进行浮点数的运算,运算结果最多显示6个有效数字。
格式:awk "BEGIN{print 算术表达式}"
变量的自增和自减
简写表达式 | 等同于 |
---|---|
let i++ | i=$[$i+1] |
let i-- | i=$[$i-1] |
let i+=2 | i=$[$i+2] |
let i-=2 | i=$[$i-2] |
let i*=2 | i=$[$i*2] |
let i/=2 | i=$[$i/2] |
let i%=2 | i=$[$i%2 |
使用分号,可以在一行执行多个命令。
Linux命令并行执行,顺序执行
1、Linux并行执行命令
在命令之间采用&进行连接
示例:
run1.sh&run2.sh&run3.sh&run4.sh&run5.sh
如果不是脚本而是一个程序块,那么可以采用如下形式来进行:
{程序块内容1}&{程序块内容2}&{程序块内容3}&{程序块内容4}&{程序块内容5}
注:&放在启动参数后面表示设置此进程为后台进程
2、Linux多命令顺序执行
当我们需要一次执行多个命令的时候,命令之间需要用连接符连接,不同的连接符有不同的效果。
(1) ; 分号,没有任何逻辑关系的连接符。当多个命令用分号连接时,各命令之间的执行成功与否彼此没有任何影响,都会一条一条执行下去。
(2) || 逻辑或,当用此连接符连接多个命令时,前面的命令执行成功,则后面的命令不会执行。前面的命令执行失败,后面的命令才会执行。
(3) && 逻辑与,当用此连接符连接多个命令时,前面的命令执行成功,才会执行后面的命令,前面的命令执行失败,后面的命令不会执行,与 || 正好相反。
(4) | 管道符,当用此连接符连接多个命令时,前面命令执行的正确输出,会交给后面的命令继续处理。若前面的命令执行失败,则会报错,若后面的命令无法处理前面命令的输出,也会报错。