第九章、shell脚本编程基础
本章内容
- 编程基础
- 脚本基本格式
- 变量
- 运算
- 条件测试
- 配置用户环境
编程基础
- 程序:指令+数据
- 程序编程风格:
过程式:以指令为中心,数据服务于指令
对象式:以数据为中心,指令服务于数据
- shell程序:提供了编程能力,解释执行
程序的执行方式
- 计算机:运行二进制指令
- 编程语言:
低级:汇编
高级:
编译:高级语言-->编译器-->目标代码
java,C#
解释:高级语言-->解释器-->机器代码
shell, perl, python
(系统后台有个解释器,直接将语言转化为机器码,放到内存中直接运行)
编程基本概念
- 编程逻辑处理方式:
顺序执行
循环执行
选择执行
- shell编程:过程式、解释执行
编程语言的基本结构:
各种系统命令的组合
数据存储:变量、数组
表达式: a + b
语句:if
shell脚本基础
- shell脚本:
包含一些命令或声明,并符合一定格式的文本文件
- 格式要求:首行shebang机制
#!/bin/bash(告诉系统是哪种shell语法)
#!/usr/bin/python
#!/usr/bin/perl
- shell脚本的用途有:
自动化常用命令(三次命令以上,推荐使用脚本)
执行系统管理和故障排除
创建简单的应用程序
处理文本或文件
创建shell脚本
- 第一步:使用文本编辑器来创建文本文件
第一行必须包括shell声明序列:#!
#!/bin/bash
添加注释
注释以#开头
- 第二步:运行脚本
给予执行权限,在命令行上指定脚本的绝对或相对路径
直接运行解释器,将脚本作为解释器程序的参数运行
脚本规范
- 脚本代码开头约定
1、第一行一般为调用使用的语言
2、程序名,避免更改文件名为无法找到正确的文件
3、版本号
4、更改后的时间
5、作者相关信息
6、该程序的作用,及注意事项
7、最后是各版本的更新简要说明
脚本的基本结构
- 脚本的基本结构
#!SHEBANG
CONFIGURATION_VARIABLES
FUNCTION_DEFINITIONS
MAIN_CODE
- shell脚本示例
#!/bin/bash
# ------------------------------------------
# Filename: hello.sh
# Revision: 1.1
# Date: 2017/11/22
# Author: sunan
# Email: 123456@163.com
# Website: www.sunanblog.com
# Description: This is the first script
# ------------------------------------------
# Copyright: 2017 sunan
# License: GPL
echo “hello shell”
脚本调试
- 检测脚本中的语法错误
bash -n /path/to/some_script(检查语法错误,不检查命令错误)
- 调试执行
bash -x /path/to/some_script(检查语法和命令错误)
变量
- 变量:命名的内存空间
数据存储方式:
字符:
数值:整型,浮点型
- 变量:变量类型
作用:
1、数据存储格式
2、参与的运算
3、表示的数据范围
类型:
字符
数值:整型、浮点型
- 强类型:变量不经过强制转换,它永远是这个数据类型,不允许隐式的类型转换。一般定义变量时必须指定类型、参与运算必须符合类型要求;调用未声明变量会产生错误
如 java,c#
- 弱类型:语言的运行时会隐式做数据类型转换。无须指定类型,默认均为字符型;参与运算会自动进行隐式类型转换;变量无须事先定义可直接调用
如:bash 不支持浮点数,php
- 变量命名法则:
1、不能使程序中的保留字:例如if, for
2、只能使用数字、字母及下划线,且不能以数字开头
3、见名知义
4、统一命名规则:小驼峰命名法和大驼峰命名法
例如:firstFunctionName( )(小驼峰命名法)
FirstClassName( ) (大驼峰命名法)
bash中变量的种类
- 根据变量的生效范围等标准划分下面变量类型:
局部变量:生效范围为当前shell进程;对当前shell之外的其它shell进程,包括当前shell的子shell进程均无效(如何查看当前的shell属于哪个bash进程,见附录1)
环境(全局)变量:生效范围为当前shell进程及其子进程
本地变量:生效范围为当前shell进程中某代码片断,通常指函数
位置变量:$1, $2, ...来表示,用于让脚本在脚本代码中调用通过命令行传递给它的参数
特殊变量:$?, $0, $*, $@, $#,$$
局部变量
- 变量赋值:name=‘value’(=两边不要加空格)
将变量赋两遍值,不是将原来空间的值覆盖,而是新开辟一个空间进行赋值,
变量指向新的空间值,而原来的空间闲置并被别的赋值覆盖。(2.66)
- 可以使用引用value:
(1) 可以是直接字串; name=“root"
(2) 变量引用:name="$USER"
(3) 命令引用:name=`COMMAND` name=$(COMMAND)
- 变量引用:${name} $name
"":弱引用,其中的变量引用会被替换为变量值
‘‘:强引用,其中的变量引用不会被替换为变量值,而保持原字符串
- 显示已定义的所有变量:set
- 删除变量:unset name
环境变量
- 变量声明、赋值:
export name=VALUE
declare -x name=VALUE
环境变量赋予的值可以传递给子进程,但子进程修改环境变量的值只能往下传递,不能影响父进程的值。(详见附录1.2)
- 变量引用:$name, ${name}(其中脚本中调用参数超过10使用${name},因为脚本中$10 =$1+0)
- 显示所有环境变量:
env
printenv
export
declare -x
- 删除变量:
unset name
- bash内建的环境变量:
?PATH
?SHELL
?USER
?UID
?HOME
?PWD
?SHLVL(查看shell嵌套深度,详见附录1.3)
?LANG
?HOSTNAME
?HISTSIZE
?_(下划线,上一条命令的参数)
只读和位置变量
- 只读变量:只能声明,但不能修改和删除
声明只读变量:
readonly name
declare -r name
查看只读变量:
readonly –p
- 位置变量:在脚本代码中调用通过命令行传递给脚本的参数
$1, $2, ...:对应第1、第2等参数,shift [n]换位置(往前移动几个位置,详见1.6)
$0: 命令本身
$*: 传递给脚本的所有参数,全部参数合为一个字符串
$@: 传递给脚本的所有参数,每个参数为独立字符串
$#: 传递给脚本的参数的个数
$@ $* 只在被双引号包起来的时候才会有差异
set -- 清空所有位置变量
(4.10)
退出状态
- 进程使用退出状态来报告成功或失败
0 代表成功,1-255代表失败
- $? 变量保存最近的命令退出状态
例如:
ping -c1 -W1 hostdown &> /dev/null
echo $?
退出状态码
- bash自定义退出状态码
exit [n]:自定义退出状态码
注意:脚本中一旦遇到exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字
注意:如果未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码
算术运算
- bash中的算术运算:help let
+, -, *, /, %取模(取余), **(乘方)
实现算术运算:
(1) let var=算术表达式
(2) var=$[算术表达式]
(3) var=$((算术表达式))
(4) var=$(expr arg1 arg2 arg3 ...)(详见1.9)
(5) declare –i var = 数值
(6) echo ‘算术表达式’ | bc
- 乘法符号有些场景中需要转义,如*
- bash有内建的随机数生成器:$RANDOM(0-32767)
echo $[$RANDOM%50] :0-49之间随机数
赋值
- 增强型赋值:
+=, -=, *=, /=, %=
- let varOPERvalue
例如:let count+=3
自加3后自赋值
- 自增,自减:
let var+=1
let var++
let var-=1
let var--
逻辑运算
- true, false
1, 0
- 与:
1 与 1 = 1
1 与 0 = 0
0 与 1 = 0
0 与 0 = 0
- 或:
1 或 1 = 1
1 或 0 = 1
0 或 1 = 1
0 或 0 = 0
- 非:!
! 1 = 0
! 0 = 1
- 短路运算
短路与
第一个为0,结果必定为0
第一个为1,第二个必须要参与运算
短路或
第一个为1,结果必定为1
第一个为0,第二个必须要参与运算
- 异或:^
异或的两个值,相同为假,不同为真
条件测试
- 判断某需求是否满足,需要由测试机制来实现
专用的测试表达式需要由测试命令辅助完成测试过程
- 评估布尔声明,以便用在条件性执行中
若真,则返回0
若假,则返回1
- 测试命令:
test EXPRESSION
[ EXPRESSION ]
[[ EXPRESSION ]]
注意:EXPRESSION前后必须有空白字符
=~后正则表达式不要加引号
字符串匹配加引号
条件性的执行操作符
- 根据退出状态而定,命令可以有条件地运行
&& 代表条件性的AND THEN
|| 代表条件性的OR ELSE
例如:
grep -q no_such_user /etc/passwd \
|| echo ‘No such user‘
No such user
ping -c1 -W2 station1 &> /dev/null \
> && echo "station1 is up" \
> || (echo ‘station1 is unreachable‘; exit 1)
station1 is up
test命令
- 长格式的例子:
test "$A" == "$B" && echo "Strings are equal"
test “$A” -eq “$B” && echo "Integers are equal"
- 简写格式的例子:
[ "$A" == "$B" ] && echo "Strings are equal"
[ "$A" -eq "$B" ] && echo "Integers are equal"
建议使用简写格式,注意[ 和单词之间有空格。
[ ]里面有东西就为真,但是要用” “把变量引起来才知道有东西。(详见1.12)
bash的数值测试
- -v VAR
变量VAR是否设置(详见附录1.12)
- 数值测试:(判断数字大小,两边必须都是数字)
-gt 是否大于
-ge 是否大于等于
-eq 是否等于
-ne 是否不等于
-lt 是否小于
-le 是否小于等于
bash的字符串测试
- 字符串测试:
== 是否等于
> ascii码是否大于ascii码
< 是否小于
!= 是否不等于
=~ 左侧字符串是否能够被右侧的PATTERN(正则表达式)所匹配(左侧能否包含右侧)正则表达式中变量加引号,字符串不加引号。(详见附录1.13)
注意: 此表达式一般用于[[ ]]中;扩展的正则表达式
[[]]中判断字符串是否相等用==,[]判断字符串是否相等用一个=即可,==也行。这种判断一般一个中括号就行了,两个中括号用于支持正则表达式。
-z "STRING“ 字符串是否为空,空为真,不空为假
-n "STRING“ 字符串是否不空,不空为真,空为假(详见附录1.12)
注意:用于字符串比较时的用到的操作数都应该使用引号
Bash的文件测试
- 存在性测试
-a FILE:同-e
-e FILE: 文件存在性测试,存在为真,否则为假
- 存在性及类别测试
-b FILE:是否存在且为块设备文件
-c FILE:是否存在且为字符设备文件
-d FILE:是否存在且为目录文件
-f FILE:是否存在且为普通文件
-h FILE 或 -L FILE:存在且为符号链接文件(详见附录1.15)
-p FILE:是否存在且为命名管道文件
-S FILE:是否存在且为套接字文件
Bash的文件权限测试
- 文件权限测试:
-r FILE:是否存在且可读
-w FILE: 是否存在且可写
- -x FILE: 是否存在且可执行(详见附录1.16)
- 文件特殊权限测试:
-u FILE:是否存在且拥有suid权限
-g FILE:是否存在且拥有sgid权限
-k FILE:是否存在且拥有sticky权限
Bash的文件属性测试
- 文件大小测试:
-s FILE: 是否存在且非空
- 文件是否打开:
-t fd: fd 文件描述符是否在某终端已经打开(详见附录1.14)
-N FILE:文件自从上一次被读取之后是否被修改过
-O FILE:当前有效用户是否为文件属主
-G FILE:当前有效用户是否为文件属组
(当前有效用户和实际用户不一定是同一个人,像当前用户执行passwd,当前实际执行的是root权限,当时有效用户变成了root,实际用户是当前用户)
- 双目测试:
FILE1 -ef FILE2: FILE1是否是FILE2的硬链接
FILE1 -nt FILE2: FILE1是否新于FILE2(mtime)
FILE1 -ot FILE2: FILE1是否旧于FILE2
Bash的组合测试条件
- 第一种方式:
COMMAND1 && COMMAND2 并且
COMMAND1 || COMMAND2 或者
! COMMAND 非
如:[[ -r FILE ]] && [[ -w FILE ]]
- 第二种方式:
EXPRESSION1 -a EXPRESSION2 并且
EXPRESSION1 -o EXPRESSION2 或者
! EXPRESSION
必须使用测试命令进行
示例:
[ -z “$HOSTNAME” -o $HOSTNAME "==\
"localhost.localdomain" ] && hostname www.magedu.com
[ -f /bin/cat -a -x /bin/cat ] && cat /etc/fstab
使用read命令来接受输入
- 使用read来把输入值分配给一个或多个shell变量
-p 指定要显示的提示
-s 静默输入,一般用于密码
-n N 指定输入的字符长度N
-d ‘字符’ 输入结束符
-t N TIMEOUT为N秒
read 从标准输入中读取值,给每个单词分配一个变量
所有剩余单词都被分配给最后一个变量
read -p “Enter a filename: “ FILE
bash如何展开命令行
(命令运行的过程)
- 把命令行分成单个命令词
- 展开别名
(在脚本中别名不能使用)
- 展开大括号的声明({})
echo {1..6}
1 2 3 4 5 6
- 展开波浪符声明(~)
家目录
- 命令替换$() 和 ``)
- 再次把命令行分成命令词
- 展开文件通配(*、?、[abc]等等)
- 准备I/0重导向(<、>)
- 运行命令
上面的执行顺序说明的执行的优先级
防止扩展
- 反斜线(\)会使随后的字符按原意解释
$ echo Your cost: \$5.00
$本身是表示变量的开始符,要原意显示要加\
Your cost: $5.00
- 加引号来防止扩展
单引号(’)防止所有扩展
双引号(”)也防止所有扩展,但是以下情况例外:
$(美元符号) - 变量扩展
`(反引号) - 命令替换
\(反斜线) - 禁止单个字符扩展
!(叹号) - 历史命令替换
bash的配置文件
- 按生效范围划分,存在两类:
?全局配置:
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc
? 个人配置:
~/.bash_profile
~/.bashrc
shell登录两种方式
- 交互式登录:
(1)直接通过终端输入账号密码登录
(2)使用“su - UserName” 切换的用户
执行顺序:/etc/profile --> /etc/profile.d/*.sh --> ~/.bash_profile --> ~/.bashrc --> /etc/bashrc
- 非交互式登录:
(1)su UserName
(2)图形界面下打开的终端
(3)执行脚本
(4)任何其它的bash实例
执行顺序: ~/.bashrc --> /etc/bashrc --> /etc/profile.d/*.sh
上面的执行顺序:在文件中调用的先后顺序。
Profile类
- 按功能划分,存在两类:
profile类和bashrc类
- profile类:为交互式登录的shell提供配置
全局:/etc/profile, /etc/profile.d/*.sh
个人:~/.bash_profile
功用:
(1) 用于定义环境变量
(2) 运行命令或脚本
Bashrc类
- bashrc类:为非交互式和交互式登录的shell提供配置
全局:/etc/bashrc
个人:~/.bashrc
功用:
(1) 定义命令别名和函数
(2) 定义本地变量
编辑配置文件生效
- 修改profile和bashrc文件后需生效
两种方法:
1重新启动shell进程
2 . 或source
例:
. ~/.bashrc
执行脚本尽量不要使用.或source执行,因为会将原来bash中的相同变量值改变,而用bash或./filename会新开一个bash进程,执行完就退出,不会改变原来的变量值。
Bash 退出任务
- 保存在~/.bash_logout文件中(用户)
- 在退出登录shell时运行
- 用于
创建自动备份
清除临时文件
可以在脚本写上退出时要执行的操作。
上面的所有配置文件生效,都必须是需要用户登录,不登录就无法读取配置文件内容。要想不登录执行,就要把文件写入到开机启动的内核中了。
$-变量
- h:hashall,打开这个选项后,Shell 会将命令所在的路径hash下来,避免每次都要查询。通过set +h将h选项关闭
- i:interactive-comments,包含这个选项说明当前的 shell 是一个交互式的 shell。所谓的交互式shell,在脚本中,i选项是关闭的。
- m:monitor,打开监控模式,就可以通过Job control来控制进程的停止、继续,后台或者前台执行等。
- B:braceexpand,大括号扩展
- H:history,H选项打开,可以展开历史列表中的命令,可以通过!感叹号来完成,例如“!!”返回上最近的一个历史命令,“!n”返回第 n 个历史命令
- (详见附录1.18)
附录:
1.1 进程树
[root@centos7 ~]#ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 2058 0.0 0.2 147788 2360 ? Rs 08:14 0:04 sshd: root@pts/0
root 2062 0.0 0.2 116292 2796 pts/0 Ss 08:15 0:00 -bash
root 5191 0.0 0.0 107904 612 ? S 11:15 0:00 sleep 60
root 5198 0.0 0.1 151064 1804 pts/0 R+ 11:15 0:00 ps aux
……
查看所有的进程信息
[root@centos7 ~]#echo $$
2062
查看当前的进程编号
[root@centos7 ~]#pstree -p
├─smartd(591)
├─sshd(1013)───sshd(2058)───bash(2062)───pstree(5319)
├─systemd-journal(357)
├─systemd-logind(600)
查看当前的进程树子进程和父进程信息,可以看到pstree是bash的子进程,运行pstree命令,会用bash程序会去解释执行命令
[root@centos7 ~]#bash
[root@centos7 ~]#echo $$
5498
当前进程号
bash下面bash,bash的子进程bash
[root@centos7 ~]#echo $PPID
2062
查看父进程编号
[root@centos7 ~]#echo $$
2553
[root@centos7 ~]#exit
exit
[root@centos7 ~]#echo $$
2039
退出当前进程使用exit命令
[root@centos7 ~]#name=`cat /etc/issue`
[root@centos7 ~]#echo $name
The hostname is \n Time is \t TTY is \l \S Kernel \r on an \m
[root@centos7 ~]#echo "$name"
The hostname is \n
Time is \t
TTY is \l
\S
Kernel \r on an \m
变量是一个很长的文章,需要将变量加上双引号才能按原格式输出
1.2 环境变量
[root@centos7 ~]#export var=100
[root@centos7 ~]#echo $var
100
[root@centos7 ~]#bash
[root@centos7 ~]#echo $var
100
[root@centos7 ~]#var=200
[root@centos7 ~]#bash
[root@centos7 ~]#echo $var
200
[root@centos7 ~]#exit
exit
[root@centos7 ~]#exit
exit
[root@centos7 ~]#echo $var
100
可以看出环境变量的赋值只能向下传递,不能向上传递。
1.3 shell嵌套深度
[root@centos7 ~]#echo $SHLVL
1
[root@centos7 ~]#bash
[root@centos7 ~]#echo $SHLVL
2
[root@centos7 ~]#bash
[root@centos7 ~]#echo $SHLVL
3
查看shell的嵌套深度
1.4 ( )开启新的bash
[root@centos7 ~]#( umask 066;touch /app/ff )
[root@centos7 ~]#umask
0022
[root@centos7 ~]#ls /app
binary ff passwd scripts
命令中的小括号代表新开一个bash,运行完命令后退出新开的bash。
1.5 scp传文件
[root@centos7 ~]#scp filename wang@172.18.1.124:
上传文件到wang的家目录中,密码:magedu
[root@centos7 ~]#scp -r wang@172.18.1.124:/home/wang/scripts /app/
将远程主机上的文件拷贝到本机上,-r拷贝目录
1.6 脚本中shift作用
[root@centos7 /app/scripts]#cat f1.sh
#!/bin/bash
echo "1st is $1"
echo "2st is $2"
echo "3st is $3"
shift
echo "1st is $1"
echo "2st is $2"
echo "3st is $3"
[root@centos7 /app/scripts]#./f1.sh a b c
1st is a
2st is b
3st is c
1st is b
2st is c
3st is
使用shift参数往左移动了一位
1.7 上传文件脚本化
[root@centos7 /app/scripts]#vim scp14.sh
#!/bin/bash
scp $* wang@172.18.1.124:
上传代码脚本化
1.8 脚本头自动生成
#!/bin/bash
# create script header automatically
echo "#!/bin/bash
#Filename: $1
#Author: sunan
#Date: `date +%F`
">$1
chmod +x $1
vim + $1
[root@centos7 /app/scripts]#./make_script_header.sh news.sh
脚本头脚本化
1.9 expr计算运算符加空格
[root@centos7 /app/scripts]#expr 1+2
1+2
[root@centos7 /app/scripts]#expr 1 + 2
3
[root@centos7 /app/scripts]#expr 1 * 2
expr: syntax error
[root@centos7 /app/scripts]#expr 1 \* 2
2
expr计算需要运算符加空格,*需要转义。
[root@centos7 /app/scripts]#expr 1 - 1
0
[root@centos7 /app/scripts]#echo $?
1
[root@centos7 /app/scripts]#expr 1 - 1
0
[root@centos7 /app/scripts]#echo $?
1
[root@centos7 /app/scripts]#let 1-1
[root@centos7 /app/scripts]#echo $?
1
上面的命令可以正确执行,但返回的执行结果不是0,man expr可以看到:
Exit status is 0 if EXPRESSION is neither null nor 0, 1 if EXPRESSION
is null or 0, 2 if EXPRESSION is syntactically invalid, and 3 if an error occurred.
1.10 字体加颜色
[root@centos7 /app/scripts]#vim color.sh
^[[31mred^[[0m
实现字体加颜色,其中的^[是在vim中使用Ctrl+v+[实现。
[root@centos7 /app/scripts]#cat color.sh
red
字体变成红色
1.11 文件加颜色
[root@centos7 /app/scripts]#^Mcolor=$[RANDOM%7+31];echo -e "\033[${color}mred color\033[0m"
给文件加颜色
1.12 关于[ ]的判断
[root@centos7 /app/scripts]#[ ]&& echo true || echo false
false
[root@centos7 /app/scripts]#[ 0 ]&& echo true || echo false
true
[root@centos7 /app/scripts]#[ "" ]&& echo true || echo false
false
[root@centos7 /app/scripts]#[ " " ]&& echo true || echo false
true
[root@centos7 /app/scripts]#a=111
[root@centos7 /app/scripts]#[ $a ]&& echo true || echo false
true
[root@centos7 /app/scripts]#unset a
[root@centos7 /app/scripts]#[ $a ]&& echo true || echo false
false
[root@centos7 /app/scripts]#a=" "
[root@centos7 /app/scripts]#[ $a ]&& echo true || echo false
false
[root@centos7 /app/scripts]#[ "$a" ]&& echo true || echo false
true
关于[ ]判断执行成功条件
[root@centos7 ~]# var=hh;[ -n $var ]&&echo true || echo false
true
[root@centos7 ~]# unset var;[ -n $var ]&&echo true || echo false
true
[root@centos7 ~]# [ -n ]&&echo true || echo false
true
[root@centos7 ~]# [ -n "$var" ]&&echo true || echo false
false
当var没有定义,但又没加双引号[ ]判断相当于只有-n存在,就判断为真。
中括号里面当判断的时候,要加双引号。
[root@centos7 scripts]# vim useradd.sh
#!/bin/bash
[ $# -ne 1 ] &&echo -e "The arg must one \n Usage:useradd.sh username" && exit 10
id $1 &> /dev/null && echo "$! is exist " && exit 20
useradd $1 && echo "$! is created"
判断用户输入的参数是否等于1,不是退出,是继续下面的命令。
[root@centos7 scripts]# var="";[ -v var ]&&echo true ||echo false
true
[root@centos7 scripts]# var=ab;[ -v var ]&&echo true ||echo false
true
[root@centos7 scripts]# unset var;[ -v var ]&&echo true ||echo false
false
只要变量定义了就为真,var前面不需要加$,因为-v 的作用就是直接加var。
[root@centos7 scripts]# vim useradd.sh
#!/bin/bash
[ -n “$1”] ||{echo -e "The arg must one \n Usage:useradd.sh username" && exit 10;}
id $1 &> /dev/null && echo "$! is exist " && exit 20
useradd $1 && echo "$! is created"
-n 判断$1是否为空,空就是假执行后面命令,非空为真,跳过后面命令执行下面命令。同时上面必须用{ },( )新开一个子shell,退出是退出一个子shell。
1.13关于[[ ]]的判断
[root@centos7 ~]# filename=f1.sh;[[ "$filename" =~ \.sh$ ]] && echo true || echo false
true
[root@centos7 ~]# filename=f1.sh;[[ "$filename" =~ "\.sh$" ]] && echo true || echo false
false
和正则表达式做判断时,字符串加引号被认为字符串和引号是一个整体。
[]:单中括号不支持正则表达式
[root@centos7 ~]# var="abc*";[[ "$var" == "abc*" ]]&&echo true ||echo false
true
[root@centos7 ~]# var="abc*";[[ "$var" == abc* ]]&&echo true ||echo false
true
[root@centos7 ~]# var="abcdef*";[[ "$var" == abc* ]]&&echo true ||echo false
true #把*当成通配符了
[root@centos7 ~]# var="abdef*";[[ "$var" == abc* ]]&&echo true ||echo false
false
工作中最好不要这样用,掌握一个原则:只有用到正则表达式匹配用[[ ]],通常的匹配用[ ]即可,不然容易乱。
[root@centos7 ~]# ip=1.1.1.1;[[ "$ip" =~ ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ ]]&&echo true || echo false
true
[root@centos7 ~]# ip=1111.1.1.1;[[ "$ip" =~ ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ ]]&&echo true || echo false
false
判断一个ip地址是否正确
[root@centos7 ~]#filename=f1.sh ; [ "$filename" =~ \.sh$ ] && echo true ||echo false
判断后缀
[root@centos7 ~]#var=haha123 ; [[ "$var" =~ ^[0-9]+$ ]] && echo true || echo false
判断数字
[root@centos7 ~]#var=0123 ; [[ "$var" =~ ^0*[1-9][0-9]*$ ]] && echo true || echo false
判断正整数
[root@centos7 ~]#mobile=12800138000 ;[[ "$mobile" =~ ^1[3456789][0-9]{9}$ ]] && echo true || echo false
手机号
1.14 文件描述符
面试题:当前这个进程打开了多少个文件?
每打开一个文件就打开一个文件描述符,就是统计文件描述符的个数。
[root@centos7 scripts]# ls -l /proc/$$/fd
total 0
lrwx------. 1 root root 64 Nov 25 08:13 0 -> /dev/pts/0
lrwx------. 1 root root 64 Nov 25 08:13 1 -> /dev/pts/0
lrwx------. 1 root root 64 Nov 25 08:13 2 -> /dev/pts/0
lrwx------. 1 root root 64 Nov 25 10:30 255 -> /dev/pts/0
[root@centos7 scripts]# ls /proc/$$/fd | wc -l
4
[root@centos7 scripts]# ls -l /proc/$$/fd | wc -l
5
同时注意加 -l和不加结果不同
1.15 链接文件
[root@centos7 scripts]# [ -d /lib ] && echo true
true
[root@centos7 scripts]# [ -l /lib ] && echo true
-bash: [: -l: unary operator expected
[root@centos7 scripts]# [ -L /lib ] && echo true
true
判断文件属性,需要先判断是不是链接文件,如果是链接文件判断的文件属性石,链接的原文件的属性。
1.16 文件权限判断
[root@centos7 scripts]# [ -r /etc/shadow ]&& echo true
true
[root@centos7 scripts]# [ -w /etc/shadow ]&& echo true
true
[root@centos7 scripts]# [ -x /etc/shadow ]&& echo true
[root@centos7 scripts]# ll /etc/shadow
----------. 1 root root 1197 Nov 7 17:43 /etc/shadow
判断一个文件的权限不能只看表面的权限,要看实际的最终权限。
1.17 read
[root@centos7 scripts]# read
asdf
[root@centos7 scripts]# echo $REPLY
asdf
[root@centos7 scripts]# read name
abc
[root@centos7 scripts]# echo $name
abc
read没有给变量,就赋给了REPLY。
[root@centos7 scripts]# read name age title
sunan 26 CTO
[root@centos7 scripts]# echo $name $age $title
sunan 26 CTO
赋予多个值,最好一个个赋值。
[root@centos7 scripts]# read -s -p "please input your password:" password
please input your password:[root@centos7 scripts]#
-s静默模式输入信息后看不到。
[root@localhost ~]#read -n 8 -p "please input your password:" passwd
please input your password:12345678[root@localhost ~]#
-n 规定最多输入几个字符,超过最多的字符数就自动退出。
1.18$-变量
[root@localhost ~]#echo $-
himBH
查看当前启用的功能。
[root@localhost ~]#set +h
[root@localhost ~]#echo $-
imBH
去掉hash功能
[root@localhost ~]#hash
-bash: hash: hashing disabled
[root@localhost ~]#set -h
[root@localhost ~]#hash
hits command
1 /bin/cat
3 /bin/ls
set -h 恢复hash功能的使用
[root@localhost ~]#vim i.sh
#!/bin/bash
echo "$-"
[root@localhost ~]#bash i.sh
hB
在脚本中i功能是被禁用的,i交互式(像别名)。
[root@localhost ~]#echo {1..9}
1 2 3 4 5 6 7 8 9
B大括号扩展功能可以使用
练习:
不想让别的用户登录:生成文件/etc/nologin