本章主要介绍函数的基础,变量赋值,逻辑运算,条件表达式,测试
linux之基础shell脚本编程1 基础变量赋值
linux之基础shell脚本编程2 if语句循环判断
linux之基础shell脚本编程3 函数数组
linux之基础shell脚本编程4 字符串操作,变量赋值,配置用户环境
一,编程基础的介绍
1.1 编程基础
程序是由指令和数据组成的
程序编程风格可分为面向对象和面向过程编程
面向过程:以指令为中心,数据服务于指令
面向对象:以数据为中心,指令服务于数据
sehll程序提供了编程能力,是通过解释器解释执行的
1.2 程序的执行方式
计算机只能运行二进制指令;
编程语言:
低级:汇编
高级:
编译:高级语言 --> 编译器 --> 目标代码 比如 java,C# 等
解释:高级语言 --> 解释器 --> 机器代码 比如 shell perl python 等
1.3 编程基本概念
编程逻辑处理方式:
- 顺序执行
- 循环执行
- 选择执行
shell编程:过程式,解释执行
编程语言的基本结构:
数据存储:变量,数组 等
表达式:a+b 等
语句:if while 等
二,shell脚本编程
2.1 shell脚本基础
shell脚本是包含一些命令或声明,并符合一定格式的文本文件
格式要求:首行shebang机制
#!/bin/bash
#!/usr/bin/python
#!/usr/bin/perl
shell脚本的用途:
- 自动化常用命令
- 执行系统管理和故障排除
- 创建简单的应用程序
- 处理文本或文件
2.2 创建shell脚本
第一步:使用文本编辑器来创建一个以 .sh 为结尾的文本文件,第一行必须包括shell声明序列:#!/bin/bash ;添加适当的注释,注释以 # 开头
第二步:运行脚本,首先,先给脚本赋予执行权限,在命令行上指定脚本的绝对或相对路径;也可以直接运行解释器,将脚本作为解释器程序的参数运行
脚本范例如下:
#!/bin/bash
#author: nineven
#Version: 1.0
#Description:This script displays some information about your
# environment
echo "Greetings. The date and time are $(date)" echo "Your working directory is: $(pwd)"
2.3 脚本调试
bash -n /path/to/some_script #检查脚本中的语法错误(这个功能好像不怎么好用)
bash -x /path/to/some_script #调试执行,感觉很强大
2.4 编程程序语言分类
强类型:
定义变量时必须指定类型,参与运算必须符合类型要求,调用未声明变量会产生错误,比如 java python
弱类型:
无须指定类型,默认均为字符型,参与运算会自动进行隐式类型转换,变量无须事先定义可直接调用,比如 bash 不支持浮点数
变量命令法则:
- 不能使用程序中的保留字:比如 if for 等
- 只能使用数字,字母及下划线,且不能以数字开头
- 见名知义
2.5 变量
变量:命名空间的内存空间
变量的数据存储方式:
字符
数值,比如 整形,浮点型
变量的类型:
字符
数值:整形,浮点型
变量的作用:
- 数据存储格式
- 参与的运算
- 表示的数据范围
2.5.2 bash中的变量的种类
根据变量的生效范围等标准可分为以下几类:
- 本地变量:生效范围为当前shell进程,对当前shell之外的其他shell进程,包括当前shell的子shell进程均无效
- 环境变量:生效范围为当前shell进程及其子进程
- 局部变量:生效范围为当前shell进程中某代码片段(通常指函数)
- 位置变量:$1,$2,...来表示,用于让脚本在脚本代码中调用通过命令行传递给它的参数
- 特殊变量:$?,$0,$*,$@,$#
2.5.2.1 本地变量
变量赋值: name='value'
可以使用引用value:
可以直接是字符串:name="nineven"
变量引用:name="$USER"
命令引用:name=`command`,name=$(command)
变量引用:${name},$name
"":弱引用,其中的变量引用会被替换为变量值
'':强引用,其中的变量引用不会被替换为变量值,而是保持原字符串
显示已定义的所有变量:set
删除变量: unset name (删除变量时千万不要加 $ 符号)
实战训练1
1、编写脚本/root/bin/systeminfo.sh,显示当前主机系统信 息,包括主机名,IPv4地址,操作系统版本,内核版本, CPU型号,内存大小,硬盘大小。
#!/bin/bash
echo -e "The computer's info is"
name=$(hostname)
ip=$(ifconfig | sed -rn "s@^[[:space:]]+inet[[:space:]]+(.*)net.*@\1@p"|head -1)
sysinfo=$(cat /etc/centos-release)
kernav=$(uname -r)
cpuinfo=$(lscpu | sed -r -n 's@Model name.*:[[:space:]]+(.*)@\1@p')
meminfo=$( cat /proc/meminfo | head -1)
hhdinfo=$(fdisk -l | head -2|tail -1)
echo -e "\033[31m------主机名----------\033[0m"
echo $name
echo -e "\033[31m--------IPv4地址-----------------\033[0m"
echo -e $ip
echo -e "\033[31m---------操作系统版本-------------\033[0m"
echo $sysinfo
echo -e "\033[31m--------内核版本-----------------\033[0m"
echo $kernav
echo -e "\033[31m-------CPU型号------------------\033[0m"
echo $cpuinfo
echo -e "\033[31m----------内存大小---------------\033[0m"
echo $meminfo
echo -e "\033[31m-----硬盘大小--------------------\033[0m"
echo $hhdinfo
echo -e "\033[31m-------------------------\033[0m"
2、编写脚本/root/bin/backup.sh,可实现每日将/etc/目录 备份到/root/etcYYYY-mm-dd中
#!/bin/bash
cp -a /etc /root/etc`date +%F`
3、编写脚本/root/bin/disk.sh,显示当前硬盘分区中空间利 用率最大的值
#!/bin/bash
echo "当前硬盘分区利用率最大值是 : `df | egrep '\/dev\/sda*' | tr -s ' '|cut -d' ' -f5|sort -n|tail -1`"
4、编写脚本/root/bin/links.sh,显示正连接本主机的每个远 程主机的IPv4地址和连接数,并按连接数从大到小排序
#!/bin/bash
echo "the links number is:"
netstat -nt |tr -s ' ' |cut -d ' ' -f5 |cut -d: -f1 |grep [0-9]|sort |uniq -c|sort -nr
2.5.2.2 环境变量
变量声明,赋值:
export name=VALUE
declare -x name=VALUE
变量引用:
$name,${name}
显示所有环境变量:
export
env
printenv
删除:
unset name
bash有许多内建的环境变量:PATH,SHELL,USER,UID,HISTSIZE,HOME,PWD,OLDPWD,HISTFILE,PS1等
2.5.2.3 只读和位置变量
只读变量:只能声明,但不能修改和删除
readonly name
declear -r name
位置变量:在脚本代码中调用通过命令行传递给脚本的参数
$1,$2,... #对应第1,第2等参数,shift [n] 换位置
$0 #命令本身
$* #传递给脚本的所有参数,全部参数合为一个字符串
$@ #传递给脚本的所有参数,每个参数为独立字符串
$# #传递给脚本的参数的个数
$@ $* #只在被双引号包起来的时候才会有差异
比如:判断给出的文件的行数
#!/bin/bash
linecount="$(wc -l $1| cut -d' ' -f1)"
echo "$1 has $linecount lines."
三,shell脚本运算
3.1 算术运算
bash中的算术运算可以通过 help let 命令查看
+ - * / %取模(取余) **(乘方)
实现算术运算:
let var=算术表达式
var=$[算术表达式]
var=$((算术表达式))
var=$(expr arg1 arg2 arg3 ...)
declare -i var = 数值
echo `算术表达式`|bc
乘法符号有些场景中需要转义,如*
bash中有内建的随机数生成器:$RANDOM 可以生成 1-32767 ,比如
echo $[$RANDOM%80] :表示0-79之间的随机数
[root@centos7 ~]# echo $[$RANDOM%81]
73
[root@centos7 ~]# echo $[$RANDOM%81]
9
[root@centos7 ~]# echo $[$RANDOM%81]
80
3.2 赋值
增强型赋值:
+= ,-= ,*= ,/= ,%=
let varOPERvalue
例如: let count+=3 自加3后的值重新赋给自己
自增,自减:
let var+=1
let var++
let var-=1
let var--
实战训练2
1:写一个脚本/root/bin/sumid.sh,计算/etc/passwd 文件中的第10个用户和第20用户的ID之和
#!/bin/bash
echo "ID sum is : `sed -n '10p;20p' /etc/passwd | cut -d: -f3 | paste -s -d '+'| bc`"
2:写一个脚本/root/bin/sumspace.sh,传递两个文件 路径作为参数给脚本,计算这两个文件中所有空白行之和
#!/bin/bash
echo "文件空白行总共有: `cat $1 $2 | grep -c '^[[:space:]]*$'` 行"
3:写一个脚本/root/bin/sumfile.sh,统计/etc, /var, /usr目录中共有多少个一级子目录和文件
#!/bin/bash
echo $[$(ls -a /etc /var /usr |wc -l)-6]
我感觉 . .. 应该不是子目录吧,所以我在得到的结果减去6
3.3 逻辑运算
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,第二个必须要参与运算
异或:^
异或的两个值,相同为假,不同为真
3.4 聚集命令
有两种聚集命令的方法:
复合式:date;who |wc -l 命令会一个接一个地运行
子shell:(date;who |wc -l) >> /tmp/trace 所有的输出都被发送给单个STDOUT和STDERR
3.5 退出状态及退出码
进程使用退出状态来报告成功或失败
0 代表成功,1-255代表失败
$? 变量保存最近的命令退出状态
例如
[root@centos7 testdir]# ping -c1 -W1 10.1.0.1 &> /dev/null
[root@centos7 testdir]# echo $?
0
[root@centos7 testdir]# ping -c1 -W1 10.1.2.1 &> /dev/null
[root@centos7 testdir]# echo $?
1
成功返回0 失败返回非0
bash自定义退出状态码
exit [n] :自定义退出状态码
注意:脚本中一旦遇到exit命令,脚本会立即终止,终止退出状态取决于exit命令后面的数字
如果未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码
四,测试
4.1 条件测试
判断某需求是否满足,需要由测试机制来实现
专用的测试表达式需要由测试命令辅助完成测试过程
评估布尔声明,以便用在条件性执行中
若真,则返回0
若假,则返回1
测试命令
test EXPRESSION
[ EXPRESSION ]
[[ EXPRESSION ]]
注意:EXPRESSION前后必须有空白字符
4.2 条件性的执行操作符
根据退出状态而定,命令可以有条件地运行
&& 代表条件性的AND THEN
|| 代表条件性的OR ELSE
比如:
[root@centos7 testdir]# grep -q root /etc/passwd && echo 'user is exist' || echo 'no user'
user is exist
[root@centos7 testdir]# grep -q roota /etc/passwd && echo 'user is exist' || echo 'no user'
no user
[root@centos7 testdir]# ip="10.1.0.122"
[root@centos7 testdir]# ping -c1 -W1 $ip &> /dev/null && echo 'ip is up' || echo 'ip is down'
ip is down
[root@centos7 testdir]# ip="10.1.0.1"
[root@centos7 testdir]# ping -c1 -W1 $ip &> /dev/null && echo 'ip is up' || echo 'ip is down'
ip is up
4.3 test命令
长格式的例子如下:
[cent@centos7 ~]$ A=abcd
[cent@centos7 ~]$ B=abcd
[cent@centos7 ~]$ test $A == $B && echo 'strings are equal' || echo 'strings are not equal'
strings are equal
[cent@centos7 ~]$ B=abcde
[cent@centos7 ~]$ test $A == $B && echo 'strings are equal' || echo 'strings are not equal'
strings are not equal
[cent@centos7 ~]$ test $A -eq $B && echo 'strings are equal' || echo 'strings are not equal'
-bash: test: abcd: integer expression expected #字符串不能做 -eq 运算
strings are not equal
[cent@centos7 ~]$ A=18
[cent@centos7 ~]$ B=18
[cent@centos7 ~]$ test $A -eq $B && echo 'Integers are equal' || echo 'Integers are not equal'
Integers are equal
[cent@centos7 ~]$ B=19
[cent@centos7 ~]$ test $A -eq $B && echo 'Integers are equal' || echo 'Integers are not equal'
Integers are not equal
简写格式的例子如下:
[cent@centos7 ~]$ [$A -eq $B] && 'Integers are equal' || echo 'Integers are not equal'
bash: [18: command not found... # [ EXPRESSION ] ,EXPRESSION表达式前后必须有空白字符
[cent@centos7 ~]$ [ $A -eq $B ] && 'Integers are equal' || echo 'Integers are not equal'
Integers are not equal
[cent@centos7 ~]$ B=18
[cent@centos7 ~]$ [ $A -eq $B ] && echo 'Integers are equal' || echo 'Integers are not equal'
Integers are equal
[cent@centos7 ~]$ A=abcd
[cent@centos7 ~]$ B=abcd
[cent@centos7 ~]$ [ $A == $B ] && echo 'strings are equal' || echo 'strings are not equal'
strings are equal
4.4 bash的测试类型
4.4.1 数值测试
-gt :是否大于
-ge :是否大于等于
-eq :是否等于
-ne :是否不等于
-lt :是否小于
-le :是否小于等于
[cent@centos7 ~]$ A=1;B=4
[cent@centos7 ~]$ [ $A -gt $B ] && echo '成立' || echo '不成立'
不成立
[cent@centos7 ~]$ [ $A -ge $B ] && echo '成立' || echo '不成立'
不成立
[cent@centos7 ~]$ [ $A -lt $B ] && echo '成立' || echo '不成立'
成立
[cent@centos7 ~]$ [ $A -ne $B ] && echo '成立' || echo '不成立'
成立
[cent@centos7 ~]$ [ $A -eq $B ] && echo '成立' || echo '不成立'
不成立
[cent@centos7 ~]$ [ $A -le $B ] && echo '成立' || echo '不成立'
成立
4.4.2 字符串测试
== :是否等于
> :左面的ascii 码是否大于右面字符串的ascii码
< :左面的ascii 码是否小于右面字符串的ascii码
!= :是否不等于
=~ :左侧字符串是否能够被右侧的PATTERN所匹配;注意:此表达式一般用于 [[ ]] 中
-z "STRING" :字符串是否为空,空为真,不空为假
-n "STRING" :字符串是否非空,非空为真,空为假
注意:用于字符串比较时的用到的操作数都应该使用引号
参数 | 功能 |
数值测试 | |
-gt | 是否大于 |
-ge | 是否大于等于 |
-eq | 是否等于 |
-ne | 是否不等于 |
-lt | 是否小于 |
-le | 是否小于等于 |
字符串测试 | |
== | 是否等于 |
> | ascii码是否大于ascii码 |
< | 是否小于 |
!= | 是否不等于 |
=~ | 左侧字符串是否能够被右侧的PATTERN所匹配 注意: 此表达式一般用于[[]] 中 |
-z "string" | 字符串是否为空,空为真,不空为假 |
-n "string" | 字符串是否不空,不空为真,空为假 |
注意:用于字符串比较时的用到的操作数都应该使用引号 |
实战训练3
1、写一个脚本/root/bin/argsnum.sh,接受一个文件路径作 为参数;如果参数个数小于1,则提示用户“至少应该给一个 参数”,并立即退出;如果参数个数不小于1,则显示第一个 参数所指向的文件中的空白行数
#!/bin/bash
[ $# -ge 1 ] && echo "文件空白行数为: `grep -c '^[[:space:]]*$' $1`" || echo '至少应该给一个参数'
测试执行结果:
[cent@centos7 scripts]$ bash argsnum.sh /etc/passwd
文件空白行数为: 0
[cent@centos7 scripts]$ bash argsnum.sh /testdir/aa
文件空白行数为: 42
[cent@centos7 scripts]$ bash argsnum.sh
至少应该给一个参数
2、写一个脚本/root/bin/hostping.sh,接受一个主机的 IPv4地址做为参数,测试是否可连通。如果能ping通,则提 示用户“该IP地址可访问”;如果不可ping通,则提示用户“ 该IP地址不可访问”
#!/bin/bash
ping -c1 -W1 $1 &> /dev/null && echo "该ip可以访问" || echo "该ip地址不可访问"
测试执行结果:
[cent@centos7 scripts]$ bash hostping.sh 10.1.0.1
该ip可以访问
[cent@centos7 scripts]$ bash hostping.sh 10.1.0.3
该ip地址不可访问
4.4.3 文件测试
参数 | 功能 |
文件存在性测试 | |
-e FILE | 文件存在性测试,存在为真,否则为假 |
-a FILE | 文件存在性测试,存在为真,否则为假 |
存在性及类别测试 | |
-b FILE | 是否存在且为块设备文件 |
-c FILE | 是否存在且为字符文件 |
-d FILE | 是否存在且为目录文件 |
-f FILE 或 -L FILE | 是否存在且为普通文件 |
-h FILE | 是否存在且为符号链接文件 |
-p FILE | 是否存在且为管道文件 |
-S FILE | 是否存在且为套接字文件 |
文件权限测试 | |
-r FILE | 是否存在且可读 |
-w FILE | 是否存在且可写 |
-x FILE | 是否存在且可执行 |
文件特殊权限测试 | |
-g FILE | 是否存在且拥有sgid权限 |
-u FILE | 是否存在且拥有suid权限 |
-k FILE | 是否存在且拥有sticky权限 |
文件大小测试 | |
-s FILE | 是否存在且非空 |
文件是否打开 | |
-t fd | fd表示文件描述符是否存在已经打开且与某终端相关 |
-N FILE | 文件自动上一次被读取之后是否被修改过 |
-O FILE | 当前有效用户是否为文件属主 |
-G FILE | 当前有效用户是否为文件属组 |
双目测试 | |
FILE1 -ef FILE2 | FILE1与FILE2是否指向同一个设备上的相同的inode |
FILE1 -nt FILE2 | FILE1是否新于FILE2 |
FILE1 -ot FILE2 | FILE1是否旧于FILE2 |
4.4.4 组合测试
第一种方式
COMMAND1 && COMMAND2 并且
COMMADN1 || COMMAND2 或者
!COMMAND 非
第二种方式
EXPRESSION1 -a EXPRESSION2 并且
EXPRESSION1 -o EXPRESSION2 或者
!EXPRESSION 非
必须使用测试命令进行
[ -z "$HOSTNAME" -o $HOSTNAME == "localhost.localdomain" ] && hostname www.liuyuworld
### 判断hostname是否为空或者hostname是"localhost.localdomain" 如果前面有一个条件成立,然后执行hostname 命令
[ -f /bin/cat -a -x /bin/cat ] && cat /etc/fstab ##判断文件是否存在并且可执行
实战训练4
1、chmod -rw /tmp/file1,编写脚本/root/bin/per.sh,判断当前用户对/tmp/fiile1文件 是否不可读且不可写
#!/bin/bash
[ ! -r /tmp/file1 ] && [ ! -w /tmp/file1 ] && echo "不可读写" || echo "可读写"
执行结果:
[root@centos7 shell]# chmod -rw /tmp/file1
[root@centos7 shell]# ll /tmp/file1
----------. 1 root root 0 Aug 10 16:22 /tmp/file1
[root@centos7 shell]# bash per.sh
可读写
[root@centos7 shell]# su cent
[cent@centos7 shell]$ bash per.sh
不可读写
2、编写脚本/root/bin/nologin.sh和login.sh,实现禁止和充许普通用户登录系统
思路:当/etc/目录下存在 nologin 文件时,普通用户会无法登陆,因此,我们只需要通过删除或增加该文件来限制普通用户是否可登陆
[root@centos7 shell]# cat login.sh
#!/bin/bash
[ -e /etc/nologin ] && echo "`rm -f /etc/nologin` enable user login "
[root@centos7 shell]# cat nologin.sh
#!/bin/bash
[ -a /etc/nologin ] || touch /etc/nologin
echo "disable user login "
执行 bash nologin.sh 脚本时,登陆普通用户会提示
Connecting to 172.18.16.152:22...
Connection established.
To escape to local shell, press 'Ctrl+Alt+]'.
Connection closed by foreign host.
Disconnected from remote host(centos7) at 20:11:18.
然后执行bash login.sh 脚本时,就可以直接登陆了
Connecting to 172.18.16.152:22...
Connection established.
To escape to local shell, press 'Ctrl+Alt+]'.
Last failed login: Fri Aug 12 20:11:09 CST 2016 from 172.18.16.171 on ssh:notty
There was 1 failed login attempt since the last successful login.
Last login: Fri Aug 12 20:10:29 2016 from 172.18.16.171
生成01-20
seq -f'%02g' 1 20
更多推荐
linux之基础shell脚本编程1 基础变量赋值
发布评论