Linux操作系统shell脚本语言-第六章
一、Shell高级
1、echo 带颜色输出
1.1、echo命令介绍
`语法:
echo [-ne] [字符串]/echo [--help] [--version]
补充说明:echo会将输入的字符串送往标准输出。输出的字符串加以空白字符隔开,并在最后加上换行符
参数:
-n:不要再最后自动换行
-e:打开反斜杠ESC转义。若字符串出现以下字符,则特别加以处理,而不会将它当成一般文字输出:
\a:发出警告声
\b:删除前一个字符
\c:最后不加上换行符号
\f:换行光标仍停留在原来的位置
\n:换行且光标移至行首
\r:光标移至行首,但不换行
\t:插入tab
\v与\f相同
\\:插入\
\nnn:插入nnn(八进制)所代表的ASCII字符
-E:取消反斜杠ESC转义
-help:显示帮助
-version:显示版本信息
1.2 使用echo达到输出有颜色字体的效果
echo -e "\e[背景底色号码;字体颜色号码m 文本内容 \e[0m"
echo -e "\033[背景;字体颜色m 字符串\033[0m"`扩展:使用printf显示颜色字体
printf "\e[背景底色号码;字体颜色号码m 格式化输出符号 \e[0m" "文本内容"
显示黑色背景绿色字体
printf "\e[40;32m %s\n \e[0m" "hello world"
对应的颜色范围:
字体背景颜色范围 40-47
40:黑
41:深红
42:绿
43:黄色
44:蓝色
45:紫色
46:深绿
47:白色字基本颜色号码 30-37
30:黑
31:红
32:绿
33:黄
34:蓝色
35:紫色
36:深绿
37:白色字体高亮颜色号码 90-97
90:黑
91:红
92:绿
93:黄
94:蓝色
95:紫色
96:深绿
97:白色字背景颜色范围 40-47
40:黑
41:深红
42:绿
43:黄色
44:蓝色
45:紫色
46:深绿
47:白色
还有一些特殊的颜色
\33[0m 关闭所有属性
\33[1m 设置高亮度
\33[4m 下划线
\33[5m 闪烁
\33[7m 反显
\33[8m 消隐
\33[30m — \33[37m 设置前景色
\33[40m — \33[47m 设置背景色
\33[nA 光标上移n行
\33[nB 光标下移n行
\33[nC 光标右移n行
\33[nD 光标左移n行
\33[y;xH设置光标位置
\33[2J 清屏
\33[K 清除从光标到行尾的内容
\33[s 保存光标位置
\33[u 恢复光标位置
\33[?25l 隐藏光标
\33[?25h 显示光标
2、Shell的八大扩展功能
2.1、花括号
在shell脚本中,可以使用括号对字符串进行扩展,我们可以在一对花括号中包含一组以分号分隔的字符串或者字符串序列组成一个字符串扩展,注意最终输出结果以空格分隔,使用该扩展花括号不可以被引号引用,花括号的数量必须是偶数个
[root@localhost ~]# echo {1,5} #对字符串进行扩展
1 5
[root@localhost ~]# echo {hello,world} #对字符串进行扩展
hello world
[root@localhost ~]# echo {a..z} #对字符串序列进行扩展
a b c d e f g h i j k l m n o p q r s t u v w x y z
#字符串后面可以跟一个步长整数,默认为1或-1
[root@localhost ~]# echo {a..z..2}
a c e g i k m o q s u w y
[root@localhost ~]# echo {a..z..3}
a d g j m p s v y
[root@localhost ~]# echo {1..9..3}
1 4 7
[root@localhost ~]# echo {1..9..2}
1 3 5 7 9
[root@localhost ~]# echo "{a..z}" #花括号扩展不能使用引号
{a..z}
[root@localhost ~]# echo t{i,o}p #花括号前后都可以添加可选字符串
tip top
[root@localhost ~]# echo t{o,e{a,m}}p #花括号支持嵌套
top teap temp
#花括号批量操作
[root@localhost ~]# mkdir -p t{o,e{a,m}}p
[root@localhost ~]# touch t{o,e{a,m}}p/{a,b,c,d}e.txt
2.2、波浪号
波浪号在Shell脚本中默认代表当前用户家目录
[root@localhost /]# echo ~ #显示当前用户的家目录
/root
[root@localhost /]# echo ~/elk
/root/elk
[root@localhost /]# echo ~elk #显示特定用户的家目录,该用户必须存在
/home/elk
[root@localhost /]# echo ~+ #显示当前工作目录
/
[root@localhost /]# echo ~- #显示前一个工作目录
/root
2.3、变量替换
在Shell脚本中我们会使用 对变量进行扩展替换,变量字符可以放到花括号中,这样可以防止需要扩展的变量字符与其他不需要扩展的字符混淆,如果 对变量进行扩展替换,变量字符可以放到花括号中,这样可以防止需要扩展的变量字符与其他不需要扩展的字符混淆,如果 对变量进行扩展替换,变量字符可以放到花括号中,这样可以防止需要扩展的变量字符与其他不需要扩展的字符混淆,如果后面是位置变量且多余一个数字,必须使用{}
[root@localhost ~]# a="hello word"
[root@localhost ~]# echo $a
hello word
[root@localhost ~]# echo ${a}
hello word
[root@localhost ~]# b=a
[root@localhost ~]# echo ${b} #直接返回变量的值
a
[root@localhost ~]# echo ${!b} #间接引用a变量的值
hello word
[root@localhost ~]# c=b
[root@localhost ~]# echo ${!c} #尽可以实现一层简介引用
a
变量替换操作还可以测试变量是否存在及是否为空,若变量不存在或为空,则可以为变量设置一个默认值
Shell脚本支持多种形式的变量测试与替换功能,如下表所示
语法格式 | 功能描述 |
---|---|
${变量:-关键字} | 如果变量未定义或为空,则返回关键字,否则返回变量值 |
${变量:=关键字} | 如果变量未定义或为空,则将关键字赋值给变量,并返回结果,否则直接返回变量值 |
${变量:?关键字} | 如果变量未定义或为空,则通过标准错误显示包含关键字的错误信息,否则返回变量值 |
${变量:+关键字} | 如果变量未定义或为空,则直接返回空,否则返回关键字 |
[root@localhost ~]# echo $bb[root@localhost ~]# echo ${bb:-bbb}
bbb
[root@localhost ~]# echo $bb[root@localhost ~]# echo ${bb:=bbb}
bbb
[root@localhost ~]# echo $bb
bbb
此外,变量替换还有非常实用的字符串切割与掐头去尾功能
偏移量起始值为0
语法格式 | 功能描述 |
---|---|
${变量:偏移量} | 从变量的偏移量位置开始,切割截取变量的值到结尾 |
${变量:偏移量:长度} | 从变量的偏移量位置开始,切割截取特定长度的变量值 |
${变量#关键字} | 用关键字对变量进行模式匹配,从左到右删除匹配到的内容,关键字可以用*表示,使用#匹配时为最短匹配 |
${变量##关键字} | 用关键字对变量进行模式匹配,从左到右删除匹配到的内容,关键字可以用*表示,使用##匹配时为最长匹配 |
${变量%关键字} | 用关键字对变量进行模式匹配,从右到左删除匹配到的内容,关键字可以用*表示,使用%匹配时为最短匹配 |
${变量%%关键字} | 用关键字对变量进行模式匹配,从右到左删除匹配到的内容,关键字可以用*表示,使用%%匹配时为最长匹配 |
这几种变量替换方式,都不会改变原变量的值
#!/bin/bash
#!/bin/bash
home="hello world linux java spring"
echo ${home:2}
#llo world linux java spring
echo ${home:2:5}
#llo w
echo ${home#he}
# lo world linux java spring
echo ${home#*ja}
# va spring
echo ${home##*r}
# ing
echo ${home%ing}
# hello world linux java spr
echo ${home%i*}
# hello world linux java spr
echo ${home%%i*}
# hello world l
变量内容的统计与替换
语法格式 | 功能描述 |
---|---|
${!前缀字符*} | 查找以指定字符开头的变量名称,变量名之间使用IFS分隔 |
${!前缀字符@} | 查找已指定字符开头的变量名称,@在引号中将被扩展为独立的单词 |
${!数组名称[*]]} | 列出数组中所有下标,*在引号中被扩展为一个整体 |
${!数组名称[@]]} | 列出数组中所有下标,@在引号中被扩展为独立的单词 |
${#变量} | 统计变量的长度,变量可以是数组 |
${变量/旧字符串/新字符串} | 将变量中的旧字符串替换为新字符串,仅替换第一个 |
${变量//旧字符串/新字符串} | 将变量中的旧字符串替换为新字符串,替换所有 |
${变量^匹配字符} | 将变量中的小写替换为大写,仅替换第一个 |
${变量^^匹配字符} | 将变量中的小写替换为大写,替换所有 |
${变量,匹配字符} | 将变量中的大写替换为小写,仅替换第一个 |
${变量,匹配字符} | 将变量中的大写替换为小写,替换所有 |
2.4、命令替换
#我们可以通过$(命令)或`命令`方式实现替换[root@localhost /]# echo -e "$(date +%Y-%m-%d;uptime)"
2021-07-1423:47:56 up 29 days, 4:12, 1 user, load average: 0.36, 0.18, 0.14
[root@localhost /]# echo "系统登录人数:$(who | wc -l)"
系统登录人数:1
[root@localhost /]# echo "系统登录人数:`who | wc -l`"
系统登录人数:1
2.5、算数替换
通过算数替换阔可以进行算数计算并返回计算结果,算数替换扩展的格式为$(())
,也可以使用$[]
的方式,算数扩展支持嵌套
[root@localhost /]# echo $((i++))
1
[root@localhost /]# echo $((++i))
3
[root@localhost /]# echo $((--i))
2
[root@localhost /]# echo $((18%5)) #取余
3
[root@localhost /]# echo $((2**3)) #幂运算
8
[root@localhost /]# echo $((2>3))
0
[root@localhost /]# echo $((2<3))
1
[root@localhost /]# echo $((2!=3))
1
2.6、进程替换
进程替换将进程的返回结果通过命令管道的方式传递给另一个进程
语法格式为:<(命令)或者>(命令)
一旦使用了进程替换功能,系统将会在/dev/fd目录下创建文件描述符文件,通过该文件描述符将进程的输出结果传递给其他进程
Linux系统中可以使用管道将前一个命令输出重定向到文件,但是一旦使用了重定向输出到文件,输出结果无法在屏幕上显示
[root@localhost /]# ls /etc/*.conf > ~/conf.log
[root@localhost /]# cat ~/conf.log
/etc/asound.conf
...后续内容省略...
使用tee
命令既可以重定向到文件,又可以在屏幕上显示输出结果
[root@localhost /]# ls /etc/*.conf | tee ~/conf.log
/etc/dracut.conf
...后续内容省略...
[root@localhost /]# cat ~/conf.log
/etc/dracut.conf
...后续内容省略...
2.7、单词切割
单词切割又叫做分词,Shell使用IFS变量进行分词处理。如果没有自定义IFS变量,默认为空格,Tab制表符,换行符
[root@localhost ~]# read -p "输入:" x y z
输入:1 2 3
[root@localhost ~]# echo $x
1
[root@localhost ~]# echo $y
2
[root@localhost ~]# echo $z
3
#自定义IFS变量的值
[root@localhost ~]# IFS=$',' read -p "输入:" x y z
输入:4,5,6
[root@localhost ~]# echo $x
4
[root@localhost ~]# echo $y
5
[root@localhost ~]# echo $z
6
2.8、路径替换
除非使用set -f
禁用路径替换,否则bash会在路径和文件名中搜索*、?和[符号,如果找到了这些符号则进行模式匹配的替换。
使用shopt
命令时开启了nocaseglob
选项,则bash的进行模式匹配时不区分大小写,默认区分大小写。
此外还可以开启extglob
选项,让bash支持扩展通配符。
shopt
命令-s选项可以开启特定的Shell属性,-u选项可以关闭特定的Shell属性
[root@localhost shopt]# touch {a,A,b,B}.txt
[root@localhost shopt]# ls a.txt
a.txt
[root@localhost shopt]# shopt -s nocaseglob
[root@localhost shopt]# shopt nocaseglob
nocaseglob on
[root@localhost shopt]# ls B*
b.txt B.txt
[root@localhost shopt]# ls a*
a.txt A.txt
[root@localhost shopt]# shopt -u nocaseglob
[root@localhost shopt]# ls a*
a.txt
[root@localhost shopt]# shopt -s extglob
[root@localhost shopt]# ls !(a.txt|b.txt)
A.txt B.txt
[root@localhost shopt]# shopt -u extglob
[root@localhost shopt]# ls !(a.txt|b.txt)
-bash: !: event not found
basename
和dirname
:
basename
:可以获取一个路径中的文件名
dirname
:仅保留路径,删除文件名
3、Shell中的expect
3.1、expect介绍
expect 是由Don Libes基于Tcl( Tool Command Language )语言开发的,主要应用于自动化交互式操作的场景,借助Expect处理交互的命令,可以将交互过程如:ssh登录,ftp登录等写在一个脚本上,使之自动化完成。尤其适用于需要对多台服务器执行相同操作的环境中,可以大大提高系统管理人员的工作效率。
`expect 的安装
yum -y install expect
3.2、expect命令
`语法格式:
expect [选项] [ -c cmds ] [ [ -[f|b] ] cmdfile ] [ args ]
常用选项
-c :从命令行执行expect脚本,默认expect是交互地执行的
-d :可以输出调试信息expect中相关的命令
spawn:启动新的进程
send:用于向进程发送字符串
expect:从进程接收字符串
interacr:允许用户交互
exp_continue:匹配多个字符串在执行动作后加此命令
set timeout 30:设置超时时间timeout为30s,expect命令阻塞超时时会自动往下继续执行。将timeout配置为-1时表示expect一直阻塞直到与期待的字符串匹配上才继续往下执行。超时时间timeout默认为10s。
[lindex $argv n]:可以在脚本中使用该命令获取在脚本执行时传入的第n个参数。这里argv为传入的参数,另外argv为传入的参数,另外argc表示传入参数的个数,$argv0表示脚本名字。另外我们也可以使用[lrange $argv sn en]命令获取第sn到第en个参数。
3.3、expect最常用的语法(tcl语言:模式-动作)
3.3.1、expect的单分支语法
[root@localhost ~]# expect
expect1.1> expect "hi" {send "say hi\n"} #捕捉用户输入的hi,然后给用户发送"say hi\n"
hi #这一行是我输入的,由于被我上面定义的语句捕捉到了,下面一行的输出信息就是我之前自定义的
say hi
expect1.2> # 如果不想使用该程序了,可以通过输入"exit"或者ctrl+D来正常退出交互式界面
3.3.2、expect的多分支语法
[root@localhost ~]# expect
expect1.1> expect "hi" {send "say hi\n"} "bye" {send "byebye\n"}
bye
byebye
3.4、通过实际脚本来理解expect
#!/usr/bin/expect
# 使用expect来解释该脚本
set timeout 30
# 设置超时时间,单位为秒,默认情况下是10s
set host "njdx01.91vps1.com"
#设置远程连接的主机
set port "31026"
#设置SSH端口变量
set username "root"
set pass "Gizakps@1289"
#设置密码
spawn ssh $username@$host -p$port
# spawn:是进入expect环境后才可以执行的expect内部命令,如果没有装expect或者直接在默认的SHELL下执行是找不到spawn命令的。
# 它主要的功能是给ssh运行进程加个壳,用来传递交互指令
expect "*password*" {send "$pass\r"}
#这里的expect也是expect的一个内部命令,这个命令的意思是判断上次输出结果里是否包含"password"的字符串,如果有则立即返回;否则就等待一段时间后返回
interact
#执行完后保持交互状态,把控制权交给控制台,这个时候就可以手工操作了`特别提醒:该脚本不能通过bash 脚本名来运行,而是需要通过
#chmod +x 脚本名
#./脚本名
在上述的示例中,设计到expect中一个非常重要的概念–模式-动作;即上述expect "*password*" {send "$password\r"}
这句代码表达出来的含义。简单的说就是匹配一个模式,就执行对应的做东;匹配到password字符串,就输入密码。你可能回看到这样的代码
expect {"password"{send "$pass\r"exp_continue}eof{send "eof"} }
其中exp_continue表示循环式匹配,通常匹配之后都会退出语句,但如果有exp_continue则可以不断循环匹配,输入多条命令,简化写法。
很多时候,我们需要传递参数到脚本中,现在通过下面这段代码来看看如何在expect中使用参数:
#!/usr/bin/expect
if {$argv < 3} {puts "Usage:cmd <host> <username> <password>"exit 1
}
set timeout -1
# 表示expect一直阻塞直到与期待的字符串匹配上才继续往下运行
set host [lindex $argv 0]
set username [lindex $argv 1]
set port [lindex $argv 2]
set pass [lindex $argv 3]
spawn ssh $username@$host -p$port
expect "*password*" {send "$pass\r"}
interact# 在expect,$argc表示参数个数,而参数值存放在$argv中,比如取第一个参数就是[index $argv 0],以此类推`特别提醒:该脚本不能通过bash 脚本名来运行,而是需要通过
#chmod +x 脚本名
#./脚本名 参数1 参数2 参数3 参数4
4、Shell中的信号捕捉trap(不用了解)
Linux常用命令trap用于指定在接收信号后将要采取的动作,常见的用途是在脚本程序被中断时完成清理工作
4.1、Linux信号
信号(IPC)最初是UNIX系统响应某些状况而产生的事件,进程在接收到信号时会采取相应的行动。简单来说信号是操作系统(内核)响应某些条件而产生的一个事件(给进程)。进程之间无法通信,可以使用信号来解决。
信号是由于某些错误条件而生成的,如内存段冲突,浮点处理器错误或非法指令等。他们由 shell 和终端处理器生成来引起中断,他们还可以作为在进程中传递消息或修改行为的一种方式,明确地由一个进程发送给另外一个进程
Linux常见的信号有:
信号 | 值 | |
---|---|---|
1 | SIGHP | 挂起进程 |
2 | SIGINT | 终止进程 |
3 | SIGQUIT | 停止进程 |
9 | SIGKILL | 无条件终止进程 |
15 | SIGTERM | 尽可能终止进程 |
17 | SIGSTOP | 无条件停止进程,但不是终止进程 |
18 | SIGTSTP | 停止或暂停进程,但不终止进程 |
19 | SIGCONT | 继续运行停止的进程 |
ctrl+c
组合键会产生SIGINT
信号,ctrl+z
组合键会产生SIGTSTP
信号
kill -0 pid
不发送任何信号,但是系统会进行错误检查。该命令可以用来检查一个进程是否存在,若存在,即进程正在运行,执行echo $?
会返回0.若不存在,即进程已停止运行,执行echo $?
会返回1
4.2、trap命令
trap
命令允许你来指定Shell脚本要监视并拦截的Linux信号
`语法格式:
trap commands signal1 [signal2 signal3 ....]
# 如果当前脚本进程收到上述signals信号中的一个,就会执行commands命令trap '' 信号 # 忽略信号的操作
trap '-' 信号 # 恢复原信号的操作
trap -p # 列出自定义信号操作
trap finish EXIT #当脚本退出时.执行finish函数,当然这个"finish"这个名字可以自定义
脚本示例
#!/bin/bash
trap "echo 'Sorry~I have trapped Ctrl+c'" SIGINT
echo "This is a test script"
count=1
while [ $count -le 10 ];doecho "Loop $count"sleep 1count=$[ $count +1 ]
done
echo "The end"