什么是Shell
操作系统最外层的程序,shell通过提示符让用户输入,向操作系统解释该输入,然后处理来自操作系统的任何结果输出来,管理用户与操作系统之间的交互。
Shell是一个用户跟操作系统之间的一个命令解释器。Shell是用户与Linux操作系统之间沟通的桥梁。用户可以输入命令执行,又可以利用 Shell脚本编程去运行。
为什么要用到shell
shell是一个交互式程序,当用户输入一条命令,shell就解释一条,一次只处理一条命令。如果我们一些复杂操作,逐个敲命令工作量就会增大,因此,我们可以事先写一个脚本,在脚本中写入多条命令,让shell一次性把这些命令全都执行完毕,而不必一条一条的敲
常见的shell种类
/bin/sh,/bin/csh,/bin/ksh,/bin/bash等,bash是大多数linux默认的shell程序,因此,在日常工作中被广泛使用
编程基础:
程序:指定+数据
指令:由程序文件提供
数据:IO设备、文件、管道、变量等
程序编程风格:
过程式:以指令为中心,数据服务于指令
对象式:以数据为中心,指令服务于数据
编程语言:
强类型:必须实现声明定义的变量及变量类型。如 Java、Python
弱类型:无需事先声明变量,默认均为字符串类型,可直接调用变量。如 shell
程序运行方式:
编译运行:源代码 --> 编译器编译 --> 可执行的二进制文件
解释运行:源代码 --> 运行时启动解释器,边解释边运行
如何编写shell脚本:
脚本文件的第一行,顶格:给出shebang,解释器路径,用于指明解释执行当前脚本的解释器程序文件
常见的解释器:
#!/bin/bash
#!/usr/bin/perl
#!/usr/bin/python
编写第一个shell脚本:
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: first shell script
echo "Hello,world"
[root@CentOS6 bin]# bash first.sh
Hello,world
[root@CentOS6 bin]#
一个好的shell脚本会标明、作者、日期、版本、作用,这是一个良好的习惯,不然我们代码写到百行的时候,过几个月或几年,回过头来看,又能知道自己写的是什么脚本呢
如何运行shell脚本:
(1)赋予脚本文件执行权限,并直接运行脚本
chmod +x SCRIPT_FILE
./PATH/TO/SCRIPT_FILE
(2)直接运行解释器,将脚本以命令参数传递给解释器程序
bash /PATH/TO/SCRIPT_FILE
变量:指向内存的命令空间
变量名+指向的内存空间
变量赋值:VAR_NAME=value
变量类型:存储格式、表示数据范围、参与的运算
字符型
数值:整型、浮点型(shell不支持浮点型)
变量替换:把变量名出现的位置替换为指定的内存空间中的数据
变量引用:${var_name},$var_name
变量名命名规则:
1.不能与系统中定义过的变量同名,不能与bash中的关键字同名,如 if case等等
2.只能包含数字、下划线、字母
3.要做到见名只义
bash变量类型:
本地变量:仅对当前shell进程生效
环境变量:对当前shell以及子shell生效
局部变量:仅对shell进程中某一代码片段生效
位置变量:脚本执行时传递给脚本的参数
特殊变量:shell内置的有特殊功用的变量
本地变量:
变量赋值:name='value'
变量引用:${name},$name
"":变量名会被替换为其值
'':变量名不会替换为值,当做字符串来处理
查看变量:set
撤销变量:unset var_name
环境变量:
变量赋值:
(1)export name='value'
(2)name='value'
export name
(3)declare -x name='value'
(4)name='value'
declare -x name
变量引用:${name},$name
查看环境变量:export、declare、env、printenv
撤销变量:unset
bash有许多内置的环境变量:PATH,SHELL,USER,UID,GID,HISTSIZE,HISTFILESIZE,HOME,PWD,OLDPWD,HISTFILE,HISTCONTROL等
只读变量:只能声明,不可修改删除
readonly var_name
declare -r var_name
位置变量:在执行脚本时,传递给脚本的参数
$1,$2,...${10}、${11}对应1、2个参数
特殊变量:shell内置变量
$?:存放上一条命令的执行状态返回码
$#:执行脚本时传递给脚本的参数个数
$*:传递给脚本的所有参数,会把这些参数当做一个字符串
$@:传递给脚本的所有参数,把每个参数当做一个字符串来处理
$0:此脚本的名称
$$:当前shell程序的PID
算术运算:
+, -, *, /, %, **
算术运算格式:
(1)let VAR=Expression
(2)VAR=$[Expression]
(3)VAR=$((Expression))
(4)VAR=$(expr $ARG1 $OP $ARG2)
(5)echo "Expression" | bc
bash內建随机数生成器:$RANDOM(1-32767)
增强型赋值:+=, -=, *=, /=, %=
例如:let COUNT+=3
COUNT等于自身加3
自增,自减:
let var+=1
let var++
let var-=1
let var--
i++运算后加1,i--运算后减1
++i运算前加1,--i运算前减1
逻辑运算:
运算数:
true:真,用1表示
false:假,用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
短路法则:
~]# COMMADN1 && COMMADN2
COMMADN1为“假”,COMMADN2将不会执行
否则,COMMADN1为“真”,COMMADN2必须执行
~]# COMMADN1 || COMMADN2
COMMAND1为“真”,则COMMADN2不会被执行
否则,COMMADN1为“假”,则COMMAND2必须执行
异或:^
异或的两个值,相同为假,不同则真
退出状态码:
进程使用退出状态码来报告命令的执行结果,成功或失败
$?:上一条命令的执行状态返回码
成功:0
失败:1-255
自定义状态返回值:
exit#:#为自己指定的状态返回码
注意:当脚本中遇到exit时,将终止整个脚本运行,默认是脚本中执行的最后一条命令的状态返回值
条件测试:
判断某个需求是都满足,如果满足则执行相应的操作,需要由测试机制来实现
如何编写测试表达式以实现所需的测试:
(1)评估布尔声明,以便用在条件测试中
若真,则返回0
若假,则返回非1
(2)测试表达式:
test EXPRESSION
[ EXPRESSION ]
[[ EXPRESSION ]]
注意:EXPRESSION前后两端要有空格,否则报错
bash测试类型:
数值测试:
字符串测试:
文件测试:
数值测试:数值比较
-eq:测试是否等于
-gt:是否大于
-lt:是否小于
-ge:是否大于等于
-le:是否小于等于
-ne:是否不等于
字符串测试:字符比较
==:测试字符是否相等
!=:是否不等于
=~:右侧的字符是否能被左侧的PATTERN所匹配
>:是否大于
<:是否小于
-z:测试指定字符串是否为空,空为真,否则为假
-n:测试指定的字符串是否不不空,不空则真,否则为假
注意:字符串比较实用[[ ]]
文件测试:
存在性测试:
[ -a FILE ]:测试文件是否存在
[ -e FILE ]
存在性及类型测试:
[ -b FILE ]:测试文件是否为快设备文件
[ -c FILE ]:测试文件是否为字符设备
[ -d FILE ]:测试文件是否为目录文件
[ -f FILE ]:测试文件是否为普通ASCII文件
[ -p FILE ]:侧是文件是否为管道文件
[ -h FILE ]或[ -L FILE ]:测试文件是否为符号链接文件
[ -S FILE ]:测试文件是否为套接字文件
文件权限测试:
[ -r FILE ]:测试当前用户是否对指定文件可读
[ -w FILE ]:测试当前用户是否对文件可写
[ -x FILE ]:测试当前用户是否对文件可执行
特殊权限测试:
[ -u FILE ]:测试文件是否具有SUID权限
[ -g FILE ]:测试文件是否具有SGID权限
[ -k FILE ]:测试文件是否有Sticky权限
测试文件中是否有内容:
[ -s FILE ]:测试文件中是否有内容,有为真,否则假
时间戳:
[ -N FILE ]:文件自从上次读取后是否被修改过
从属关系测试:
[ -O FILE ]:测试当前用户是否是文件的属主
[ -G FILE ]:测试当前用户是否是文件属组
双目测试:
[ FILE1 -ef FILE2 ]:FILE1与FILE2是否指向同一个文件系统的相同inode的硬链接
[ FILE1 -nt FILE2 ]:FILE1是否新于FILE2
[ FILE1 -ot FILE2 ]:FILE1是否旧于FILE2
组合条件测试:
第一种方式:
COMMAND1 && COMMAND2
COMMAND1 || COMMAND2
! COMMAND
[ -x FILE ] && [ -r FILE ]
第二种方式:
-a:与
-o:或
!:非
[ EXPRESSION1 -a EXPRESSION2 ]
[ EXPRESSION1 -o EXPRESSION2 ]
[ ! EXPRESSION ]
练习:
1.编写脚本/root/bin/systeminfo.sh,显示当前主机系统信息,包括主机名,IPv4地址,操作系统版本,内核版本,CPU型号,内存大小,硬盘大小。
[root@CentOS6 bin]
# cat systeminfo.sh #脚本内容
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 显示当前系统信息包括主机名,IPv4地址,操作系统版本,内核版本,CPU型号,内存大小,硬盘大小
Hostname=$(
hostname
)
IP=$(
ifconfig
|
sed
-n
'2p'
|
cut
-d: -f2 |
cut
-d
' '
-f1)
OS=$(lsb_release |
grep
"Description"
|
cut
-d: -f2 |
sed
's@^[[:space:]]\+@@'
)
Kernel=$(
uname
-r)
CPU=$(
cat
/proc/cpuinfo
|
grep
"model name"
|
cut
-d: -f2 |
sed
's@^ @@'
)
MEM=$(
free
-h |
tr
-s
' '
|
cut
-d
' '
-f2 |
sed
-n
'2p'
)
DISK=$(
fdisk
-l |
grep
"Disk /dev/sd"
|
cut
-d: -f2 |
cut
-d, -f1)
echo
-e
"\033[32mShow system information.\033[0m"
echo
"Hostname: $Hostname"
echo
"IPv4 address: $IP"
echo
"OS version: $Kernel"
echo
"CPU model: $CPU"
echo
"Mem size: $MEM"
echo
"Disk size: $DISK"
[root@CentOS6 bin]
# vim systeminfo.sh
[root@CentOS6 bin]
# bash systeminfo.sh #运行结果
Show system information.
Hostname: CentOS6.localdomain
IPv4 address: 10.1.252.233
OS version: 2.6.32-642.el6.x86_64
CPU model: Intel(R) Core(TM) i7-6500U CPU @ 2.50GHz
Mem size: 980M
Disk size: 107.4 GB
[root@CentOS6 bin]
#
2.编写脚本/root/bin/backup.sh,可实现每日将/etc/目录备份到/root/etcYYYY-mm-dd中
[root@CentOS6 bin]
# cat backup.sh
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 将/etc/目录下的内容备份到/root/etcYYYY-mm-dd中
FILE=
"/root/etc`date +%F`"
cp
-rpf
/etc/
$FILE
echo
"$FILE file backup finished."
[root@CentOS6 bin]
# bash backup.sh
/root/etc2016-08-12
file
backup finished.
[root@CentOS6 bin]
# ls -d ../etc2016-08-12/
..
/etc2016-08-12/
[root@CentOS6 bin]
#
3.编写脚本/root/bin/disk.sh,显示当前硬盘分区中空间利用率最大的值
[root@CentOS6 bin]
# cat disk.sh
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 显示当前硬盘分区中空间利用率最大的值
Max_Used=$(
df
-lhTP |
sed
-r
's@.* ([0-9]+%).*@\1@'
|
sed
'1d'
|
sort
|
tail
-1)
echo
"分区空间利用率最大为$Max_Used"
[root@CentOS6 bin]
# bash disk.sh
分区空间利用率最大为19%
[root@CentOS6 bin]
#
4.编写脚本/root/bin/links.sh,显示正连接本主机的每个远程主机的IPv4地址和连接数,并按连接数从大到小排序
[root@CentOS6 bin]
# cat links.sh
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 显示正连接本主机的每个远程主机的IPv4地址和连接数,并按连接数从大到小排序
echo
-e
"当前连接本机的远程主机IP\n"
echo
-e
"连接次数 IP地址"
netstat
-tn |
tr
-s
' '
|
cut
-d
' '
-f5 |
cut
-d: -f1 |
sed
'1,2d'
|
sort
|
uniq
-c |
sort
-n -t
' '
-k1
[root@CentOS6 bin]
# bash links.sh
当前连接本机的远程主机IP
连接次数 IP地址
2 10.1.250.60
3 10.1.253.23
[root@CentOS6 bin]
#
5.写一个脚本/root/bin/sumid.sh,计算/etc/passwd文件中的第10个用户和第20用户的ID之和
[root@CentOS6 bin]
# cat sumid.sh
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#descriptions: 计算/etc/passwd文件中的第10个用户和第20用户的ID之和
FILE=
"/etc/passwd"
ID_10=$(
cat
$FILE |
sed
-n
'10p'
|
cut
-d: -f3)
ID_20=$(
cat
$FILE |
sed
-n
'20p'
|
cut
-d: -f3)
SUMID=$[$ID_10+$ID_20]
echo
"$FILE file 10 user and 20 user uid sum is $SUMID."
[root@CentOS6 bin]
#
[root@CentOS6 bin]
# bash sumid.sh
/etc/passwd
file
10 user and 20 user uid
sum
is 180.
[root@CentOS6 bin]
#
6.写一个脚本/root/bin/sumfile.sh,统计/etc, /var, /usr目录中共有多少个一级子目录和文件
[root@CentOS6 bin]
# cat sumfile.sh
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 统计/etc, /var, /usr目录中共有多少个一级子目录和文件
ETC=$(
ls
-A
/etc/
|
wc
-l)
VAR=$(
ls
-A
/var/
|
wc
-l)
USR=$(
ls
-A
/usr/
|
wc
-l)
SUMFILE=$[$ETC+$VAR+$USR]
echo
"/etc/ /var/ /usr/ A total of $SUMFILE files"
[root@CentOS6 bin]
#
[root@CentOS6 bin]
# bash sumfile.sh
/etc/
/var/
/usr/
A total of 296 files
[root@CentOS6 bin]
#
7.写一个脚本/root/bin/argsnum.sh,接受一个文件路径作为参数;如果参数个数小于1,则提示用户“至少应该给一个参数”,并立即退出;如果参数个数不小于1,则显示第一个参数所指向的文件中的空白行数
[root@CentOS6 bin]
# cat argsnum.sh
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 接受一个文件路径作为参数;如果参数个数小于1,则提示用户“至少应该给一个参数”,并立即退出;如果参数个数不小于1,则显示第一个参数所指向的文件中的空白行数
[ $
# -lt 1 ] && echo "At least one parameter." && exit 1
SpaceLine=$(
grep
"^$"
$1 |
wc
-l)
echo
"$1 spaceline is $SpaceLine."
[root@CentOS6 bin]
#
[root@CentOS6 bin]
# bash argsnum.sh #没有给出参数,执行结果提示至少一个参数
At least one parameter.
[root@CentOS6 bin]
# bash argsnum.sh /etc/inittab #给出一个文件路径,显示此文件的空白行数
/etc/inittab
spaceline is 8.
[root@CentOS6 bin]
#
8.写一个脚本/root/bin/hostping.sh,接受一个主机的IPv4地址做为参数,测试是否可连通。如果能ping通,则提示用户“该IP地址可访问”;如果不可ping通,则提示用户“该IP地址不可访问”
[root@CentOS6 bin]
# cat hostping.sh
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 接受一个主机的IPv4地址做为参数,测试是否可连通。如果能ping通,则提示用户“该IP地址可访问”;如果不可ping通,则提示用户“该IP地址不可访问”
[ $
# -lt 1 ] && echo "At least one parameter." && exit 1
ping
-c 1 -W 1 $1 &>
/dev/null
&&
echo
"$1 IP accessible"
||
echo
"$1 IP not accessible."
[root@CentOS6 bin]
#
[root@CentOS6 bin]
# bash hostping.sh 10.1.1.1
10.1.1.1 IP not accessible.
#IP不可访问
[root@CentOS6 bin]
#
[root@CentOS6 bin]
# bash hostping.sh 10.1.252.233
10.1.252.233 IP accessible
#IP可访问
[root@CentOS6 bin]
#
9.chmod -rw /tmp/file1,编写脚本/root/bin/per.sh,判断当前用户对/tmp/fiile1文件是否不可读且不可写
[root@CentOS6 /]
# cat per.sh
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 判断当前用户对/tmp/fiile1文件是否不可读且不可写
FILE=
"/tmp/file1"
[ ! -r $FILE -a ! -w $FILE ] &&
echo
"yes"
||
echo
"no"
#如果不可读也不可写,则输出yes,否则输出no
[root@CentOS6 /]
#
[root@CentOS6 /]
# ll /tmp/file1 #首先查看/tmp/file1文件的权限,为640
-rw-r-----. 1 root root 0 Aug 11 22:43
/tmp/file1
[root@CentOS6 /]
#
[root@CentOS6 /]
# bash per.sh
no
#root用户对此文件可读写,输出no
[root@CentOS6 /]
# su - zhai #切换至其他用户
[zhai@CentOS6 ~]$
whoami
zhai
[zhai@CentOS6 ~]$
bash
/per
.sh
yes
#zhia用户对此文件不可读也不可写,输出yes
[zhai@CentOS6 ~]$
10.编写脚本/root/bin/nologin.sh和login.sh,实现禁止和充许普通用户登录系统。
用户不可登录nologin
[root@CentOS6 bin]
# cat nologin.sh
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 实现禁止普通用户登录系统
FILE=
"/etc/nologin"
([ ! -e $FILE ] &&
touch
$FILE &&
echo
"create $FILE finished. "
) ||
echo
"$FILE exists."
#判断/etc/nologin文件是否存在,如果不存在,则创建
[root@CentOS6 bin]
# bash nologin.sh
create
/etc/nologin
finished.
#提示创建文件成功,此时普通用户已经不可登录系统了
[root@CentOS6 bin]
#
用户可登录login
[root@CentOS6 bin]
# cat login.sh
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 实现允许普通用户登录系统
FILE=
"/etc/nologin"
([ -e $FILE ] &&
rm
-rf $FILE &&
echo
"delete $FILE finished."
) ||
echo
"$FILE not exists."
#判断/etc/nologin文件是否存在,如果存在就删除
[root@CentOS6 bin]
#
[root@CentOS6 bin]
# bash login.sh
delete
/etc/nologin
finished.
#提示删除文件完成,此时普通用户就可以登录到系统了
[root@CentOS6 bin]
#
11.写一个脚本/root/bin/hostping.sh,接受一个主机的IPv4地址做为参数,先判断是否合格IP,否,提示IP格式不合法并退出,是,测试是否可连通。如果能ping通,则提示用户“该IP地址可访问”;如果不可ping通,则提示用户“该IP地址不可访问”
[root@CentOS6 bin]
# cat hostping.sh
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 写一个脚本/root/bin/hostping.sh,接受一个主机的IPv4地址做为参数,先判断是否合格IP,否,提示IP格式不合法并退出,是,测试是否可连通。如果能ping通,则提示用户“该IP地址可访问”;如果不可ping通,则提示用户“该IP地址不可访问”
[ $
# -lt 1 ] && echo "At least one parameter." && exit 1
echo
"$1"
|
grep
-E -q
"\<([1-9]|[1-9][0-9]|1[0-9]{2}|2[0-1][0-9]|22[0-3])\>\.(\<([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\>\.){2}\<([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\>"
RETVAL=$?
[ $RETVAL -
ne
0 ] &&
echo
"$1 not is IP address."
&&
exit
5
(
ping
-c 1 -W 1 $1 &>
/dev/null
&&
echo
"$1 IP accessible"
) || (
echo
"$1 IP not accessible"
&&
exit
7)
[root@CentOS6 bin]
#
[root@CentOS6 bin]
# bash hostping.sh 255.1.1.1 #给定非IP地址
255.1.1.1 not is IP address.
[root@CentOS6 bin]
# bash hostping.sh 10.1.252.255 #给定正确IP地址
10.1.252.255 IP accessible
#此IP可访问
[root@CentOS6 bin]
# bash hostping.sh 10.1.252.252
10.1.252.252 IP not accessible
#此IP不可访问
[root@CentOS6 bin]
#
12.计算1+2+3+…+100的值
[root@CentOS6 bin]
# cat sum2.sh
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 计算1到100之间所有整数之和
SUM=$(
echo
{1..100} |
tr
' '
'+'
|
bc
)
echo
"1到100之间的所有整数之和为$SUM."
[root@CentOS6 bin]
#
[root@CentOS6 bin]
# bash sum2.sh
1到100之间的所有整数之和为5050.
[root@CentOS6 bin]
#
13.计算从脚本第一参数A开始,到第二个参数B的所有数字的总和,判断A是否大于B,否提示错误并退出,是则计算
[root@CentOS6 bin]
# cat sum3.sh
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 计算从脚本第一参数A开始,到第二个参数B的所有数字的总和,判断B是否大于A,否提示错误并退出,是则计算之
[ $
# -ne 2 ] && echo "Unknown Argument." && exit 1
!
expr
$1 + $2 &>
/dev/null
&&
echo
"Non integer parameter."
&&
exit
2
[ $1 -gt $2 ] &&
echo
"$1 greater $2."
&&
exit
3
SUM=$(
seq
$1 $2 |
tr
'\n'
' '
|
tr
' '
'+'
|
sed
-r
's@\+$@\n@'
|
bc
)
echo
"$1至$2之间所有整数之和为$SUM."
[root@CentOS6 bin]
#
[root@CentOS6 bin]
# bash sum3.sh #脚本后不跟参数,提示未知参数
Unknown Argument.
[root@CentOS6 bin]
# bash sum3.sh 1 a #给出的参数不是整数
Non integer parameter.
[root@CentOS6 bin]
# bash sum3.sh 10 1 #$1大于$2
10 greater 1.
[root@CentOS6 bin]
# bash sum3.sh 1 99 #正确执行脚本
1至99之间所有整数之和为4950.
[root@CentOS6 bin]
#
本文永久更新链接地址:http://www.linuxidc.com/Linux/2017-03/142274.htm