shell编程:

编译器,解释器

编程语言:机器语言、汇编语言、高级语言

静态语言:编译型语言 强类型(变量) 事先转换成可执行格式 C、C++、JAVA、C#

动态语言:解释型语言,on the fly 弱类型 边解释边执行 PHP、SHELL、Python、Perl、lua

面向过程:shell,C 面向对象:JAVA,Python,Perl,C++

变量:内存空间,命名

内存:编址的存储单元

进程:

变量类型:事件确定数据的存储格式和长度 字符 数值 整型 浮点型

逻辑:1+1>2 逻辑运算:与、或、非、异或 1:真 0:假

与: 1 & 0 = 0 0 & 1 = 0 0 & 0 = 0 1 & 1 = 1

或:

非: !真=假 !假=真

异或: 相同为1,不同为0

短路逻辑运算: 与:只要一个为假,结果一定为假 或:只要一个为真,结果一定为真

整型:8bit:256 0-255,溢出

shell:弱类型编程语言

强:变量在使用前,必须事先声明,甚至还需要初始化; 弱:变量用时声明,甚至不区分类型

显式: 11+c=

变量赋值:VAR_NAME=VALUE

bash变量类型: 环境变量 本地变量(局部变量) 位置变量 特殊变量

本地变量: set VARNAME=VALUE:作用域整个bash进程

局部变量: local VARNAME=VALUE:作用域为当前代码段

环境变量:作用域为当前shell进程及其子进程 export VARNAME=VALUE VARNAME=VALUE export VARNAME "导出"

位置变量: $1,$2,...

特殊变量: $?:上一个命令的执行状态返回值:

程序执行,可能有两类返回值: 程序执行结果 程序状态返回代码(0-255) 0:正确执行 1-255:错误执行,1,2,127系统预留

输出重定向:

2> 2>> &>

撤销变量: unset VARNAME

查看当前shell中变量: set

查看当前shell中的环境变量: printenv env export

export PATH=$PATH:/usr/local/apache/bin

脚本:命令的堆砌,按实际需要,结合命令流程控制机制实现的源程序

shebang:魔数 #! /bin/bash

注释行,不执行

/dev/null:软件设备,bit bucket,数据黑洞

脚本在执行时会启动一个子shell进程: 命令行中启动的脚本会继承当前shell环境变量 系统自动执行的脚本(非命令行启动)就需要自我定义需要各环境变量

引用变量:${VARNANE},括号有时可以省略

编程能力: 脚本编程

练习:写一个脚本,完成以下任务 1、添加5个用户,user1,...,user5 2、每个用户的密码同用户名,而且要去,添加密码完成后不显示passwd命令的执行结果信息 3、每个用户添加完成后,都要显示某某用户已经成功添加

练习:写一个脚本,完成以下任务 1、使用一个变量保存一个用户名; 2、删除此变量中的用户,且一并删除其家目录; 3、显示"用户删除完成"类的信息

条件判断: 如果用户不存在 添加用户,给密码并显示添加成功 否则 显示已经存在,不用添加

bash中如何实现条件判断 条件测试类型: 整数测试 字符测试 文件测试

条件测试的表达式: [ expression ] [[ expression ]] test expression

整数比较: -eq:测试整个整数是否相等:比如$A -eq $B -ne:测试两个整数是否不等:不等,为真:相等,为假 -gt:测试一个数是否大于另一个数:大于,为真,否则,为假 -lt:测试一个数是否小于另一个数:小于,为真,否则,为假 -ge:大于或等于 -le:小于或等于

命令间的逻辑关系: 逻辑与:&& 第一个条件为假时,第二条件不用再判断,最终结果已经有; 第一个条件为真时,第二条件必须得判断 逻辑或:||

如果用户user6不存在,就添加用户user6 ! id user6 && useradd user6 id user6 || useradd user6

如果/etc/inittab文件的行数大于100,就显示好大的文件: [ wc -l /etc/inittab | cut -d' ' -f1 -gt 100 ] && echo "Large file"

变量名称 只能包含字母、数字和下划线,并且不能数字开头 不应该跟系统中已有的环境变量重名 最好做到见名知义

如果用户存在,就显示用户已存在;否则,就添加添加此用户 id user1 && echo "user1 exists" || useradd user1

如果用户不存在,就添加;否则,显示其已经存在 ! id user1 && useradd user1 || echo "user exists"

如果用户不存在,添加并且给密码;否则,否则,显示其已经存在 ! id user1 && useradd user1 && echo "user1" | passwd --stdin user1 || echo "user1 exists"

练习,写一个脚本,完成以下要求: 1、添加三个用户user1,user2,user3:但要先判断用户是否存在,不存在而后在添加 2、添加完成后,显示一共添加了几个用户,当然,不能包括事先存在而没有添加的 3、最后显示当前系统上共有多少个用户

练习,写一个脚本,完成以下要求: 给定一个用户: 如果其UID为0,就显示此为管理员,否则,就显示其为普通用户;

#! /bin/bash NAME=user1 USERID= id -u $NAME [ $USERID -eq 0 ] && echo "admin" || echo "common user"

#! /bin/bash NAME=user1 USERID=id -u $NAME if $USERID -eq 0 ; then echo "admin" else echo "common user" fi

条件判断,控制结构:

单分支if语句 if 判断条件; then statement1 statement2 ... fi

双分支的if语句: if 判断条件; then statement1 statement2 ... else statement3 statement4 ... fi

多分支的if语句: if 判断条件1; then statement1 ... elif 判断条件2; then statement2 ... elif 判断条件3; then statement3 ... else statement4 ... fi

#! /bin/bash NAME=user1 if id $NAME &> /dev/null(这个表示命令执行状态,即执行成功还是失败); then echo "$NAME exists." else useradd $NAME echo "$NAME" | passwd --stdin $NAME &> /dev/null echo "Add $NAME finished." fi

练习:写一个脚本 判断当前系统上是否有用户的默认shell为bash,如果有,就显示有多少个这类用户:否则,就显示没有这类用户 #! /bin/bash grep "bash$" /etc/passwd &> /dev/null RETVAL=$? if [ $RETVAL -eq 0 ]; then USERS=grep "bash$" /etc/passwd | wc -l echo "the shells of $USERS users is bash." else echo "no such user" fi

练习:写一个脚本 给定一个文件,比如/etc/inittab 判断这个文件中是否有空白行: 如果有,则显示其空白行数;否则,显示没有空白行

练习:写一个脚本 给定一个用户,判断其uid与gid是否一样 如果一样,就显示此用户为"good guy":否则,就显示此用户为"bad guy". #! /bin/bash USERNAME=user1 USERID=id -u $USERNAME GROUPID=id -g $USERNAME if [ $USERID -eq $GRUPID ]; then echo "GOOD guy" else echo "Bad guy" fi

进一步要求:不使用id命令获得其id号:

练习:写一个脚本 判定命令历史中历史命令的总条目是否大于1000:如果大于,则显示"some command will gone.",否则显示ok。

shell中如何进行算术运算: 1、let算术运算表达式 let C=$A+$B echo $C 2、$[算术运算表达式] C=$[$A+$B] 3、$((算术运算表达式)) C=$(($A+$B)) 4、expr算术运算表达式,表达式中各操作数及运算符之间要有空格,而且要使用命令引用 C=expr $A + $B

练习:写一个脚本 给定一个用户,获取其密码警告期限: 而后判断用户密码使用期限是否已经小于警告期限 提示:计算方法,最长使用期限减去已经使用的天数即为剩余使用期限 如果小于,则显示"warning";否则,就显示"ok"

#! /bin/bash W=grep "student" /etc/shadow | cut -d: -f6 S=date +%s T=expr $S/86400 L=grep "^student" /etc/shadow | cut -d: -f5 N=grep "^student" /etc/shadow | cut -d: -f3 SY=$[$L-$[$T-$N]] if [ $SY -lt $W ]; then else 'Warning' else else 'OK' fi

圆整:丢弃小数点后的所有内容

测试方法: [ expression ] [[ expression ]] test expression

bash中常用的条件测试有三种: 整数测试: -gt -le -ne -eq -ge -lt

INT1=63 INT2=77 [ $INT1 -eq $INT2 ] [[ $INT1 -eq $INT2 ]] test $INT1 -eq $INT2

if grep "^$USERNAME>" /etc/passwd; then

文件测试: -e FILE:测试文件是否存在 -f FILE:测试文件是否为普通文件 -d FILE:测试指定路径是否为目录 -r FILE:测试当前用户对指定文件是否有读取权限 -w FILE:测试当前用户对指定文件是否有写权限 -x

[ -e /etc/inittab ] [ -x /etc/rc.d/rc.sysinit ]

练习:写一个脚本 给定一个文件: 如果是一个普通文件,就显示之 如果是一个目录,亦显示之 否则,此为无法识别之文件 #! /bin/bash FILE=/etc/rc.d/rc.sysinit if [ ! -e $FILE ]; then echo "No such file." exit 6 fi

if [ -f $FILE ]; then echo "Common file" elif [ -d $FILE ]; then echo "Directory" else echo "Unknown" fi

测试脚本是否有语法错误: bash -n 脚本 bash -x 脚本:单步执行

定义脚本退出状态码

exit:退出脚本 exit # 如果脚本没有明确定义退出状态码,那么,最后执行的一条命令的退出码即为脚本的退出状态码

bash变量的类型: 本地变量(局部变量) 环境变量 位置变量: $1,$2,... shift 特殊变量: $? $#:参数的个数 $*:参数列表 $@:参数列表

练习:写一个脚本 能接受一个参数(文件路径) 判定:此参数如果是一个存在的文件,就显示"ok";否则 vim filetest3.sh #! /bin/bash if [ $# -lt 1 ]; then echo "Usage: ./filetest3.sh ARG1 [ARG2]..." exit 7 fi

if [ -e $1 ]; then echo ok else echo "No such file" fi

bash filetest3.sh /etc/fstab

练习:写一个脚本 给脚本传递两个参数(整数): 显示此两者之和,之积 #! /bin/bash if [ $# -lt 2 ]; then echo "Usage: cacl.sh ARG1 ARG2" exit fi echo "The sum is:$[$1+$2]" echo "The prod is:$[$1*$2]"

练习: 传递一个用户名参数给脚本,判断此用户的用户名跟基本组的组名是否一致,并将结果显示出来 #! /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 "yiyang" else echo "bu yiyang" fi

字符测试: ==:测试是否相等,相等为真,不等为假 !=:测试不等,不等为真,相等为假

< -n string:测试指定字符串是否为空,空则真,不空为假 -z string:测试指定字符串是否不空,不空为真,空则假

练习:写一个脚本 传递一个参数(单字符就行)给脚本,如果参数为q,就退出脚本,否则,就显示用户的参数

练习:写一个脚本 传递一个参数(单字符就行)给脚本,如参数为q,Q,quit或者Quit,就退出脚本;否则,显示用户的参数 #! /bin/bash if [ $1 = 'q' ]; then echo "quitting..." exit 1 elif [ $1 = 'Q' ]; then echo "quitting..." exit 2 elif [ $1 = 'quit' ]; then echo "quitting..." exit 3 elif [ $1 = 'Quit' ]; then echo "quitting..." exit 4 else echo $1 fi

练习: 传递三个参数给脚本,第一个为整数,第二个为算术运算符,第三个为整数,将计算结果显示出来,要去保留两位精度。形如: ./cacl.sh 5 / 2 echo "scale=2;111/22;" | bc bc <<< "scale=2;111/22"

练习: 传递3个参数给脚本,参数均为用户名,将此些用户账号信息提取出来后放置于/tmp/testusers.txt文件中,并要求每一行行首有行号

写一个脚本: 判断当前主机的CPU生产商,其信息在/proc/cpuinfo文件中vendor id 一行中 如果其生产商为AuthnticAMD,就显示其为AMD公司 如果其生产商为GenuineIntel,就显示其为Intel公司 否则,就说其非主流公司

写一个脚本: 给脚本传递三个整数,判断其中的最大数和最小数,并显示出来 #! /bin/bash MAX=$1 MIN=$1 [ $2 -gt $MAX ] && MAX=$2 [ $3 -gt $MAX ] && MAX=$3 echo "$MAX" [ $2 -lt $MIN ] && MIN=$2 [ $3 -lt $MIN ] && MIN=$3 echo "$MIN"

#! /bin/bash a=1 MAX=0 MIN=$3 while [ $a -le 3 ] do [ $1 -gt $MAX ] && MAX=$1 [ $1 -lt $MIN ] && MIN=$1 shift 1 a=$(($a+1)) done echo $MAX echo $MIN

循环:进入条件,退出条件 for while until

for 变量 in 列表; do 循环体 done

for i in 1 2 3 4 5 6 7 8 9 10; do 加法运算 done

遍历完成之后,退出:

如何生成列表: {1..100} seq [起始数 [步进长度]] 结束数 declare -i SUM=0

#! /bin/bash declare -i SUM=0 for i in {1..100}; d0 let SUM=$[SUM+$i] done echo "the sum is:$SUM."

写一个脚本: 1.设定变量FILE的值为/etc/passwd 2.依次向/etc/passwd中的每个用户问好,并显示对方的shell,形如: hello,root,your shell: /bin/bash 3.统计一共多少个用户 for i in seq 1 $LINES; do echo "Hello,head -n $I /etc/passwd | tail -1 | cut -d: -f1"; done

只向默认shell为bash的用户问声好

写一个脚本: 1.添加10个用户user1到user10,密码同用户名:但要求只有用户不存在的情况下才能添加 #! /bin/bash for i in user{1..10}; do if id $i &> /dev/null; then echo "$i exists." else useradd $i echo $i | passwd --stdin $i &> /dev/null echo "add user $i finished." fi done

扩展: 接受一个参数: add:添加用户user1..user10 del:删除用户user1..user10 其它:退出 #! /bin/bash if [ $# -lt 1 ]; then echo "Usage: adminusers ARG" exit 7 fi

if [ $1 == '--add' ]; then for i in {1..10}; do if id user$i &> /dev/null; then echo "user$i exists." else useradd user$i echo user$i | passwd --stdin user$i &> /dev/null echo "Add user$i finished." fi done elif [ $1 == '--del' ]; then for i in {1..10}; do if id user$i &> /dev/null; then userdel -r user$i echo "user$i exists." else echo "No user$i." fi done else echo "Unknown ARG" exit 8 fi

#! /bin/bash if [ $1 == '--add' ]; then for i in echo $2 | sed 's/,/ /g'; do if id $i &> /dev/null; then echo "$i exists." else useradd $i echo $i | passwd --stdin $i &> /dev/null echo "add $i finished." fi done elif [ $1 == '--del' ]; then for i in echo $2 | sed 's/,/ /g'; do if id $i &> /dev/null; then userdel -r $i echo "delete $i finished" else echo "$i not exist" fi done elif [ $1 == '--help' ]; then echo "Usage:adminuser2.sh --add user1,user2... | --del user1,user2...| --help" else echo "Unknown options." fi

写一个脚本: 计算100以内所有能被3整除的正整数的和 取模,取余:% 3%2=1 100%55=45

写一个脚本: 计算100以内的所有奇书的和以及所有偶数的和;分别显示之

写一个脚本,分别显示当前系统上所有默认的shell为bash的用户和默认shell为/sbin/nologin的用户,并统计各类shell下的用户总数,显示结果形如下: BASH,3users,they are: root,redhat,gentoo

NOLOGIN,2users,they are: bin,ftp

expect远程自动批量登录以及执行命令 #! /bin/bash cat /root/iplist | while read line iplist文件中存放了IP地址和密码,每行格式为"IP地址 密码" do A=($line) /usr/bin/expect<<EOF set timeout 5 是设置超时时间的 spawn ssh root@${A[0]} spawn是进入expect环境后才可以执行的expect内部命令 expect "password:" 这个命令的意思是判断上次输出结果里是否包含“password:”的字符串,如果有则立即返回,否则就等待一段时间后返回,这里等待时长就是前面设置的5秒
send "${A[1]}\r" 用户的登陆密码,这里是root用户,密码是passwd
expect "
]#" 意思为遇到这个提示符为执行命令开始 send "df -h\r" 意思为发送命令 send "pwd\r" 意思为发送命令 expect "*]#" 意思为遇到这个提示符为执行命令结束 send "logout\r" 退出远程机器 EOF done

#! /bin/bash cat /root/iplist | while read line do A=($line) /usr/bin/expect<<EOF set timeout 5 spawn ssh root@${A[0]} expect { "yes/no" { send "yes\r"; exp_continue} "password:" { send "${A[1]}\r" } } expect "]#" send "df -h\r" send "pwd\r" expect "]#" send "logout\r" EOF done

expect实现su切换脚本 #! /bin/bash cat /root/iplist | while read line do A=($line) /usr/bin/expect<<EOF set timeout 5 spawn ssh weihu@${A[0]} expect { "yes/no" { send "yes\r"; exp_continue} "password:" { send "${A[1]}\r" } } expect "]$" send "su - root\r" expect "密码:" send "${A[2]}\r" send "ifconfig\r" expect "]#" send "pwd\r" expect "*]#" send "logout\r" EOF done =========================================================================================>

测试: 整数测试 -le -lt -gt -eq -ne 字符测试 == != > < -n -z 文件测试 -e -f -d -r -w -x

if [ $# -gt -1 ]; then

组合测试条件 -a:与关系 -o:或关系 !:非关系

if [ $# -gt 1 -a $# -le 3 ] if [ $# -gt 1 ] && [ $# -le 3 ]

写一个脚本,利用RANDOM生成10个随机数,并找出其中的最大值,和最小值 #! /bin/bash declare -i MAX=0 declare -i MIN=0 for i in {1..10}; do MYRAND=$RANDOM [ $i -eq 1 ] && MIN=$MYRAND if [ $i -le 9 ]; then echo -n "$MYRAND," else echo "$MYRAND" fi [ $MYRAND -gt $MAX ] && MAX=$MYRAND [ $MYRAND -lt $MIN ] && MIN=$MYRAND done

echo $MAX,$MIN

面向过程 控制结果 顺序结构 选择结构 循环结构

选择结构: if:单分支,双分支,多分支 if condition; then statement .... fi

if condition; then statement .... else statement .... fi

if condition; then statement .... elif condition2; then statement .... else statement .... fi

case语句:选择结构

case switch in value1) statement .... ;; value2) statement .... ;; *) statement .... ;; esac

#! /bin/bash case $1 in [0-9]) echo "A digit." ;; [a-z]) echo "Lower" ;; [A-Z]) echo "Upper" ;; *) echo "Special character." ;; esac

只接受参数start,stop,restart,status其中之一 #! /bin/bash case $1 in start) echo "start server...." ;; stop) echo "stop server...." ;; restart) echo "restarting server...." ;; *) echo "basename $0 {start|stop|restrt|status}" ;; esac

写一个脚本,可以接受选项及参数,而后能获取每一个选项,及选项的参数;并能根据选项及参数做出特定的操作,比如 #! /bin/bash DEBUG=0 ADD=0 DEL=0

for i in seq 1 $#; do case $1 in -v|--verbose) DEBUG=1 shift ;; -h|--help) echo "Usage:basename $0 --add USER_LIST --del USER_LIST -v|--verbose -h|--help" exit 0 ;; --add) ADD=1 ADDUSERS=$2 shift 2 ;; --del) DEL=1 DELUSERS=$2 shift 2 ;; #*)

echo "echo "Usage:basename $0 --add USER_LIST --del USER_LIST -v|--verbose -h|--help""

exit 7

;;

esac done

if [ $ADD -eq 1 ]; then for USER in echo $ADDUSERS | sed 's@,@ @g'; do if id $USER &> /dev/null; then [ $DEBUG -eq 1 ] && echo "$USER exists." else useradd $USER [ $DEBUG -eq 1 ] && echo "Add user $USER finished." fi done fi

if [ $DEL -eq 1 ]; then for USER in echo $DELUSERS | sed 's@,@ @g'; do if id $USER &> /dev/null; then userdel -r $USER [ $DEBUG -eq 1 ] && echo "$USER exists." else [ $DEBUG -eq 1 ] && echo "$USER not exist." fi done fi

脚本编程控制结构: 顺序 选择 if case 循环 for while until

while condition; do statment done

进入循环:条件满足 退出循环:条件不满足

until condition; do statment done

进入循环:条件不满足 退出循环:条件满足

#! /bin/bash read -p "Input something: " STRING while [ $TRING != 'quit' ]; do echo $STRING | tr 'a-z' 'A-Z' read -p "Input something: " STRING

done

#! /bin/bash read -p "Input something: " STRING until [ $STRING == 'quit' ]; do echo $STRING | tr 'a-z' 'A-Z' read -p "Input something: " STRING done

#! /bin/bash who | grep "hadoop" &> /dev/null RETVAL=$? until [ $RETVAL -eq 0 ]; do echo "hadoop is not come" sleep 5 who | grep "hadoop" &> /dev/null RETVAL=$? done echo "hadoop is logged in"

for 变量 in 列表; do 循环体 done

for (( expr1 ; expr2 ; expr3 )); do 循环体 done

#!/bin/bash declare -i sum=0 for ((i=1;i<=100;i++)); do let sum+=$i done echo $sum

写一个脚本: 通过ping命令测试192.168.0.151到192.168.0.254之间的所有主机是否在线,如果在线,就显示"ip is up",其中的ip要换成为真正的ip地址,且以绿色显示 如果不在线,就显示"ip is down.",其中的ip要换成为真正的ip地址,且以红色显示 要求:分别使用while,until和for循环实现

awk 'PATTERN{ACTION}' file

df -Ph | awk '{print $2}' awk -F: '{print $1,$2}' /etc/passwd

写一个脚本(前提:请为虚拟机新增一块硬盘,假设它为/dev/sdb),为指定的硬盘创建分区: 1.列出当前系统上所有的磁盘,让用户选择,如何选择quit则退出脚本;如果用户选择错误,就让用户重新选择; 2.当用户选择后,提醒用户确认接下来的操作可能会损坏数据,并请用户确认,如果用户选择y就继续,否则,让用户重新选择; 3.抹除那块硬盘的所有分区(提示,抹除所有分区后执行sync命令,并让脚本睡眠3秒钟后在分区):并为其创建三个主分区,第一个为20M,第二个为512M,第三个为128M,且第三个为swap分区类型;(提示:将分区命令通过echo传送给fdisk即可实现) #! /bin/bash echo 'n p 1

+20M n p 2

+512M n p 3

+128M t 3 82' w | fisk /dev/hda

fdisk -l 2> /dev/null | grep ^Disk /dev/[sh]d[a-z]'' | awk -F: '{print $1}' dd if=/dev/zero of=/dev/hda bs=512 count=1 sync sleep 3

写一个脚本,完成以下功能: 说明:此脚本能于同一个repo文件中创建多个yum源的指向; 1.接受一个文件名作为参数,此文件存放至/etc/yum.repo.d目录中,且文件名以.repo为后缀; 2.在脚本中,提醒用户输入repo id:如果为quit,则退出脚本;否则,继续完成下面的步骤; 3.repo name以及baseurl的路径,而后以repo文件的格式将其保存至指定的文件中; 4.enabled默认为1,而gpgcheck默认设定为0; 5.此脚本会循环执行多次,除非用户为repo id指定为quit #! /bin/bash REPOFILE=/etc/yum.repos.d/$1 if [ -e $REPOFILE ]; then echo "$1 exists." exit 3 fi

read -p "Repository ID:" REPOID until [ $REPOID == 'quit' ]; do echo "[$REPOID]" >> $REPOFILE read -p "Repository name: " REPONAME echo "name=$REPONAME" >> $REPOFILE read -p "Repository Baseurl:" REPOURL echo "baseurl=$REPOURL" >> $REPOFILE echo -e 'enabled=1\ngpgcheck=0' >> $REPOFILE read -p "Repository ID: " REPOID done

while,until,for

break:提前退出循环 continue:提前结束本轮循环,而进入下一轮循环

#! /bin/bash let sum=0 let i=0 while [ $i -lt 100 ]; fo let i++ if [ $[$i%2] -eq 0 ]; then continue fi let sum+=$i done

#! /bin/bash declare -i sum=0 for i in {1..1000}; do let sum+=$i if [ $sum -gt 10000]; then break fi done echo $i echo $SUM

while的特殊用法一: while :;do

done

while的特殊用法二: while read LINE; do

done < /path/to/somefile

#! /bin/bash file=/ect/passwd let i=0 while read LINE; do [ echo $LINE | awk -F: '{print $3}' -le 505 ] && continue [ echo $LINE | awk -F: '{print $7}' == '/bin/bash' ] && echo $LINE | awk -F : '{print $1}' && let i++ [ $i -eq 6 ] && break done < $FILE

写一个脚本: 1.判断一个指定的bash脚本是否有语法错误;如果有错误,则提醒用户键入Q或者q无视错误并退出,其它任何键可以通过vim打开这个指定的脚本; 2.如果用户通过vim打开编辑后保存并退出时仍然有错误,则重复第一步中的内容;否则,就正常关闭退出 #! /bin/bash until bash -n $1 &> /dev/null; do read -p "Syntax error,[Qq] to quit,others for editing: " CHOICE case $CHOICE in q|Q) echo "somthing wrong,quiting." exit 5 ;; *) vim + $1 ;; esac done

函数:功能,function

代码重用:

库:so

定义一个函数: function FUNNAME { command }

FUNCNAME() { command }

自定义执行状态返回值: return # 0-255

接受参数的函数: ./a.sh m n $1:m $2:n

#! /bin/bash cat << EOF d|D) show disk usages m|M) show memory usages s|S) show swap usages q|Q) quit. EOF

read -p "your choice: " CHOICE until [ $CHOICE == 'q' -o $CHOICE == 'Q' ] do case $CHIOCE in d|D) df -lh ;; m|M) free -m | grep '^Mem';; s|S) free -m | grep '^Swap';; *) cat << EOF d|D) show disk usages m|M) show memory usages s|S) show swap usages q|Q) quit. EOF read -p "your choice,again: " CHOICE esac cat << EOF d|D) show disk usages m|M) show memory usages s|S) show swap usages q|Q) quit. EOF read -p "your choice: " CHOICE done

#! /bin/bash TWOINT() { A=9 B=7 C=$[$A+$B] echo $C } M=11 SUM=$[$M+TWOINT] echo $SUM

#! /bin/bash ADDUSER() { USERNAME=$1 if ! id -u $USERNAME &> /dev/null; then useradd $USERNAME echo $USERNAME | passwd --stdin $USERNAME &> /dev/null return 0 else return 1 fi } for i in {1..10} do ADDUSER user$i fi [ $? -eq 0 ]; then echo "add user$i finished." else echo "Failuser." fi done

#! /bin/bash function SHOWMENU { cat << EOF d|D) show disk usages m|M) show memory usages s|S) show swap usages q|Q) quit. EOF } SHOWMENU SHOWMENU SHOWMENU

#! /bin/bash TWOSUM() { echo $[$1+$2] } for i in {1..10} do let J=$[$i+1] echo "$i plus $J is TWOSUM $i $j" done

练习:写一个脚本,判定192.168.0.200-192.168.0.254之间主机哪些在线。要求 1.使用函数来实现一台主机的判定过程 2.在主程序中来调用此函数判定指定的范围内的所有主机的在线情况 #! /bin/bash PING() { for i in {200..254} do if ping -c1 -W 1 192.168.0.$i &> /dev/null; then echo "192.168.0.$i is up." else echo "192.168.0.$i is down." fi done } PING

#! /bin/bash PING() { if ping -c 1 -W 1 $1 &> /dev/null; then echo "$1 is up." else echo "$1 is down." fi }

for i in {200..254} do PING 192.168.0.$i done

for i in {220..254} do PING 172.16.100.$i done

#! /bin/bash PING() { if ping -c 1 -W 1 $1 &> /dev/null; then return 0 else return 1 fi }

for i in {200..254} do PING 192.168.0.$i if [ $? -eq 0 ]; then echo "192.168.0.$i is up." else echo "192.168.0.$i is down." fi done

写一个脚本:使用函数完成 1.函数能够接受一个参数,参数为用户名 判断一个用户是否存在 如果存在,就返回此用户的shell和UID;并返回正常的状态值 如果不存在,就说此用户不存在;并返回错误的状态值 2.在主程序中调用函数

扩展1:在主程序中,让用户自己输入用户名后,传递给函数来进行判断 扩展2:在主程序中,输入用户名判断后不退出脚本,而是提示用户继续输入下一个用户名;如果用户输入的用户不存在,请用户重新输入,但如果用户输入的是q或Q就退出;

=========================================================================================>

二、变量的截取替换

表达式 含义 ${#string} $string的长度

${string:position} 在$string中, 从位置$position开始提取子串 ${string:position:length} 在$string中, 从位置$position开始提取长度为$length的子串

${string#substring} 从变量$string的开头, 删除最短匹配$substring的子串 ${string##substring} 从变量$string的开头, 删除最长匹配$substring的子串 ${string%substring} 从变量$string的结尾, 删除最短匹配$substring的子串 ${string%%substring} 从变量$string的结尾, 删除最长匹配$substring的子串

${string/substring/replacement} 使用$replacement, 来代替第一个匹配的$substring ${string//substring/replacement} 使用$replacement, 代替所有匹配的$substring ${string/#substring/replacement} 如果$string的前缀匹配$substring, 那么就用$replacement来代替匹配到的$substring ${string/%substring/replacement} 如果$string的后缀匹配$substring, 那么就用$replacement来代替匹配到的$substring

脚本编程知识点: 1.变量中字符的长度:${#VARNAME}

2.变量赋值等: ${parameter:-word}:如果parameter为空或未定义,则变量展开为"word":否则,展开为parameter的值: ${parameter:+word}:如果parameter为空或未定义,不做任何操作;否则,则展开为"word"值 ${parameter:=word}:如果parameter为空或未定义,则变量展开为"word",并将展开后的值赋值为parameter ${parameter:offset} ${parameter:offset:length}:取字串,从offset处的后一个字符开始,取lenth长的字串

3.脚本配置文件 /etc/rc.d/init.d/服务脚本 服务脚本支持配置文件:/etc/sysconfig/服务脚本同名的配置文件

vim a.conf TEST='hello world'

vim a.sh #! /bin/bash . /root/a.conf TEST=${TEST:-info} [ -n "$TEST" ] && echo "$TEST"

4.局部变量 lcoal VAR_NAME=

a=1 test() { local a=$[3+4] } test for i in seq $a 10 do echo "$i" done

5.命令mktemp 创建临时文件或者目录 mktemp /tmp/file.XX -d:创建为目录

6.信号 kill -SIGNAL PID 1:HUP 2:INT 9:KILL 15:TERM 脚本中,能实现信息捕捉,但9和15无法捕捉不了

Ctrl+c:SIGINT

trap命令: trap 'COMMAND' 信号列表

vim showdate.sh #! /bin/bash trap 'echo "you go..."' INT while : do date sleep 2 done

7.一行执行多个语句,语句间用分号分隔 #! /bin/bash NET=192.168.0 FILE=mktemp /tmp/file.xxxxx clearup() { echo "quit...." rm -rf $FILE exit 1 } trap 'clearup' INT for i in {200..254} do if ping -c 1 -W 1 $NET.$i &> /dev/null; then echo "$NET.$i is up."| tee >> $FILE else echo "$NET.$i is down." fi done

任务计划:

1.在未来的某个时间点执行一次某任务: at batch

at 时间 at> COMMAND at> Ctrl+d

指定时间: 绝对时间:HH:MM,DD.MM.YY MM/DD/YY 相对时间:now+# 单位:minutes,hours,days,weeks 模糊时间:noon,midnight,teatime

命令的执行结果:将以邮件的形式发给安排任务的用户

at -l = atq at -d AT_JOB_ID = atrm AT_JOB_ID

2.周期性地执行某任务 cron:自身是一个不间断运行的服务 anacron:cron的补充,能够实现让cron因为各种原因在过去的时间改执行而未执行的任务在恢复正常执行一次

cron: 系统cron任务: /etc/crontab 分钟 小时 天 月 周 用户 任务 用户cron任务: /var/spool/cron/USERNAME 分钟 小时 天 月 周 任务

时间的有效取值:
    分钟:0-59
    小时:0-23
     天:1-31
     月:1-12
     周:0-7,0和7都表示周日
时间通配表示:
     *:对应时间的所有有效值
       3 * * * *
       3 * * * 7
       13 12 * * *
     ,:离散时间点:
       10,40 * * * *
     -:连续时间点
        10 02 * * 1-5
      /#:对应取值范围内每多久一次
        */3 * * * *

每两小时执行一次: 00 */2 * * * 每两天执行一次: 10 04 */2 * *

执行结果将以邮件形式发送给管理员: */3 * * * * /bin/cat /etc/fstab &> /dev/null

cron的环境变量:cron执行所有命令都去PATH坏境变量指定的路径下去找 PATH /bin:/sbin:/usr/bin:/usr/sbin

用户任务的管理: crontab -l:列出当前用户的所有cron任务 -e:编辑 -r:移除所有任务 -u USERNAME:管理其用户的cron任务

anacron: cat /etc/anacrontab

service crond status service anacron status

Knernel + initrd(busybox制作,提供ext3文件系统模块) + ROOTFS(busybox制作)

make arch/ arch/x86/boot/bzImage

硬件驱动:initrd initrd:仅需要提供内核访问真正的根文件系统所在设备需要的驱动 存储设备和文件系统相关的模块 系统初始化rc.sysinit:初始化其它硬件的驱动程序

ROOTFS:busybox,init不支持运行级别 /etc/inittab:格式也不尽相同 ash,hush bash

内核编译: tar -zxvf linux-2.6.38.5.tar.gz -C /usr/src/ cd linux-2.6.38.5 ln -sv linux-2.6.38.5 linux cd linux mv kernel-2.6.38.1-i686.cfg .config make memuconfig make drivers/net/pcnet32.ko make arch/x86/或者(make SUBIDR=arch/)

/boot/vmlinuz(ext3,IDE)

/mnt/boot: /dev/hda1 /mnt/sysroot: /dev/hda2

1、安装grub grub-install --root-directory=/mnt /dev/hda

cp /boot/vmlinuz-2.16.18-308.e15 /mnt/boot/vmlinuz

tar -zxvf busybox-1.20.2.tar.gz mkdir busybox-1.20.2/include/mtd cp /usr/src/linux-2.6.38.5/include/mtd/ubi-user.h busybox-1.20.2/inuclude/mtd

cd busybox-1.20.2 make menuconfig make install mkdir /tmp/initrd cp _install/* /tmp/initrd/ -a cd /tmp/initrd/ mkdir proc sys mnt/sysroot dev tmp lib/modules etc -pv rm -rf linuxrc mknod dev/console c 5 1 mknod /dev/null c 1 3

vim init #! /bin/sh echo "mounting proc and sys..." mount -t proc proc /proc mount -t sysfs sysfs /sys

echo "load ext3 module..." insmod /lib/modules/jbd.ko insmod /lib/modules/ext3.ko mdev -s mount -t ext3 /dev/hda2 /mnt/sysroot

echo "Switch to real_rootfs..." exec switch_root /mnt/sysroot /sbin/init

modinfo jbd mkdinfo ext3 把输出的filename信息路径复制到 lib/modules

find . | cpio -H newc --quit -o | gzip -9 > /mnt/boot/initrd.gz

vim /mnt/boot/grub/grub.conf default=0 timeout=3 title MageEdu Tiny Linux (2.6.18) root(hda0,0) kernel /vmlinuz ro root=/dev/hda2 initrd /initrd.gz

cp -a busybox-1.20.2/_install/* /mnt/sysroot/

cd /mnt/sysroot rm -rf linuxrc mkdir boot root etc/rc.d/init.d var/{log,lock,run} proc sys dev lib/moudles tmp home mnt media -pv

mknod dev/console c 5 1 mknod dev/null c 1 3

vim etc/inittab ::sysinit:/etc/rc.d/rc.sysinit console::respawn:-/bin/sh ::ctrlaltdel:/sbin/reboot ::shutdown:/bin/umount -a -r

vim /etc/rc.d/rc.sysinit #! /bin/sh echo -e "\tWelcome to \033[3 4mMegeEdu Tiny\033[0m Linux"

echo "mount proc and sys..." mount -t proc proc /proc mount -t sysfs sysfs /sys

echo -e "Rmount the rootfs..." mount -t ext3 -o remount,rw /dev/hda2 /

echo "Detect and export hardward information" mdev -s

echo "Mount the ohter filesystem.." mount -a

chmod +x etc/rc.d/rc.sysinit

vim etc/fstab sysfs /sys sysfs defaults 0 0 proc /proc proc defaults 0 0 /dev/hda1 /boot ext3 defaults 0 0 /dev/hda2 / ext3 defaults 1 1

./bincp.sh bash 然后吧etc/inittab中的/bin/sh改成/bin/bash

grep -E "^root:" /etc/passwd > /mnt/systoot/etc/passwd grep -E "^root:" /etc/passwd > /mnt/sysroot/etc/shadow grep -E "^root:" /etc/group > /mnt/sysroot/group

vim etc/inittab ::sysinit:/etc/rc.d/rc.sysinit ::respawn:/sbin/getty 9600 tty1 ::ctrlaltdel:/sbin/reboot ::shutdown:/bin/umount -a -r

vim /mnt/sysroot/etc/hostname HOSTNAME=tiny.magedu.com

vim /mnt/sysroot/ect/rc.d/rc.sysinit #! /bin/sh echo -e "\tWelcome to \033[3 4mMegeEdu Tiny\033[0m Linux"

echo "set the hostname" [ -f /etc/hostname ] && . /etc/hostname [ -z "$HOSTNAME" -o "$HOSTNAME" == '(none)' ] && HOSTNAME=localhost hostname $HOSTNAME

echo "mount proc and sys..." mount -t proc proc /proc mount -t sysfs sysfs /sys

echo -e "Rmount the rootfs..." mount -t ext3 -o remount,rw /dev/hda2 /

echo "Detect and export hardward information" mdev -s

echo "Mount the ohter filesystem.." mount -a

cp /etc/issue /mnt/sysroot/ect/

find . | cpio -H newc --quiet -o | gzip > /root/tiny.1.gz

modinfo mii modinfo pcnet32 同样复制其模块位置到lib/modules/

在sysinit中末尾添加一行 echo "Load ethernet card module..." insmod /lib/modules/...

telnet:远程登录协议,23/tcp C/S S:telnet服务器 C:telnet客户端

ssh:Secure SHell,应用层协议,22/tcp 通信过程及认证过程是加密的,主机认证 用户认证过程加密 数据传输过程加密

主机密钥 非对称加密 Secret key Public key 密钥交换

ssh v1,v2 man-in-middle

sshv2

认证过程: 基于口令认证 基于密钥认证

协议:规范 实现:服务器端、客户端

Linux:openssh C/S 服务器端:sshd,配置文件/etc/sshd_config 客户端:ssh,配置文件/etc/ssh/ssh_config ssh-keygen:密钥生成器 ssh-copy-id:将公钥传输至远程服务器 scp:跨主机安全复制工具

ssh: ssh USERNAME@HOST ssh -l USERNAME HOST ssh USERNAME@HOST 'COMMAND'

scp: scp SRC DEST -r -a scp SERNAME@HOST:/path/to/somefile /path/to/lcoal scp /path/to/local USERNAME@HOST:/path/to/file

ssh-keygen -t rsa ~/.ssh/id_rsa ~/.ssh/id_rsa.pub -f /path/to/KEY_FILE -P '':指定加密私钥的密码

公钥追加到远程主机某用户的家目录下的.ssh/anthorized_keys文件或.ssh/authorized_keys2文件中

ssh-copy-id -i ~/.ssh/id_rsa.pub ssh-copy-id -i ~/.ssh/id_rsa.pub USERNAME@HOST

dropbear:嵌入式系统专用的ssh服务器和客户端工具 服务器端:dropbear dropbearkey 客户端:dbclient

dropbear默认使用nsswitch实现名称解析
     /etc/nsswitch.conf
     /usr/libnss_files*
     /usr/lib/libnss3.so
     /usr/lib/libnss_files*

dropbear会在用户登录检查其默认shell是否当前系统的安全shell
    /etc/shells

主机密钥默认位置: /etc/dropbear/ RSA:dropbear_rsa_host_key 长度可变,只要是8的整数倍,默认为1024 DSS:dropbear_dss_host_key 长度固定,默认为1024 dropbearkey

tar -zxvf dropbear-2013.56.tar.bz2 cd dropbear ./configure make make install

./bincp.sh dropbear dropbearkey dbclient

cd /mnt/sysroot/ vim etc/shells /bin/sh /bin/bash /bin/ash /bash/hush

然后在etc/fstab中加一行加载 devpts /dev/pts devpts mode=620 0 0

mkdir /dev/pts

dropberkey -t rsa -f /mnt/sysroot/etc/dropbear/dropbear_rsa_host_key -s 2048
dropberkey -t rsa -f /mnt/sysroot/etc/dropbear/dropbear_dss_host_key -s 2048

mkdir usr/lib cp -d /lib/libnss_files* /mnt/sysroot/lib/ cp -d /usr/lib/libnss3.so /usr/lib/libnss_files.so /mnt/sysroot/usr/lib/ ls -l /mnt/sysroot/lib/ cp /etc/nsswitch.conf /mnt/sysroot/etc

vim nsswitch.conf 只需要保留passwd,shadow,group,hosts这几行

启动小系统,配置IP地址 /usr/lcoal/sbin/dropbear [-E -F]

/usr/lcoal/bin/dbclient -l root 172.16.200.1

vim select.sh #! /bin/bash PS3="select your will exec memu:" select i in apache mysql php do case $i in apache) echo "apache" ;; mysql) echo "mysql" ;; php) echo "php" ;; esac done

bash select.sh

find . -maxdepth 1 -type f -name ".txt" -mtime +30 30天之前的文件 find . -maxdepth 1 -type f -name ".txt" -mtime -1 一天以内的文件 find . -maxdepth 1 -size +50M -type f -exec mv {} /tmp/ ;把大于50M的文件移到/tmp

制作csv格式的文档: echo "ip,hostname,cpu,mem,disk" >> 1.csv echo "10.30.231.98,localhost,inter,1G,500M,40G" >> 1.csv

发邮件格式: mail -s "邮件主题" -c"抄送地址" -b"密送地址" -f 发送人邮件地址 -F 发件人姓名 收件人地址 < "发邮件的内容"