shell脚本编程
扩展知识
编译器 | 解释器
编程语言的分类:
机器语言、汇编语言、高级语言
静态语言:编译型语言
强类型(变量类型):变量使用前必须事先声明,甚至需要初始化。未初始化的变量存储随机数。
事先转换成可执行格式
c\c++\java\c#
动态语言:解释型语言, on the fly
弱类型:变量无需事先声明,可直接使用。甚至不区分类型,默认为字符串。
边解释边执行
python\php\shell\perl
shell脚本属于面向过程。
一、变量的介绍
变量:内存空间
命名的内存空间。
字符型:10 ---16bit
数值型:10 ---> 1010 ----4bit 需要 8bit(最小存储单位)
不同变量类型具有不同的数据格式,所需的内存空间也不一样。
变量类型:事先定义数据的存储格式和长度。
字符
数值
整型
浮点型
布尔型
变量赋值
set VAR_NAME = VALUE
二、bash变量类型
本地变量(局部变量)
环境变量
位置变量
特殊变量
2.1 本地变量:
VARNAME=VALUE 作用域是整个bash进程。
局部变量: local VARNAME=VALUE 仅对当前代码段有效。
[root@localhost ~]# NAME=Beny
[root@localhost ~]# echo $NAME
Beny
[root@localhost ~]# bash
[root@localhost ~]# echo $NAME
引用变量:${VARNAME} ,括号有时可省略
[root@localhost ~]# ANIMAL=pig
[root@localhost ~]# echo "There are some $ANIMALs.
> "
There are some .
[root@localhost ~]# echo "There are some ${ANIMAL}s."
There are some pigs.
单引号是强引用,不实现变量替换。双引号是弱引用,可以实现变量替换。
[root@localhost ~]# echo 'There are some ${ANIMAL}s.'
There are some ${ANIMAL}s.
2.2 环境变量:
作用域为当前shell进程及其子进程。
export VARNAME=VALUE
VARNAME=VALUE
export VARNAME
脚本在执行时会启动一个子shell进程。
命令行中启动的脚本会继承当前shell环境变量。
系统自动执行的脚本(非命令行启动)就需要自我定义需要的环境变量。
[root@localhost ~]# export NAME
[root@localhost ~]# echo $NAME
Beny
[root@localhost ~]# bash
[root@localhost ~]# echo $NAME
Beny
[root@localhost ~]# bash
[root@localhost ~]# echo $NAME
Beny
2.3 位置变量:
$1 $2 $3 ……
在脚本中引用参数。
./file.sh /etc/fstab /etc/inittab
$1: /etc/fstab
$2: /etc/inittab
练习:接受一个参数(文件路径),判断此参数如果存在,就显示OK,否则显示NO Such file。
[root@localhost shell_example]# bash -x filetest1.sh /etc/fstab
+ '[' '!' -e /etc/fstab ']'
+ echo OK
OK
[root@localhost shell_example]# bash -x filetest1.sh
+ '[' '!' -e ']'
+ echo OK
OK
shift 将使用过的参数清空,后一个参数前移---$2变成$1
#!/bin/bash
echo $#
echo $*
echo $1
shift
echo $1
shift
echo $1
[root@localhost shell_example]# bash -x shift.sh 1 2 3
+ echo 3
3
+ echo 1 2 3
1 2 3
+ echo 1
1
+ shift
+ echo 2
2
+ shift
+ echo 3
3
#!/bin/bash
if [ $# -lt 2 ] ; then
echo "usage: ./add.sh arg1 arg2"
exit 3
fi
echo "The sum is : $[$1+$2]."
echo "The prod is : $[$1*$2]."
[root@localhost shell_example]# ./add.sh 2 6
The sum is : 8.
The prod is : 12.
[root@localhost shell_example]# ./add.sh 2 6 3
The sum is : 8.
The prod is : 12.
2.4 特殊变量:
$? : 上一个命令执行状态返回值
[root@localhost ~]# ls /var/
account caozesheng_py empty gopher local mail preserve target yp
adm crash games kerberos lock nis run tmp
cache db gdm lib log opt spool var
[root@localhost ~]# echo $?
0
[root@localhost ~]# ls /vaarr
ls: 无法访问/vaarr: 没有那个文件或目录
[root@localhost ~]# echo $?
2
[root@localhost ~]# lss /var
bash: lss: 未找到命令...
相似命令是: 'ls'
[root@localhost ~]# echo $?
127
程序执行,可能有两类返回值:
程序执行结果
程序执行返回代码(0-255)
0:正确执行
1-255:错误执行。 1、2、127 系统预留。
输出重定向:
>
>>
2>
2>>
&>
/dev/null : 软件模拟的设备。 bit bucket 位桶。数据黑洞。
$#:参数的个数
$* :参数列表
$@:参数列表
#!/bin/bash
echo $#
echo $*
echo $@
if [ $# -lt 1 ] ;then
echo "Usage: ./filetest1.sh arg1 [arg2,....]"
exit 7
fi
if [ ! -e $1 ] ; then
echo "no such file"
else
echo "OK"
fi
2.5 如何撤销变量:
unset VARNAME 撤销变量
[root@localhost ~]# echo $NAME
Beny
[root@localhost ~]# unset NAME
[root@localhost ~]# echo $NAME
查看当前shell中的变量:
set
查看当前shell中的环境变量:
printenv
env
export
[root@localhost ~]# ANIMALS=pig
[root@localhost ~]# ANIMALS=$ANIMALS:goat
[root@localhost ~]# echo $ANIMALS
pig:goat
[root@localhost ~]# ANIMALS=$ANIMALS:sheep
[root@localhost ~]# echo $ANIMALS
pig:goat:sheep
[root@localhost ~]# a=1
[root@localhost ~]# b=2
[root@localhost ~]# c=$a+$b
[root@localhost ~]# echo $c
1+2
三、脚本:命令的堆砌
按照实际需要,结合命令流程控制机制实现的源程序。
shebang:魔数
#!/usr/bin/bash
# 注释行,不执行。
<span style="color:#ff0000;">[root@localhost ~]# bash first.sh </span>
#
# /etc/fstab
# Created by anaconda on Sat Apr 16 18:39:54 2016
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
/dev/mapper/centos-root / xfs defaults 0 0
UUID=0c8c0c2c-d3b8-4658-b899-0fed6a2e94d2 /boot xfs defaults 0 0
/dev/mapper/centos-swap swap swap defaults 0 0
account caozesheng_py empty gopher local mail preserve target yp
adm crash games kerberos lock nis run tmp
cache db gdm lib log opt spool var
练习:
添加5个用户,user1、user2.。。。。
密码同用户名。添加密码完成后不显示passwd命令的执行结果信息。
每个用户添加完成后,显示“用户xxxx已经成功添加”
#!/usr/bin/bash
for i in `seq 5`
do
useradd user${i}
echo user${i} | passwd --stdin user${i} &> /dev/null
echo "用户 user${i} 已经成功添加"
done
练习:
使用一个变量保存一个用户名
删除此变量中的用户,且一并删除其家目录。
显示“用户删除完成”类的信息。
#!/usr/bin/bash
USER=user1
userdel -r $USER
echo "用户删除完成"
3.1 条件判断
条件测试类型:
整数测试
字符测试
文件测试
条件测试的表达式:
[ expression ] 命令测试法
[[ expression ]] 关键字测试法
test expression
3.1.1 整数比较:双目操作,一般需要两个操作数
-eq : 测试两个整数是否相等。 例:$A -eq $B
[root@localhost shell_example]# A=3
[root@localhost shell_example]# B=6
[root@localhost shell_example]# [ $A -eq $B ]
[root@localhost shell_example]# echo $?
1
[root@localhost shell_example]# B=3
[root@localhost shell_example]# [ $A -eq $B ]
[root@localhost shell_example]# echo $?
0
-ne:测试两个整数 是否不等。
-gt:测试一个数是否大于另一个数。大于为真,否则为假。
-lt:测试一个数是否小于另一个数。
-ge: 大于或等于
-le:小于或等于
命令间的逻辑关系
逻辑与: &&
[root@localhost shell_example]# id user2 &> /dev/null && echo "Hello, user2"
Hello, user2
[root@localhost shell_example]# id user1 &> /dev/null && echo "Hello, user2"
逻辑或: ||
id user0 && echo "user0 exists." || useradd user0
! id user0 && useradd user0 || echo "user0 exists."
! id user0 && useradd user0 && echo "user0" | passwd --stdin user0 || echo "user0 exists."
1、命令的执行状态结果可以作为判断条件 -- "$?"
条件判断,控制结构:
单分支if语句:
if 判断条件; then
suit1
suit2
.......
fi
双分支if语句:
if 判断条件
then
suit1
suit2
...
else
suit3
suit4
...
fi
case语句
case SWITCH in
value1)
statment
...
;;
value2)
statment
....
;;
*)
statment
……
;;
esac
#!/bin/bash
echo $1
case $1 in
[0-9])
echo "A digit" ;;
[a-z])
echo "Lower" ;;
[A-Z])
echo "Upper" ;;
*)
echo "Spicial character" ;;
esac
练习:
判断当前系统上是否有用户的默认shell为bash。如果有,就显示有多少个这类用户,否则显示没有这类用户。
显示其中一个这样的用户名。
#!/bin/bash
grep "\<bash$" /etc/passwd &> /dev/null
RETVAL=$?
if [ $RETVAL -eq 0 ]
then
AUSER=`grep "\<bash$" /etc/passwd | head -1 | cut -d: -f1`
USERS=`grep "\<bash$" /etc/passwd | wc -l`
echo "$USERS user's shell is bash"
echo "Such as $AUSER"
else
echo "No such users"
fi
练习:
给定一个文件,/etc/inittab,判断是否有空白行,若有,显示空白行数,否则显示无空白行。
#!/bin/bash
FILE=/etc/inittab
if [ ! -e $FILE ]
then
echo "File is not exists."
exit 0
fi
if grep "^$" $FILE &> /dev/null
then
echo "Tatal blank lines is : `grep "^$" $FILE | wc -l`"
else
echo "No blank lines"
fi
练习:
给定用户,判断密码警告期限,判断时间间距是否小于警告期限,如果小于,提示warning,否则,ok
#!/bin/bash
USERNAME=root
TIMESTAMP=`date +%s`
TODAY=$[$TIMESTAMP/86400]
MAX=`grep "^root" /etc/shadow | cut -d: -f5`
CHANGE=`grep "^root" /etc/shadow | cut -d: -f3`
USE=$[$TODAY-$CHANGE]
REMAIN=$[$MAX-$USE]
WARNINGDAY=`grep "^root" /etc/shadow | cut -d: -f6`
if [ $WARNINGDAY -gt $REMAIN ]
then
echo "Warning"
else
echo "OK"
fi
shell中进行算术运算
[root@localhost ~]# A=3
[root@localhost ~]# B=3
[root@localhost ~]# C=$A+$B
[root@localhost ~]# echo $C
3+3
[root@localhost ~]#<span style="color:#ff0000;"> let C=$A+$B</span>
[root@localhost ~]# echo $C
6
[root@localhost ~]# <span style="color:#ff0000;">D=$[$A+$B]</span>
[root@localhost ~]# echo $D
6
[root@localhost ~]#<span style="color:#ff0000;"> E=$(($A+$B))</span>
[root@localhost ~]# echo $E
6
exper 算术运算表达式,表达式中各操作数及运算符之间要有空格,而且要使用命令引用
[root@localhost ~]# F=`expr $A + $B `
[root@localhost ~]# echo $F
6
3.2 文件测试
-e FILE:文件是否存在
-f FILE: 文件是否为普通文件
-d FILE:指定路径是否为目录
-r FILE:当前用户对指定文件是否有读权限
-w 写
-x 执行
[ -e /etc/passwd ]
多分支if语句
if 判断条件1
then
suit1
.......
elif 判断条件2
then
suit2
.....
elif 判断条件3
then
suit3
......
else
suit4
.....
fi
#!/bin/bash
FILE=/etc/passwd
if [ ! -e $FILE ] ; then
echo "NO such file"
exit 6
fi
if [ -f $FILE ] ; then
echo "It's common file"
elif [ -d $FILE ] ; then
echo "It's a dictionary"
else
echo "I don't kown"
fi
测试脚本是否有语法错误
bash -n
bash -x 单步执行的过程显示。
[root@localhost shell_example]# bash -x file.sh
+ FILE=/etc/inittab
+ '[' '!' -e /etc/inittab ']'
+ grep '^$' /etc/inittab
+ echo 'No blank lines'
No blank lines
exit:退出脚本
exit n 自定义运行结束状态码
如果脚本没有明确定义退出状态码,那么,最后一条命令执行的退出码即为脚本的退出状态码
[root@localhost shell_example]# bash -x filetest.sh
+ FILE=/etc/asdf
+ '[' '!' -e /etc/asdf ']'
+ echo 'NO such file'
NO such file
<span style="color:#ff0000;">+ exit 6</span>
字符串测试
== / = :两个字符串是否相等。 等号两端需要空格。
!= :是否不等。 != 需要空格。
>
<
-n string : 测试指定字符串是否为空,空则真。
-z string : 测试指定字符串是否不空,不空则真。
[root@localhost shell_example]# [ $A == $B ]
[root@localhost shell_example]# echo $?
1
[root@localhost shell_example]# [ $A = $B ]
[root@localhost shell_example]# echo $?
1
[root@localhost shell_example]# B=hello
[root@localhost shell_example]# [ $A = $B ]
[root@localhost shell_example]# echo $?
0
[root@localhost shell_example]# [ -e asdf ]
[root@localhost shell_example]# echo $?
1
[root@localhost shell_example]# [ -e ]
[root@localhost shell_example]# echo $?
0
循环控制结构
for
while
until
for 变量 in 列表
do
循环体
done
生成列表:
{1..100}
seq
SYNOPSIS
seq [OPTION]... LAST
seq [OPTION]... FIRST LAST
seq [OPTION]... FIRST INCREMENT(步进) LAST
seq [OPTION]... LAST
seq [OPTION]... FIRST LAST
seq [OPTION]... FIRST INCREMENT(步进) LAST
for i in `seq 10`
do
........
done
declare -i SUM 声明SUM就是个整型。
#!/bin/bash
let SUM=0
for i in `seq 100`
do
let SUM=$[$SUM+$i]
done
echo $SUM
组合测试条件:
-a :与关系
-o :或关系
! :非关系
if [ $# -gt 1 -a $# -le 3 ]
if [ $# -gt 1 ] && [ $# -le 3 ]
练习:
1、向系统的所有用户问好,并显示shell
#!/bin/bash
LINES=`wc -l /etc/passwd | cut -d' ' -f1`
for i in `seq 1 $LINES`
do
echo "hello `head -n $i /etc/passwd | tail -1 | cut -d: -f1` and your shell is `head -n $i /etc/passwd | tail -1 | cut -d: -f7`"
done
~
2、添加10个用户,用户名等同密码。接受参数add添加,del删除,其他字符串,退出。
#!/bin/bash
if [ $1 == 'add' ] ; then
for i in `seq 10` ; do
useradd user$i
echo "user$i" | passwd --stdin user$i &>/dev/null
done
elif [ $1 == 'del' ]; then
for i in `seq 10` ; do
userdel -r user$i
done
else
echo "Quiting......"
exit 10
fi
3、100以内被3整除的整数的和。
#!/bin/bash
declare -i sum=0
for i in `seq 100` ; do
if [ $[$i%3] -eq 0 ] ;then
sum=$[$sum+$i]
else
echo "$i not div 3"
fi
done
echo "The sum is : $sum"
4、100以内奇数的和和偶数的和,分别显示。
#!/bin/bash
declare -i sum1=0
declare -i sum2=0
for i in `seq 0 2 100`; do
sum2=$[$sum2+$i]
done
for i in `seq 1 2 100`; do
sum1=$[$sum1+$i]
done
echo "The odd number sum is : $sum1 ."
echo "The even number sum is : $sum2 ."
5、分别统计shell为bash,nologin的用户,并统计用户总数。
declare -i bashnum=0
declare -i nologinnum=0
let num=`wc -l /etc/passwd | cut -d' ' -f1`
for i in `seq $num` ;do
username=`head -n $i /etc/passwd | tail -1 | cut -d: -f1`
shell=`head -n $i /etc/passwd | tail -1 | cut -d: -f7`
if [ `echo $shell | sed -r "s@^/.*/([^/]+/?)@\1@g" ` == 'bash' ]; then
bashnum=$[$bashnum+1]
echo $username >> /tmp/bash.txt
elif [ `echo $shell | sed -r "s@^/.*/([^/]+/?)@\1@g"` == 'nologin' ]; then
nologinnum=$[$nologinnum+1]
echo $username >> /tmp/nologin.txt
fi
done
练习:
1、传递一个用户名参数给脚本,判断此用户的用户名跟其基本组的组名是否一致,并将结果输出
#!/bin/bash
if ! id $1 &>/dev/null
then
echo "no such user"
exit 10
fi
if [ `id -n -u $1` == `id -n -g $1` ]
then
echo "same"
else
echo "no same"
fi
2、传递三个参数给脚本,第一个是整数、第二个是运算符、第三个是整数,将计算结果输出,保留两位精度。
echo "scale=2;$1$2$3;" | bc
[root@localhost shell_example]# bc <<< "scale=2;5/3;"
3、传递三个用户名给脚本,将这些用户的账号信息提取出来放置于/tmp/testusers.txt文件中,每行行首有行号。
4、判断当前主机CPU生产商,/proc/cpuinfo 中 vandor id,判断是哪个生产商,就显示结果是AMD或Intel,否则是非主流公司,
5、给脚本3个整数,判断最大最小数并输出。
6、传递一个参数-单字符,如果参数为q或者Q,或quit,或Quit,退出,否则,显示用户的参数。
#!/bin/bash
if [ $1 == 'q' ]
then
echo "quiting..."
exit 1
elif [ $1 == 'Q' ]
then
echo "quiting..."
exit 2
elif [ $1 == 'quit' ]
then
echo "quiting..."
exit 3
elif [ $1 == 'Quit' ]
then
echo "quiting..."
exit 4
else
echo $1
fi
7. 传递多个参数,-c -v -h 来查看当前系统的登录用户,以及详细信息。
#!/bin/bash
#
declare -i count=0
declare -i info=0
for i in `seq 1 $#`; do
if [ $# -gt 0 ]; then
case $1 in
-h|--help)
echo "usage: `basename $0` -h|--help -c|--count -v|--verbose"
exit 0
;;
-v|--verbose)
let info=1
shift
;;
-c|--count)
<span style="white-space:pre"> </span> let count=1
shift
;;
<span style="white-space:pre"> </span>*)
echo "usage: `basename $0` -h|--help -c|--count -v|--verbose"
exit 7
<span style="white-space:pre"> </span> ;;
<span style="white-space:pre"> </span>esac
fi
done
if [ $count -eq 1 ]; then
echo "Logged users: `who | wc -l`."
if [ $info -eq 1 ]; then
echo "Logged users: `who | wc -l`. "
echo "They are: "
w
fi
fi
<strong>
</strong>