本文是上课笔记总结,涉及细节知识点会在以后文章说明!
bash脚本编程:
脚本程序:解释器解释执行;
shell: 交互式接口;编程环境;
shell: 能够提供一些内部命令,并且能通过PATH环境变量找到外部命令;把命令提交给内核启动为进程;
编程环境:
流程控制语句:
顺序执行;
循环执行;
选择执行;
条件测试:真、假
$?
命令的状态结果:
0: 真
1-255: 假
过程式的编程语言的元素:变量、流程、函数、数组
变量:局部变量、本地变量、环境变量、位置参数变量、特殊变量
变量:
数值型:整型、浮点型、布尔型
字符型:字符串
bash变量是弱类型;默认字符型;
变量引用:${VAR_NAME}
引号:
弱引用:""
强引用:''
命令引用:``
声明某变量为整型变量:
let VAR_NAME=VALUE
declare -i VAR_NAME=VALUE
声明某变量为环境变量:
export VAR_NAME=VALUE
declare -x VAR_NAME=VALUE
脚本的编写格式:
第一行:写明解释器; #!/bin/bash
注释行:所有以#开头的行均为注释行;会被解释器忽略;
执行脚本:
赋予执行权限;指明路径执行;
直接传递脚本给bash解释器
bash的选项:
-n: 测试脚本中是否有语法 错误;
-x: 调试执行;
算术运算:
$[EXPRESSION]
let VAR_NAME=EXPRESSION
$((EXPRESSION))
$(expr argu1 argu2 argu3)
流程控制之一:for循环
将一段代码反复执行;
表达式:
for VAR in LIST; do
STATEMENT1
...
done
for VAR in LIST; do statement1; statement2; ...; donefor ((初始化循环控制变量;循环条件;修正表达式)); do
循环体
done
进入条件; 退出条件;
循环体;STATEMENT1
睡眠:sleep [n]
循环次数:为列表中的元素的个数;
LIST:列表,包含至少一个元素的字符串集合;
(1) 直接给出;
(2) 数值列表:
(a) {start..end}
例如:{1..10}
(b) seq [start [step]] end
seq LAST
seq FIRST LAST
seq FIRST STEP LAST
(3) 返回列表的命令;
(4) globbing;
(5) 变量引用;
$*, $@
例如:添加3个用户,user1, user2, user3; 密码同用户名;
#vi useradd.sh
#!/bin/bash
for username in user1 user2 user3; do
useradd $username
echo $username |passwd --stdin $username
done
例如:添加9个用户,user101...user109;密码同用户名;
#vi useradd2.sh
#!/bin/bash
for i in {1..9}; do
useradd user10$i
echo user10$i |passwd --stdin user10$i
done
练习:于/tmp/test目录中创建10个空文件f1,.., f10;
#vi addfile.sh
#!/bin/bash
directory=/tmp/test-$(date '+%Y%m%h')
mkdir $directory
for i in {1..10} ; do
touch $directory/f$i
done
练习:写一个脚本,
(1) 创建"/tmp/test-当前时间"目录;
(2) 添加10用户tuser1,.., tuser10;
(3) 在"/tmp/test-当前时间"目录中,创建10空文件f1,...,f10;
(4) 修改f1的属主为tuser1;依次类推;
#vi creatdir.sh
#!/bin/bash
dir=/tmp/test-$(date '+%F-%H-%S')
mkdir $dir
for i in {1..10} ; do
useradd testuser$i
touch $dir/f$i
chown testuser$i $dir/f$i
done
rm -rf $dir
userdel testuser$i
练习:求100以内所有正整数之和;
#vi plus.sh
#!/bin/bash
sum=0
for i in `seq 100`;do
sum=$(($sum+$i))
done
echo $sum
练习:求100以内所有偶数之和;以及所有奇数之和;
#!/bin/bash
declare -i sum=0
for i in `seq 1 2 100`;do
sum=$(($sum+$i))
done
echo $sum
declare -i sum1=0
for i in `seq 2 2 100`; do
sum1=$(($sum1+$i))
done
echo $sum1
练习5:计算当前系统上所有用户ID之和;
#!/bin/bash
sum=0
for i in `cut -d: -f3 /etc/passwd` ; do
sum=$(($sum+$i))
done
echo $sum
练习:传递参数(文本文件路径)给脚本,统计所有文件的空白行数之和;显示此次共对多少文件进行统计;
#!/bin/bash
sum=0
num=0
echo $#
for i in `seq 1 $# `; do
num=`grep '^$' $1 |wc -l`
sum=$(($num+$sum))
shift
done
echo $sum
练习:显示当前系统所有默认shell为bash的用户的总数;并统计此些用户ID之和;
for>#!/bin/bash
sum=0
num=0
for i in `grep 'bash$' /etc/passwd |cut -d: -f3` ; do
sum=$(($sum+$i))
num=$(($num+1))
done
for的几种特殊情况:
(1) for省略,会自动获取脚本参数列表;
(2) C编程风格:
for ((变量赋值;循环条件;修正表达式)); do
CMD1
CMD2
done
(3) 循环嵌套:
for i in [LIST1]; do
CMD1
for j in [LIST2]; do
CMD2
...
done
done
练习:写一个脚本
(1) ping 172.16.X.Y内的所有主机;
172.16.0-255.1-254
for i in {0..255}; do
for j in {1..254}; do
ping -c1 -w1 172.16.$i.$j
done
done
练习:写个脚本
(1) 传递一些目录给脚本;
(2) 逐个显示每个目录下的所有一级文件的内容类型;
(3) 统计一个有多少个目录;一共显示了多少个文件;
#!/bin/bash
#
declare -i dirs=0
declare -i files=0
for d in $*; do
for f in $d/*; do
file $f
let files++
done
let dirs++
done
echo "Directories: $dirs."
echo "Files: $files."
bash命令退出和退出状态码
命令在bash中执行结束退出时,其执行成功与否可通过退出状态码来记录
#echo $? 脚本的退出状态码取决于执行的最后一条命令;自定义退出状态码:
exit #
成功:0
失败:1-255
#!/bin/bash
ls /varr
retval=$?
echo 'hello'
exit $retvel
以ls /varr的结果状态为退出状态
注意:提前退出脚本,也可使用exit命令实现
bash 脚本编程之条件测试:
if/then结构
条件测试:
test EXPRESSION
[ EXPRESSION ]
[[ EXPRESSION ]]
COMMAND:使用的是命令退出状态码
1.使用命令执行结果
a.使用命令引用
b.使用比较操作符
例如:
[ `id -u $username ` -lt 500 ]
userid=`id -u $username`
[ $userid -lt 500]
2.使用命令的退出状态码
a.先运行命令
b.引用状态码
引用方式两种:
1.if COMMAND &>/dev/null;then
引用的是命令运行的退出状态码
注意:COMMAND不能被使用命令引用,COMMAND的执行结果通常没有意义,所以其结果通常(&>)被定向至/dev/null
2.先执行命令,后判断退出状态码是否为0
COMMAND
retval=$?
if [ $? -eq 0 ] ; then
测试表达式:
(1) 整数测试;例如:1 B
A < B
A >= B
A " /etc/passwd | cut -d: -f7` =~ /bin/.*sh$ ]]; then
echo "a user can log system."
else
echo "a user cannot log."
fi
组合测试条件2:
短路操作符
与:COMMAND1 && COMMAND2
COMMAND1的退出状态如果为假,则COMMAND2不用运行,即可有最终结果;
或:COMMAND1 || COMMAND2
COMMAND1的退出状态如果为真,则COMMAND2不用运行,即可有最终结果;
非:! COMMAND
[ ! -d /tmp/test ] && mkdir /tmp/test^C
[ -d /tmp/test ] || mkdir /tmp/test
练习:写一个脚本,完成如下任务:
(1) 如果httpd进程或nginx进程处于运行中,则显示“web server started.”,并显示其监听的端口;
(2) 否则显示“no web server.”;
if pidof httpd &> /dev/null && pidof nginx &> /dev/null; 交互式脚本:
read [OPTIONS] [name ...]
-p "PROMPT"
-t #: 超时时长
给变量以默认值:
[ -z "$VAR" ] && VAR=VALUE
练习:显示如下菜单给用户
cpu) show cpu infomation;
mem) show memory infomation;
disk) show disk infomation;
*) quit
提示用户键入选项:
(1) cpu: 显示CPU相关的信息
(2) mem: 显示内存相关的信息
(3) disk: 列出磁盘设备
(4) 其它任何信息,即为退出脚本
#!/bin/bash
#
cat /dev/null
status=$?
done
echo "centos is logged on."
方法二:简化版
#!/bin/bash
#
declare -i status=0
until who | grep "centos" &> /dev/null; do
sleep 5
done
echo "centos is logged on."
方法三:使用死循环
#!/bin/bash
#
while true; do
who | grep "centos" &> /dev/null
if [ $? -eq 0 ];then
break
fi
sleep 5
done
echo "centos is logged."
while的特殊用法:遍历指定文件的每一行
while read line; do
循环体
done < /path/from/somefile
练习:找出其UID为偶数的所有用户的用户名;并显示其ID号;
#!/bin/bash
#
file=/etc/passwd
while read line; do
userid=`echo $line | cut -d: -f3`
if [ $[$userid%2] -eq 0 ]; then
echo $line | cut -d: -f1,3
fi
done < $file
练习:输出给定的文件的偶数行的行号,以及其行内信息统统改为大写字母输出;
#!/bin/bash
declare -i i=1
while read line; do
if [ $[$i%2] -eq 0 ]; then
echo -n "$i "
echo $line | tr 'a-z' 'A-Z'
fi
let i++
done < /path/from/somefile
练习:显示如下菜单给用户
cpu) cpuifno
mem) memory infomation
disk) disk infomation
quit) quit
(1) 用户给定的选项后,显示相应的信息;而后提示用户再次选择;
(2) 非正确选择也提示用户重新选择,并说明,如果想退出请输入"quit";
#!/bin/bash
#
while true; do
cat /dev/null && return 2
grep "^$1\>" /etc/passwd | cut -d: -f7
[ $? -eq 0 ] && return 0 || return 3
}
while true; do
read -p "Enter a username: " username
[ "$username" == 'quit' ] && break
showuserinfo $username
[ $? -ne 0 ] && echo "There is something wrong."
done
练习:利用函数实现打印九九乘法表;
#!/bin/bash
func() {
declare i=1
declare j=1
while [ $i -le 9 ] ; do
while [ $j -le 9 ] ; do
if [ $j -gt $i ] ; then
break
fi
echo -e -n "{$j}x$i=$[$i*$j]\t"
let j++
done
echo
let j=1
let i++
done
}
func
练习:写一个脚本,完成如下功能(使用函数):
1、提示用户输入一个可执行命令;
2、获取这个命令所依赖的所有库文件(使用ldd命令);
3、复制命令至/mnt/sysroot/对应的目录中
解释:假设,如果复制的是cat命令,其可执行程序的路径是/bin/cat,那么就要将/bin/cat复制到/mnt/sysroot/bin/目录中,如果复制的是useradd命令,而useradd的可执行文件路径为/usr/sbin/useradd,那么就要将其复制到/mnt/sysroot/usr/sbin/目录中;
4、复制各库文件至/mnt/sysroot/对应的目录中;
#!/bin/bash
#
target=/mnt/sysroot
[ -d $target ] || mkdir $target
preCmd() {
if which $1 &> /dev/null; then
cmdpath=$(which --skip-alias $1)
return 0
else
echo "No such command."
return 1
fi
}
cmdCopy() {
cmddir=$(dirname $cmdpath)
[ -d $target/$cmddir ] || mkdir -p $target/$cmddir
[ -f $target/$cmdpath ] || cp $1 $target/$cmddir
return 0
}
libCopy() {
for lib in $(ldd $1 | grep -E -o "/[^[:space:]]+"); do
libdir=$(dirname $lib)
[ -d $target/$libdir ] || mkdir -p $target/$libdir
[ -f $target/$lib ] || cp $lib $target/$libdir
done
return 0
}
main() {
while true; do
read -p "Plz enter a command(quit for quiting): " cmd
[ "$cmd" == 'quit' ] && break
preCmd $cmd
if [ $? -eq 0 ]; then
cmdCopy $cmdpath
libCopy $cmdpath
fi
done
}
main
变量的作用域 :
本地变量:整个脚本,在脚本的函数中也可调用,也可修改;
局部变量:函数调用的整个生命周期;
local VAR=VALUE
函数递归 :
函数直接或间接调用函数自身;
1 1 2 3 5 8 13
阶乘:10!
n(n-1)!
n(n-1)(n-2)!
阶乘函数:
fact() {
if [ $1 -eq 0 -o $1 -eq 1 ]; then
echo 1
else
echo $[$1*$(fact $[$1-1])]
fi
}
下一篇接:Bash Shell脚本编程笔记总结(二)
运维网声明
1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网 享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com