一、作业(练习)内容:

1、描述shell程序的运行原理(可附带必要的图形说明);

shell的解释:

Shell的作用是linux操作系统的外壳,为用户提供使用操作系统的接口。它是命令语言、命令解释程序及程序设计语言的统称。

wps463B.tmp


    Shell会先确认用户的词语分析,来理解命令,选项,参数来进行解析。Shell中有内建命令,还有外部命令;流程的过如下:


   (1)读取用户由键盘输入的命令;

   (2)分析语法,命令,参数,然后配置参数;

   (3)终端进程调用fork( )建立一个子进程;

   (4)进程创建完成后,内部运行的指令提交给内核来完成任务;

   (5)代码执行结束后,进程通知shell,它的任务已完成,子进程中止;

   (6)shell接到退出指令,将显示下一个提示符,等待接受下一条命令;


wps464C.tmp



2、总结shell编程中所涉及到的所有知识点(如:变量、语法、命令状态等等等,要带图的哟);

shell 的变量:

Shell中的变量的意思就是可变化的量,程序运行到进程以后再内存中申请的内存空间,这个空间可以反复存储与修改数据,另外也可命名的存储空间。


变量的分类:

    本地变量

       只对当前shell进程有效的变量,对其他shell进程无效,包括当前shell进程的子进程。           

       定义本地变量: VAR_NAME=VALUE

         变量名:包含数字,字母和下划线,且不能以数字开头;

        变量赋值:向变量的存储空间保存数据;

       变量引用:${VAR_NAME}

         “”:弱引用,里面的变量会被替换;

         ‘’:强引用,里面的所有祖父都是字面量,直接输出;


声明数值类型

declare -i name{=value}

wps464D.tmp


环境变量

对当前shell进程及其子shell有效,对其他shell进程无效。

        定义环境变量: export VAR_NAME=VALUE

        导出: export VAR_NAME

查看环境变量:env ,printenv,export

添加PATH环境变量,第1种方法:(用户注销就失效)


[root@lx_web_s1 ~]# export PATH=/usr/local/webserver/mysql/bin:$PATH

wps464E.tmp


第2种方法:

# vim /etc/profile

在最后,添加:

export PATH="$PATH:/tmp/hzm/"

保存,退出,然后运行:

#source /etc/profile


局部变量

对shell脚本中某代码片段有效,通常用于函数本地:

定义局部变量:Loacl export VAR_NAME=VALUE


位置参数与特殊变量

$#:传递给脚本的参数数量;

$0 脚本的名称;

$1,$2,$3.... 第一个参数,第二个参数,第三个参数;

$@ :所有参数的列表;


实例:用脚本输出方式展现各参数的意义

wps465E.tmp

wps465F.tmp






语法

1. 第一行必须是"#!/bin/sh"

#!/bin/sh"是对shell的声明,说明你所用的是那种类型的shell及其路径所在;

如果没有声明,则脚本将在默认的shell中执行,默认shell是由用户所在的系统定义为执行shell脚本的shell.

2.注释:开头为#

3.脚本内容:


实例:语法开头,#为注释内容,下面是脚本内容:

wps4661.tmp


bash -n 检查脚本是否有语法错误

bash -x 显示脚本执行过程

wps4662.tmp


命令状态:

0 :成功执行:检查、/etc/passwd目录是否存在,如果存在状态值返回0

wps4663.tmp


非0 : 执行过程中出现异常或非正常退出;(状态值可以在脚本中作为结束脚本来使用)

wps4664.tmp

$? 可以查看 最后一条命令的返回值 该变量可以在shell 脚本中的任何地方使用.


测试表达式:

[ EXPRESSION ]

整数测试:隐含做数值大小比较,所以不要给变量引用加引号;

实例:先定义变量:A=4,B=6

wps4665.tmp


A -gt B 是否大于(echo $? 返回为1的状态为否定的,所以A不大于B)

wps4676.tmp


A -ge B是否大于等于

wps4677.tmp


A -lt B 是否小于

wps4678.tmp


A -le B是否小于等于

wps4679.tmp


A -eq B 是否等于

wps467A.tmp


A -ne B 是否不等于

wps467B.tmp


字符串比较:ASCII数值越大,所以相同的字母值越大;

> 是否大于

< 是否小于

== 是否等于:$A == $B

!= 是否不等于

-z 测试是否为空:空着为真,否则为假

-n 测试是否不空:不空为真,否则为假

文件测试:($? 为0存在,1则不存在)


-e:是否存在并且为文件存在;

wps467C.tmp


-f:是否存在并且为普通文件;

wps467D.tmp


-d 是否存在并且为目录;

wps467E.tmp


-h:是否存在并且为链接文件

-L:同上

-b:是否为块设备文件

-c:是否存在并且为字符设备文件

-S 是否存在并且为套接字文件

-p:是否存在且为管道文件

读写权限检测:

-r:当前用户是否拥有读权限

-w:当前用户是否拥有写权限

-x:当前用户是否拥有执行权限

-u:当前用户是否拥有suid权限

-k:文件是否拥有sticky权限

-g:文件是否有sgid

-O:当前用户是否为指定的属主

-G:当前用户是否为指定的属


双目操作

A -nt B:是否晚于

A -ot B:A是否旧于B

A -ef B:是否指向同一个文件的硬链接


算数运算方式:

1.$[$A+$B]

2.$(($A+$B))

3.Let variable=$A+$B

4.expr $A + $B

3、总结课程所讲的所有循环语句、条件判断的使用方法及其相关示例;(if (jpg|png is not exist);echo ”You say a XX“)


if语句结构

单分之if:

if then,
if true-分支
fi


实例:如果用户root存在,则输出该用户存在;

wps468F.tmp


双分支if:

if then,
if true分支
else
if false分支
fi
if


实例:如果该用户存在,输出user存在,否则该用户不存在;

wps4690.tmp


多分支if语句:

if
elif  ;then 
fi

实例:给脚本两个参数判断 a 与b的比较:

#!/bin/bash
read -p "please give two number:" a b
if [ $a -eq $b ]
then
   echo "$a is equal to $b"
elif [ $a -gt $b ]
then
   echo "$a is greater than $b"
elif [ $a -lt $b ]
then
   echo "$a is less than $b"
else
   echo "please give two number"
fi

wps4691.tmp



wps4692.tmp


for循环

for 变量 in LIST;do
循环体
done

LIST:是有一个或多个空格分隔开的字符串组成;把列表的值逐一赋值给变量;


实例1:在/tmp目录下创建1-10目录;

#!/bin/bash
for i in $( seq 1 10 );do
   mkdir -p /tmp/${k}
done

wps4693.tmp


实例2:for循环嵌套,创建文件,创建1-10个目录,并且每个文件里面创建1-10个目录

#!/bin/bash
for k in $( seq 1 10 );do
   mkdir -p /tmp/aaa${k}
      for l in $( seq 1 10 );do
      mkdir -p /tmp/aaa$[k]/bbb${l}
    done
done

wps4694.tmp

结果:

wps4695.tmp


实例3:计算数值的特殊用法

#!/bin/bash
read -p "please give number:" s1
declare -i sum=0
for ((i=1;i<=$s1;i++));do
        sum=$[$i+$s1]
done
echo "the number:$sum"

wps46A5.tmp


结果:

wps46A6.tmp


wps46A7.tmp

 

组合判断:&& ||

cmd1 && cmd2 : 如果cmd1执行完毕而且正确,则开始执行cmd2

如果cmd1执行完毕且错误,则cmd2不执行

cmd1 || cmd2 :如果cmd1执行完毕且正确,则cmd2不执行

如果cmd1执行完毕错误,则开始执行cmd2


实例:若文件目录存在则创建123文件:

wps46A8.tmp


若文件目录不存在则直接告诉没有目录,而且不执行后面的命令;

wps46A9.tmp


实例2:如果文件目录不存在则执行后面的命令,如果在执行一次该命令就会报错,因为该文件已经存在所以后面的命令不执行;

wps46BA.tmp


wps46BB.tmp


while 循环

语句格式:

while [判断式];do

程序内容

done  循环结束

实例:求100以内整数之和(经典案例拿来引用)

#!/bin/bash
declare -i sum=0
declare -i i=1
while [ $i -le 100 ]; do
        let sum+=$i
        let i++
done
echo "$sum"

wps46BC.tmp

结果:

wps46BD.tmp






until循环

语法格式:

与while相反

until condition;do

循环体

循环控制变量的修正表达式

done

进入条件:当condition失败时

退出条件:当condition成功时

实例:求100以内正整数之和

#!/bin/bash
declare -i sum=0
declare -i i=1
until [ $i -gt 100 ]; do
        let sum+=$i
        let i++
done
echo "$sum"

wps46BE.tmp

结果:

wps46BF.tmp


循环控制:

contion:提前结束本轮循环,而直接进入下一轮

break:提前终止循环

死循环:

Whine true;do

循环体

if condition;then
fi
done
#!/bin/bash
username=hzm
while true;do
        if who | grep "$username" &> /dev/null;then
        echo "$username is logged"
        break
        fi
        sleep 3
done

wps46C0.tmp

结果:当hzm用户登录的时候脚本检测到该用户,并且执行break退出;

wps46C1.tmp



while特殊用法 :

while read VARIABLE;do

循环体

done < /PATH/FROM/SOME_FILE


wps46C2.tmp


case 循环

case $变量 in

Parttern1)

分支

;;

实例:

#!/bin/bash
read -p "please give number:" num
case $num in
"one")
        echo "this is one"
        ;;
"two")
        echo "this is two"
        ;;
"tree")
        echo "this is three"
        ;;
*)
        echo "Usage $0 {one|two|three}"
        ;;
esac

wps46D2.tmp

结果:

wps46D3.tmp


wps46D4.tmp



函数库

语法:

function:把一段具有独立功能代码封装在一起,并给与命名:后续用到时,可直接通过给定函数名来调用整体代码

用法:

name () {

函数体

}


调用方法:

name[参数1,参数2]

实例:调用函数来ping一个B类地址

#!/bin/bash
#
PING() {
    if ping -c 1 -w 1 $1 &> /dev/null; then
        return 0
      else
        return 1
     fi
}
HZM="210.14"
(
for S in {1..5}; do
for I in {1..5}; do
     if PING $HZM.$S.$I; then
       echo "$HZM.$S.$I is up."  >> /root/456.txt
     else
       echo "$HZM.$S.$I is down." >> /root/789.txt
     fi
done
done
)&
wait

wps46D5.tmp


结果:显示在线的网址输出到456.txt中,不在线的输出到789.txt中;

wps46E6.tmp

wps46E7.tmp



4、写一个脚本:如果某路径不存在,则将其创建为目录;否则显示其存在,并显示内容类型;(不要怀疑,就是这么简单)


方法:

#!/bin/bash
FILEtest=/tmp/hzm
if [ -e $FILEtest ]; then
        echo "$FILEtest exist"
        file $FILEtest
else
        mkdir -p $FILEtest
fi


结果:

[root@localhost ~]# bash -x test.sh

+ FILEtest=/tmp/hzm

+ '[' -e /tmp/hzm ']'

+ echo '/tmp/hzm exist'

/tmp/hzm exist

+ file /tmp/hzm

/tmp/hzm: directory


5、写一个脚本,完成如下功能;判断给定的两个数值,孰大孰小;给定数值的方法:脚本参数,命令交互;(使用read,依然如此简单)


方法:

#!/bin/bash
read -p "Please enter two values: " -t 10 values1 values2
if [ -z "$values1" ]; then
        echo "Please enter two values"
        exit 1
fi
if [ -z "$values2" ]; then
        echo "Plz give tow integers."
        exit 1
fi
if [ $values1 -ge $values2 ]; then
        echo "Max: $values1, Min: $values2."
else
        echo "Max: $values2, Min: $values1."
fi

结果:

[root@localhost ~]# sh number.sh

Please enter two values: 4 5

Max: 5, Min: 4.

[root@localhost ~]#


6、求100以内所有奇数之和(至少用3种方法。是的这是我们的作业^_^)


第一种:

#!/bin/bash
declare -i sum=0
for i in $(seq 1 2 100); do
        sum=$[$sum+$i]
done
        echo "$sum"

结果:

[root@localhost ~]# sh 2.sh
2500
[root@localhost ~]#


第二种:

#!/bin/bash
declare -i sum=0
declare -i i=1
while [ $i -le 100 ];do
        if [ $[$i%2] -ne 0 ];then
        let sum+=$i
        fi
        let i++
done
echo $sum


结果:

[root@localhost ~]# sh 2.sh
2500
[root@localhost ~]#


第三种:

方法:

#!/bin/bash
declare -i sum=0
for i in {1..100}; do
if [ $[$i%2] -ne 0 ]; then
        sum=$[$sum+$i]
fi
done
echo "Even sum: $sum"


结果:

[root@localhost ~]# sh 1.sh
Even sum: 2500
[root@localhost ~]#


7、写一个脚本实现如下功能:

(1) 传递两个文本文件路径给脚本;

(2) 显示两个文件中空白行数较多的文件及其空白行的个数;

(3) 显示两个文件中总行数较多的文件及其总行数


方法:

#!/bin/bash
if [ $# -lt 2 ];then
        echo "two"
        exit 1
fi
if [ $(grep "^$" $1 | wc -l) -gt $(grep "^$" $2 | wc -l) ];then
        echo "more blank line file is $1" && echo "there are $(grep "^$" $1 | wc -l) blank lines"
else
        echo "more blank line file is $2" && echo "there are $(grep "^$" $2 | wc -l) blank lines"
fi
if [ $(wc -l $1 | cut -d" " -f1) -gt $(wc -l $2 | cut -d" " -f1) ];then
        echo "more line is $1" && echo "File number: $(wc -l $1)"
else
        echo "more line is $2" && echo "File number: $(wc -l $2)"
fi


结果:

[root@localhost ~]# sh 3.sh /etc/fstab /etc/init.d/functions
more blank line file is /etc/init.d/functions
there are 76 blank lines
more line is /etc/init.d/functions
File number: 834 /etc/init.d/functions
[root@localhost ~]#


8、写一个脚本

(1) 提示用户输入一个字符串;

(2) 判断:

如果输入的是quit,则退出脚本;

否则,则显示其输入的字符串内容;


方法:

#!/bin/bash

read -p "please give Parameter:" -t 10 par
if [ -z $par ]; then
        echo"please give Parameter:"
        exit 1
fi
        if [ $par == 'quit' ]; then
exit 1
        else
                echo $par
        fi


结果:输入‘quit’字符直接退出:

[root@localhost ~]# sh 4.sh
please give Parameter:quit
[root@localhost ~]#


输入其他字符:

[root@localhost ~]# sh 4.sh
please give Parameter:kkk
kkk
[root@localhost ~]#



9、写一个脚本,打印2^n表;n等于一个用户输入的值;(不好意思,我调皮了)

方法:

#!/bin/bash
read -p "please input number:" -t 10 num
        if [ -z $num ]; then
                exit 1
fi
count=2
for i in `seq 0 $num`;do
        if [ $i -eq 0 ];then
        echo "1"
        elif  [ $i -eq 1 ];then
        echo "2"
        else
        count+=x2
        echo "$count=$[2**$i]"
        fi
done


结果:

wps46E8.tmp


10、写一个脚本,写这么几个函数:函数1、实现给定的两个数值的之和;函数2、取给定两个数值的最大公约数;函数3、取给定两个数值的最小公倍数;关于函数的选定、两个数值都将通过脚本参数进行输入。

#!/bin/bash
if [ $# -lt 2 ];then
        echo "two"
        exit 1
sumnu() {
sum=$[$1+$2]
echo "the sum is $sum"
}


取两个值得最大公约数:

方法1:函数库内部调用本身(网上高手写的,思路很好拿来学习了^_^)

#!/bin/bash
#!/bin/sh
read -p "Input two positive integers: " a b
# Greatest Common Divisor
gcd()
{
   if [ $2 -eq 0 ]; then
      echo $1
   else
      gcd $2 `expr $1 % $2`
   fi
}
gcd $a $b


方法2:

求最大公约数与最小公倍数的方法:

#!/bin/bash
if [ $# -ne 2 ];then
    echo "USAGE:$0 integer1 integer2"
    exit
fi
GCD()
{
    a=$1
    b=$2
    if [ $a -lt $b ];then
        max=$b
        min=$a
    else
        max=$a
        min=$b
    fi
    if [ $[$max%$min] -ne 0 ];then
        GCD $min $[$max%$min]
    fi
    gcd=$min
}
LCM()
{
    GCD $1 $2
    b=$[$1*$2]]
    lcm=$[$b/$gcd]
}
GCD $1 $2
LCM $1 $2
echo "gcd is $gcd, lcm is $lcm"


wps46F9.tmp


sed

命令解释

是一种非交互式流编辑器,通过多种转换修改流经它的文本。默认情况下sed不会改变原文件本身,而只是对流经sed命令得文本修改,并将修改后的结果打印到屏幕。

Sed处理文本是以行为单位,每处理一行就立即打印出来,然后处理下一行,直到文本结束。


语法:

sed [options] file


选项:

d:删除 ---使用数字表示有多少行, 比如”1,5” “1 ~ 2”


实例:删除第3行。

wps46FA.tmp


实例2:删除1到3行的内容。

wps46FB.tmp


实例3:删除2到最后一行的内容。

wps46FC.tmp


实例4:删除最后一行的内容。

wps46FD.tmp


实例5:删除包含nine字符的行。

wps46FE.tmp


实例6:sed也支持正则表达式:比如”^$” 删除空行。

wps470E.tmp

wps470F.tmp


s:替换

参数:

-U:将\U后面的字符全部转换为大写

-u:吧\u后面第一个字符变成大写

-E:代表终止


实例1:将文本中is替换成to;

wps4710.tmp


实例2:将文本中第二个单词is替换成to;

wps4711.tmp


实例3:将文本中is全局替换成to;

wps4712.tmp


实例4:替换第3行的This,替换为your;

wps4723.tmp


替换is大写或者小写的字符,转换为your;

wps4724.tmp


利用正则表达式替换[ ]内的字符为your;

wps4725.tmp

另外一种做法,将[]内的内容替换成大写,利用-U来实现,反之-L转换为小写;

wps4726.tmp


那如果只是转换一个字符呢,就利用-u来实现;

wps4727.tmp


分组:(group)替换,于正则的用法一致;

wps4728.tmp


a:在指定行下一行追加;

I:在指定行上一行追加;

c:命令是匹配条件的内容替换;

y:匹配内容替换为另外内容;


实例1:在网卡目录下面最后一行增加dns1;

wps4738.tmp


实例2:将文中i替换成t,s替换成o

wps4739.tmp


i:文本修改

实例:使用i命令在第2行前插入wo字符;

wps473A.tmp


实例2:使用a命令在第二行后面插入wo字符;

wps473B.tmp


实例3:在匹配的字符前面插入字符;

wps473C.tmp


实例4:复杂一点的方法:将[]内的内容替换成yyYYe;

wps474D.tmp


实例5:将/etc/sysconfig/network-scripts/ifcfg-eth0目录下的onboot改成off;

wps474E.tmp


读入文本:

r:在匹配文中下一行添加另外的文件内容;

wps475F.tmp


打印:-n是只显示参数的内容;

wps4760.tmp


x:保持空间与模式空间互换:

h:把模式空间的内容拷贝到保持空间去:

H:把模式空间的内容追加到保持空间去:

g:把保持空间的内容写入到模式空间;

G:把保持空间的内容追加到模式空间;



wps4761.tmp


awk

命令解释:

相比较sed的逐行处理文本,awk更倾向于将一行分割为字段处理;

工作机制是把指定的文本切成字段来做处理;


变量:1.内置变量;2自定义变量;

RS:输入行分隔符;

OFS:输出时的字段分隔符;

ORS:输出时的行分隔符;


语法格式:

awk [options] 'program' file file

awk [options]'{pattern + action}' {filenames}


常用命令

print:简单输出命令

1.各项目之间使用逗号分隔,而输出时则使用输出分隔符分割;

2.输出的各item可以使用字符串或数值,当前记录字段,变量或awk得表达式;数值会被隐式转换为字符串后输出;

3.print后面item如果省略,相当于print $0:输出空白;


print  item1  item2(awk读取默认是空白作为分隔符,也可以指定分隔符)


例如: this  is   a  test----->$0    被分割为4个字符:

$1  $2  $3  $4 


FS:输入时得字段分隔符;

RS:输入行分隔符;

OFS:输出时的字段分隔符;

ORS: 输出时的行分隔符;


NF:分隔字段数;

NR:行数;所有文件一并统计;

FNR:行数,个文件分别统计;



实例1:打印指定分隔符,能显示awk特性;

wps4762.tmp


实例2:-F(指定分隔符)打印指定位置分隔符;

wps4772.tmp


实例3:利用NF参数打印显示有几个域;

wps4773.tmp


实例4:$NF打印最后一个域的字符;如果是倒数第2个域就-1;

wps4774.tmp

wps4775.tmp


实例5:NR:行数,对文件中的行做统计;

wps4776.tmp


实例6:FNR:对两个文件的内容分别计算行数;

wps4787.tmp


内置变量

OFS:输出分隔符

实例5:利用输出分隔符来给每一个空格加上:

wps4788.tmp


-v:给变量赋值;

FS=:输入分隔符;

wps4789.tmp


OFS=# 输出分隔符;

wps4799.tmp


-v:赋值

赋值给test=hi,同时在文本中加入变量;

wps479A.tmp


printf format item1,item2....

format格式的指示符都%开头u,后跟一个字符:

%c:显示字符的ASCII码;

%d,%i:十进制整数;

%e,%E:科学记数法显示值;

%f:显示浮点数;

%g:%G:以科学计数法格式或浮点数格式显示数值;

$s:显示字符串;

%u:显示无符号整数;

%%:显示%自身;


修饰符:

#:显示宽度

-:左对齐

+:显示数值的符号;

.#:取至精度


实例1:显示/etc/passwd文件里第三个字段的内容,并且加上uid字符;

wps479B.tmp


实例2:上个实例中添加上7,就是给特定字符宽度;

wps479C.tmp


实例3:用awk搜索出3个字段数,并且使其左对齐;

wKiom1YCXVDyxVvsAACmWm8MhhQ322.jpg


算术运算:+,-,*,/,%,-x,+x

赋值操作:=,+=,-=,如果自身是等于号/=/


实例:利用

wps47AD.tmp


模式:

1.正则表达式:/正则表达式/;

2.表达式:0或1,货非空的字符串满足条件;

3.BEGIN:BEGIN{语句}....END{语句};仅在awk命令program运行之前运行一次,或者之后end之后执行一次;

4.组合模式:(&&),或(||),非(|);


实例1:正则表达式:

wps47AE.tmp


实例2:显示匹配到正则表达式的行;

wps47AF.tmp


实例3:匹配数字大于等于77的数字:

wps47B0.tmp


实例4:利用正则做模式匹配,把用户名与bash类型匹配的显示出来;

wps47B1.tmp


实例5:表达行范围,NR指定行的范围;

wps47B2.tmp


实例6:在文本首尾,BEGIN开始显示一行内容,END末尾显示一行内容;

wps47B3.tmp


控制语句:

if-else:if(condition){then body}else{else body}

if(表达式) #if ( Variable in Array )
语句1
else
语句2



实例1:显示字段数大于2的行;

wps47C4.tmp


实例2:显示第三段数字大于66的整行内容,否则就显示行号;

wps47C5.tmp


while:

格式:
while(表达式){语句}


实例:用while循环计算1到5的数值:
wKiom1YCo6uBWKcTAABN_3azd7s703.jpg


for循环


格式1:

for(变量 in 数组){语句}


格式2:

for(变量;条件;表达式){语句}


实例:利用for循环语句来计算100以内正整数之和;

wps47D5.tmp


实例:遍历每一行的字符,如果大于等于3就显示出来;

wps47E6.tmp



数组

语法: awk '{pattern + action}' 或 awk 'pattern {action}'


实例:利用数组来实现对$1位置的值进行结算。

wKioL1YClUnQ1pMTAAEIQxcX3Po257.jpg


内置函数:

split(string,array[,fieldsep[,seps]]):

功能:将string表示的字符串以fieldsep为分隔符进行切片,并切片后的结果保存至array为名的数组中;数组下标从1开始;


实例:将字符做简单分割,输出除了冒号两端的字符;


[root@localhost ~]# cat 3.txt 
a:b:c:d:e:f:g:h:i
[root@localhost ~]# awk -F':' '{split($0,hzm,":")}END{for(i=1;i<=NF;i++)printf "%s\n",hzm[i]}' 3.txt 
a
b
c
d
e
f
g
h
i
[root@localhost ~]#


length(string)

功能:返回给定字串的长度;



substr(string,start[,length])

功能:从string中取子串,从start为起始位置为取length长度的子串;