《Linux Shell脚本攻略》学习笔记-第一章
1.1 简介
计算机可以从文本文件(称为shell脚本)中读取并执行命令。
sehll脚本不仅节省了时间,而且清楚明白地表明了所执行的操作。
bash shell变成了UNIX和Linux中既成事实的标准shell。
1.2 在终端中显示输出
1)
$表示普通用户,#表示管理员用户root。
推荐使用普通用户登录系统,然后借助sudo这类工具来运行特权命令,其执行命令的效果和root一样。
2)
shell脚本通常以shebang起始。
#!/bin/bash
其中#!位于解释器路径之前;/bin/bash是bash的解释器命令路径。
脚本中只有第一行可以使用shebang来定义解释该脚本所使用的解释器。
脚本执行的两种方式:
bash 文件名.sh | chmod 755 文件名.sh ./文件名.sh |
如果将脚本作为bash的命令行参数来运行,那么就不需要使用shebang了。
内核会读取脚本的首行并注意到shebang为#!/bin/bash,他会识别出/bin/bash并执行该脚本。
3)
~表示主目录,通常为/home/user,其中uesr是用户名。
$cmd1 ; cmd2 等同于$cmd1 $cmd2
4)
ehco是用于终端打印的最基本命令,默认情况下,echo在每次调用后会添加一个换行符。可以使用-n来禁止echo在文本的末尾追加换行符。
双引号允许shell解释字符串中出现的特殊字符,单引号不会对其做任何解释。
在使用转义序列时,需要使用echo -e“包含转义序列的字符串”这种形式。
打印彩色文本输出
5)
printf命令接受 引用文本或由空格分隔的参数。我们可以在printf中使用格式化字符串来指定字符串的宽度、左右对齐方式等。
默认情况下,printf并不会自动添加换行符。
%s,%c,%d,%f都是格式替换符,它们定义了该如何打印后续参数。
6)
文本颜色:0,30-37
彩色背景:0,40-47
1.3 使用变量与环境变量
和编译型语言不同,大多数脚本语言不要求在创建变量之前声明其类型,在变量名前面加上一个美元符号就可以访问到变量的值。
常用的惯例是在脚本中使用大写字母命名环境变量,使用驼峰命名法或者小写字母命名其他变量。
1)查看全部环境变量
使用env或者printenv命令查看当前shell中所定义的全部环境变量。
查看其他进程的环境变量,其中PID是相关进程的进程ID;使用pgrep命令获取进程ID。
2)
var = value:等号两边加上空格,表示的是等量测试关系。
var=value:等号两边没有空格,是赋值操作符。
3)
因为shell使用空白字符来分隔单词,所以我们需要加上一对花括号来告诉shell这里的变量名是fruit,而不是fruits。
4)
使用单引号时,变量不会被扩展,仍旧照原样显示;使用双引号就会显示出变量的值,如果没有定义过,则什么都不显示。
5)
环境变量UID中保存的是用户ID。它可以用于检查当前脚本时以root用户还是以普通用户的身份运行的。
root用户的UID是0。
1.4 使用函数添加环境变量
这个例子展示了如何将新的路径添加到环境变量的起始部分。
[ -d "$2" ]:判断第二个函数指定的目录是否存在
如果存在eval表达式将第一个参数所指定的变量值设置成第二个参数的值加上:,随后再跟上第一个参数的原始值。
${parameter:+expression}:如果parameter有值且不为空,则使用expression的值。
1.5 使用shell进行数学运算
bash shell使用let、(( ))和[ ]执行基本的算术操作。
工具expr和bc可以用来执行高级操作。
1)let命令可以用来直接执行基本的算术操作。当使用let时,变量名之前不需要再添加$。
2)操作符[]的使用方法和let命令一样;也可以在[]中使用$前缀。
3)也可以使用操作符(( )),出现在(( ))中的变量名之前需要加上$
4)expr命令也可以执行基本算术操作,算术符号的前后一定要有空格
以上这些方法只能用于整数运算,不支持浮点数。
5)bc是一种用于数学运算的高级使用工具,可以借助它执行读点书运算并使用一些高级函数。
根据在线手册,bc对于加减乘三种算法依据输入中的最高精度来确定输出精度,不会进行自行截断,此时scale设置无效。解决方法可以使用printf来控制输出精度;但是除法则相反,可以通过设置scale来设置精度。
其中obase是转换后的进制,ibase是转换前的进制。
1.6 玩转文件描述符与重定向
文件描述符是与输入和输出流相关联的整数。最广为人知的文件描述符是stdin、stdout和stderr。
脚本可以使用大于号将输出重定向到文件中。命令产生的文本可能是正常输出,也可能是错误信息。
正常输出和错误信息都会显示在屏幕上,我们可以分别为其指定特定的文件描述符来区分两者。
0 | stdin 标准输入 |
1 | stdout 标准输出 |
2 | stderr 标准错误 |
其中命令成功执行后退出状态为0,发生错误会返回一个非0的退出状态。
标准错误一般不会重定向到文件中
方法:
stdout作为单数据流,可以被重定向到文件或是通过管道传入其他程序,但是两者无法兼得。
有一种方法tee既可以将数据重定向到文件,又可以提供一份重定向数据的副本作为管道中后续命令的stdin。
默认情况下,tee命令将文件覆盖,但是它提供了一个-a选项,可用于追加内容。
借助小于号,我们可以像使用stdin一样从文件中读取数据。
出现在cat <<_EOF_ > cat.txt与下一行_EOF_之间的所有文本行都会被当做stdin数据。
cat.txt文件的内容显示如下:
exec命令创建全新的文件描述符。<操作符可以将文件读入stdin;>操作符用于截断模式的文件写入;>>操作符用于追加模式的文件写入。
1.7 数组与关联数组
bash支持普通数组和关联数组,前者使用整数作为数组索引,后者使用字符串作为数组索引。
1.8 别名
别名是一种便捷方式,可以为用户省去输入一长串命令序列的麻烦。
字符\可以转义命令,从而执行原本的命令。在不可信的环境下执行特权命令时,在命令前加上\来忽略可能存在的别名总是一种良好的安全实践。
1.9 采集中断信息
tput和stty是两款终端处理工具。
stty命令的选项-echo禁止将输出发送到终端,而选项echo则允许发送输出。
1.10 获取并设置日期及延时
在系统内部,日期被存储成一个整数,其取值为从1970年1月1日0时0分0秒起所流逝的秒数。这种纪元方式称为纪元时或者Unix时间。
sleep命令可以延迟脚本执行一段时间,以秒为单位。
1.11 调试脚本
运行带有-x选项的脚本可以打印出所执行的每一行命令及当前状态。
set -x | 在执行时显示参数和命令 |
set +x | 禁止调试 |
set -v | 当命令进行读取时显示输入 |
set +v | 禁止打印输入 |
export PS4='$LINENO:',调试时显示每行的行号。
调试时使用-x或者set -x选项,调试输出会被发送到stderr。
1.12 函数和参数
最大的差异在于函数参数可以在函数体中任意位置上使用,而别名只能将参数放在命令尾部。
$0是脚本名称,$n是第n个参数,"$@"被扩展成"$1""$2""$3","$*"被扩展成"$1c$2c$3",其中c是IFS的第一个字符。
函数也能像环境变量一样使用export导出,如此一来函数的作用域就可以扩展到子进程中。
1.13 将一个命令的输出发送给另一个命令
一个命令的输出可以作为另一个命令的输入,而这个命令的输出又会传递至下一个命令,以此类推。
我们使用管道连接每个过滤器,管道的操作符是 | 。
cmd1的输出传递给cmd2,cmd2的输出传递给cmd3,最终的输出会出现在显示器中或者被导入某个文件。
子shell是一个独立的进程,可以使用()操作符来定义一个子shell。
当命令在子shell中执行时,不会对当前shell造成任何影响;所有的改变仅限于该子shell内。
假设我们使用子shell或者反引号的方法将命令中的输出保存到变量中,为了保留输出的空格和换行符,必须使用双引号。(存疑)
1.14 在不按下回车键的情况下连续读入n个字符
bash中的read命令能够从键盘或者标准输入中读取文本。编程语言的大多数输入库都是从键盘读取输入,当回车键按下的时候标志着输入完毕。
但有时候没发按回车键,输入结束与否是由读取到的字符数或某个特定字符来决定的。
read -t有其特殊性,结束后会自动在命令行中显示输入的值;若是接下来还有read,可能将其作为一种输入。
1.15 持续运行命令直至执行成功
该循环执行以函数参数形式$@传入的命令。如果命令执行成功,则返回,进而退出循环。
true是作为/bin中的一个二进制文件来实现的。这意味着每执行一次之前提到的while循环,shell就不得不生成一个进程。
为了避免这种情况,可以使用shell的内建命令:,该命令的退出状态总是0。
1.16 字段分隔符与迭代器
内部字段分隔符(IFS)是shell脚本中的一个重要概念。IFS是一个环境变量,其中保存了用于分隔的字符,它是当前shell环境使用的默认定界字符串。
IFS的默认值是空白字符(换行符、制表符或者空格)
当条件为真时,while循环继续执行;当循环条件为假时,until循环继续执行。
用true作为循环条件可以产生无线循环。
1.17 比较与测试
程序中的流程控制是由比较语句和测试语句处理的。我们可以使用if、if else以及逻辑运算符来测试,用比较运算符来比较数据项。
除此之外,还有一个test命令可以用于测试。
&&:如果condition为真,则执行action
||:如果condition为假,则执行action
一定要注意运算符、比较复[ ]与操作数之间的空格,否则可能达不到想要的效果,或者报错。
-a和-o放在[[ ]]中会报错。
我们可以使用不同的条件标志测试各文件系统的相关属性。
根据ASCII值进行比较。
使用test命令可以避免使用过多的括号,增强代码的可读性。
但是test命令的执行效率要低于括号,因为test是外部程序,而括号是内建命令。
解决方法:
1.18 使用配置文件定制bash
Linux中能够放置定制脚本的文件不止一个。这些配置文件分为三类:登录时执行的、启动交互式shell时执行的、调用shell处理脚本文件时执行的。
- test命令
- 字符串比较
- 文件系统相关测试
- 算术比较
- 逻辑与、逻辑或
- if、if else使用方法
- 循环
- 实战练习
- 改变默认IFS
- 加入延时sleep
- 一种更快的做法
- 通过子shell的方式保留空格和换行符
- 利用子shell生成一个独立的进程
- 将命令序列的输出赋值给变量
- 管道
- shift命令可以将参数依次向左移动一个位置,让脚本能够使用$1来访问每一个参数。
- 读取命令的返回值
- 递归函数:在bash中,函数同样支持递归调用(可以调用自身的函数)
- 导出函数
- 函数参数可以按照位置访问。
- 函数定义和执行的方式
- 补充知识
- 使用set -v和set +v可以对脚本输入进行部分调试
- 使用set -x和set +x可以对脚本进行部分调试
- bash -x:启用shell脚本的跟踪调试功能
- 在脚本中加入延时
- date命令所支持的格式选项
- 计算一段shell脚本所使用的的执行时间
- 常用日期命令
- stty
- tput
- 转义字符 \
- 查看当前环境中定义的所有别名
- alias命令的效果只是暂时的。一旦关闭终端,所有设置过的别名都会失效。想要使别名在所有的shell中都可用,可以将其定义为放入~/.bashrc文件中。
- 删除别名:只需要将对应的定义从~/.bashrc文件中删除,或者使用unalias命令,也可以使用alias 别名=。
- 创建别名:alias
- 关联数组:当使用字符串作为索引时,关联数组要比数字索引数组更容易使用。
- 打印数组的长度
- 以列表形式打印出所有的值
- 打印出特定索引的数组元素内容
- 定义数组的方法
- 自定义文件描述符
- 重定向脚本内部的文本块
- 将文件重定向到命令
- 标准错误重定向
- 在命令行结束后立即执行echo $?,就可以打印出退出状态。
- 使用大于号将文本保存到文件中,使用双大于号将文本追加到文件中。
- 计算平方及平方根
- 进制转换
- 设定小数精度:
- 检查当前用户是否为超级用户
- 识别当前所使用的的shell:$SHELL或者$0
- 获取字符串长度:${#变量名}