概述:
本章重点在于讲解bash脚本的基础知识,为今后学习使用bash脚本打下基础
一、bash基础特性
程序:指令+数据
指令:由程序文件提供
数据:IO设备、文件、管道、变量
程序:算法+数据结构
变量:变量名+指向的内存空间
变量赋值:name=value
变量类型:存储格式(变量存储数据的数据类型)、表示数据范围、参与的运算
编程语言:
强类型变量:变量类型不可变,是什么类型就是什么类型;例如C语言
弱类型变量:
bash把所有变量统统视作字符型
bash正常情况下不支持浮点型数据,除非借助其他工具
bash中的变量无需事先声明:相当于把声明和赋值过程同时实现
声明:说明数据类型,定义变量名
变量替换(引用):把变量名出现的位置替换为其所指向的内存空间中数据
变量引用:${var_name},$var_name
变量名:变量名只能包含数字、字母和下划线,而且不能以数字开头
变量名:见名知义,命名机制遵循某种法则:不能够使用程序的保留字,例如if,else,then,while等等
bash变量类型:根据作用范围划分的
本地变量:作用域范围仅为当前shell进程
环境变量:作用域为当前shell进程及其子进程
局部变量:作用域仅为某代码片段(函数上下文)
位置参数变量:向执行脚本的shell进程传递的参数
特殊变量:shell内置的有特殊功用的变量;例如$?保存上一个命令执行的状态结果
0:成功
1-255:失败
本地变量:只对当前shell进程有效
变量赋值:name=value
name=$user
name=`Command`
name=$(Command)
这里需要注意的是“ 和 $()的意义是不同的。
推荐使用$()
变量引用:${name},$name
""弱引用:变量名会替换为其值
''强引用:变量名不会替换为其值
查看变量:set
撤销变量:unset name
注意:此处非变量引用,不加$
环境变量:对当前进程及其子进程有效,对父进程无效(除非写进配置文件,并且重新读取配置文件)
变量赋值:
(1)export name=value
(2)name=value
export name
(3)declare -x name=value
(4)name=value
declare -x name
变量引用:${name},$name
注意:bash内嵌了许多环境变量(通常为全大写字符),用于定义bash的工作环境
PATH,HISTFILE,HISTSIZE,HISTFILESIZE,HISTCONTROL,SHELL,HOME,UID,PWD,OLDPWD
查看环境变量:export,declare -x,printenv,env
撤销环境变量:unset name
只读变量:不能修改和撤销
(1)declare -r name
(2)readonly name
只读变量无法重新赋值,并且不支持撤销;存活时间为当前shell进程的生命周期,随shell进程终止而终止
位置参数变量:向脚本传递参数
应用范围格式:脚本后加各个参数,彼此之间用空白符隔开
eg: script.sh argu1 argu2
引用方式:$1,$2,...,${10},${11},...
轮替:shift [n] 位置参数轮替;
shift应用效果示例
#!/bin/bash
#
echo "First pos argu:$1"
shift
echo "Second pos argu:$1"
echo "First pos and Second pos:$1,$2 "
shift 2
echo "end pos:$1"
所得结果:
[root@localhost bin]# Shift.sh one two three four
First pos argu:one
Second pos argu:two
First pos and Second pos:two,three
end pos:four
在示例中我们看到,$1的初始值为第一个参数one,但是遇到shift之后$1的值变为第二个参数了
而且shift [n]可以一次性轮替n个参数
类似效果
$1 $2 $3 $4 ....
1 2 3 4 第一次遇到shift
2 3 4 第二次遇到shift 第一次遇到shift 2
3 4 第三次遇到shift
4 第四次遇到shift 第二次遇到shift 2
特殊变量:
$0:脚本文件路径本身;
$#:脚本参数的个数
$*:所有参数,所有参数作为一个字符串
$@:所有参数,每个参数作为独立的字符串
加引号时$*与$@时有这区别
局部变量:对当前shell进程中的某代码片段有效(通常指函数上下文)
PATH变量定义位置:.bash_profile –> $PATH:$HOME/binPATH=$PATH:$HOME/.local/bin:$HOME/bin –.local/bin centos7普通用户有的隐藏的目录,可以放写隐藏的脚本
写脚本的时候可以先mkdir /home/bin 在bin目录下写脚本,可省去相对路径。source bash.sh 也可以执行脚本:其执行过程相当于直接在当前shell进程中进行,而不是开一个子进程进行,所有脚本执行完,echo 变量,还可以查看到变量的值。(正常父进程是不能查看子进程的变量的)shadow 默认权限000 但是root用户属于超级用户 可读可写,但是如果文件没有x权限,root也不能执行
bash特性之多命令执行:
~]#COMMAND1;COMMAND2;COMMAND3;...
逻辑运算:
运算数:真(true,yes,on,1)
假(false,no,off,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,不同则为1
短路法则:可以提前判断出结果
~]#COMMAND1 && COMMAND2
COMMAND1为“假”,则COMMAND2不会再执行
否则,COMMAND1为“真”,则COMMAND2必须执行
~]#COMMAND1 || COMMAND2
COMMAND1为“真”,则COMMAND2不会再执行
否则,COMMAND1为“假”,则COMMAND2必须执行
示例:~]#id $username || useradd $username
示例: ~】#id $username ||useradd $username
shell脚本编程:
编程语言的分类:根据运行方式
编译运行:源代码-->编译器(编译)-->程序文件
解释运行:源代码-->运行时启动解释器,由解释器边解释边运行
根据其编程过程中功能的实现是调用库还是外部的程序文件
shell脚本编程:利用系统上的命令及编程组件进行编程
完整程序:利用库或编程组件进行编程
编程模型:过程式编程语言,面向对象的编程语言
程序=指令+数据
过程式:以命令为中心来组织代码,数据是服务于代码
顺序执行
选择执行
循环执行
代表:C,bash
对象式:以数据为中心来组织代码,围绕数据来组织指令
类(class):实例化对象(数据结构),method;
代表:java,C++,Python
shell脚本编程:过程式编程、解释运行、依赖于外部程序文件运行;
如何写shell脚本:
常见的解释器:shellbang
#!/bin/bash
#!/user/bin/python
#!/user/bin/perl
文本编辑器:nano
行编辑器:sed
全屏幕编辑器:nano、vi、vim
shell脚本是什么?
命令的堆积:
但很多命令不具有幂等性,需要用程序逻辑来判断运行条件是否满足,,以避免其运行中发生错误
运行脚本:
(1)赋予执行权限。并直接运行此程序文件;
chmod +x /PATH/SCRIPT_FILE
/PATH/TO/SCRIPT_FILE
(2)直接运行解释器,将脚本以命令行参数传递给解释器程序
bash/PATH/TO/SCRIPT_FILE
注意:脚本中的空白行会被解释器忽略
脚本中,除了shebang,余下所有以#开头的行,都会被视作注释行而被忽略;此即为注释行;
shell脚本的运行是通过运行一个子shell进程实现的
bash的配置文件:
两类:
profile类:为交互式登录的shell提供配置
bashrc类:为非交互式登录的shell进程提供配置
登录类型:
交互式登录shell进程:
直接通过某终端输入账号和密码后登陆打开的shell进程
使用su命令:su - USERNAME,或者使用su -l USERNAME执行的登录切换
非交互式登录shell进程:
su USERNAME执行的登录切换
图像界面下打开的终端
运行脚本(bash子进程)
profile类:
全局:对所有用户都生效;
/etc/profile
/etc/profile.d/*.sh
用户个人:仅对当前用户有效
~/.bash_profile
功用:
1、用于定义环境变量
2、运行命令或脚本
bashrc类:
全局:/etc/bashrc
用户个人:~/.bashrc
功用:
1、定义本地变量
2、定义命令别名
注意:仅管理员可修改全局配置文件
交互式登录shell进程:
/etc/profile-->/etc/profile.d/*-->~/.bash_profile-->~/.bashrc-->/etc/bashrc
非交互式登录shell进程:
~/.bashrc-->/etc/bashrc-->/etc/profile.d/*
命令行中定义的特性,例如变量和别名作用域为当前shell进程的生命周期
配置文件定义的特性,只对随后新启动的shell进程有效
让通过配置文件定义的特性立即生效:
(1)通过命令行重复定义一次
(2)让shell进程重读配置文件
~]#source /PAHT/FROM/CONF_FILE
~]#./PATH/FROM/CONF_FILE
变量:
本地变量、环境变量、局部变量、位置参数变量、特殊变量
变量赋值:name=value,export name=value,declare -x name=value
变量引用:$name,${name}
注意:有些时候{}不能省略,例如
myvalue=
撤销:unset name
bash脚本编程,运行脚本
#!/bin/bash shebang
# 注释
空白行 忽略不显示行
bash的配置文件
porfile类:登录式shell
bashrc类:非登录式shell
登录式shell:/etc/profile-->/etc/profile.d/*.sh-->~/.bash_profile-->~/.bashrc-->/etc/bashrc
非登录式shell:~/.bashrc-->/etc/bashrc-->/etc/profile.d/*.sh
二、总结位置变量和特殊变量:
$1,$2.. ${10},${11}..:对应调用的第1、第2个等参数;用于让脚本在脚本代码中通过调用命令行中的传递的参数,1和2 等分别代表第一个参数和第二个参数...,shift可以替换参数
特殊变量:$?:判断执行结果0-255
$0:表示命令本身脚本名称
$#:传递给脚本参数的个数
$*:传递给脚本的所有参数(所有参数整体一次性传递给脚本)
$@:引用传递给脚本的所有参数(每个参数单独为一个整体一次性传递给脚本)
$*与$@的区别:
相同点:都是引用所有参数
不同点:只有在双引号中体现出来
假设你的脚本运行时你写了三个参数 分别存储在$1 $2 $3中
则"$*" 等价于 “$1 $2 $3" --->传递了一个参数
而“$@" 等价于 "$1" "$2" "$3" --->传递了三个参数
例证:
三、作业:
1、编写脚本/root/bin/systeminfo.sh,显示当前主机系统信息,包括主机名,IPv4地址,操作系统版本,内核版本,CPU型号,内存大小,硬盘大小。
解决思路:
分步拆解,将所要的信息用合理的方法先单独获取到,再整合到shell script中
主机名:有两种简单的获取方式
hostname #hostname命令(推荐)
echo $HOSTNAME #利用hostname命令所调用的环境变量
uname -n #利用uname命令直接获取(基本上包括很多系统信息)
IPv4地址:利用ifconfig命令、扩展正则表达式、head命令获取
ifconfig |egrep -o "((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[1-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[1-9])"|head -1
此种方法看上去虽然很乱和繁琐,但是此公式的实用性很广,在Centos6与7中,ifconfig中获取的ip信息格式有些许不同,这就会造成我们从一个版本到另一个版本时,要调整表达式,而用上述表达式则可以尽量避免此种情况
操作系统版本:
cat /etc/redhat-release #适用于6、7版本(推荐适用性广、直接获取到我们需要的,没有其它)
lsb_release #适用于6
cat /etc/issue #适用于6
内核版本:
uname -r
CPU型号:
lscpu |head -n 13|tail -n +13|cut -d: -f2|tr -s " "
内存大小:
free -h |head -2|tail -1|tr -s " "|cut -d" " -f2
硬盘大小:
lsblk -d|egrep "^sd.*"|tr -s " "|cut -d" " -f1,4
注意:在不同版本中和不同的语言环境下,命令行不一定通用,例如CPU型号,我的centos7安装了中文环境,在不调整语言环境和命令行的情况下是与centos6英文版,不通用,所以大家要多多注意
根据上面的信息逐一获取方法我们可以编写以下内容作为/root/bin/systeminfo.sh脚本
vim /root/bin/systeminfo.sh
注意:在运行之前需要给脚本添加执行权限哦
chmod +x /root/bin/systeminfo.sh
这样就可以直接运行脚本文件了
直接运行
绝对路径:/root/bin/systeminfo.sh
相对路径:./systeminfo.sh
不赋予执行权限就只能再打开一个bash子进程解析运行此脚本了,但是注意结果能得到,但是在我们以后用配置脚本的话,则不见用用此方法,因为子进程运行的变量是不能影响父进程的
间接运行:
bash /root/bin/systeminfo.sh
以下每题都是默认赋予执行权限我就不说了
2、编写脚本/root/bin/backup.sh,可实现每日将/etc/目录备份到/root/etcYYYY-mm-dd中
3、编写脚本/root/bin/disk.sh,显示当前硬盘分区中空间利用率最大的值
4、编写脚本/root/bin/links.sh,显示正连接本主机的每个远程主机的IPv4地址和连接数,并按连接数从大到小排序
5、写一个脚本/root/bin/sumid.sh,计算/etc/passwd文件中的第10个用户和第20用户的ID之和
6、写一个脚本/root/bin/sumspace.sh,传递两个文件路径作为参数给脚本,计算这两个文件中所有空白行之和
7、写一个脚本/root/bin/sumfile.sh,统计/etc, /var, /usr目录中共有多少个一级子目录和文件
8、写一个脚本/root/bin/argsnum.sh,接受一个文件路径作为参数;如果参数个数小于1,则提示用户“至少应该给一个参数”,并立即退出;如果参数个数不小于1,则显示第一个参数所指向的文件中的空白行数
9、写一个脚本/root/bin/hostping.sh,接受一个主机的IPv4地址做为参数,先判断是否合格IP,否,提示IP格式不合法并退出,是,测试是否可连通。如果能ping通,则提示用户“该IP地址可访问”;如果不可ping通,则提示用户“该IP地址不可访问”
10、chmod -rw /tmp/file1,编写脚本/root/bin/per.sh,判断当前用户对/tmp/file1文件是否不可读且不可写
11、编写脚本/root/bin/nologin.sh和login.sh,实现禁止和充许普通用户登录系统。
12、计算1+2+3+...+100的值
echo {1..10}|tr " " "+"|bc
echo $((`echo {1..10}|tr " " "+"`))
13、计算从脚本第一参数A开始,到第二个参数B的所有数字的总和,判断B是否大于A,否提示错误并退出,是则计算之