一、shell程序的运行原理


1.脚本

脚本或程序源文件是一种文本文件,将文本文件转换成机器指令有两种转换方式:

  • 编译执行:预处理-->编译-->汇编-->链接;事先完成,结果:二进制程序文件

    例如:C, C++

  • 解释执行:由解释器全程参与运行过程,每次读取一行,运行一行;

    例如:  Python:程序控制结构,调用编程库完成程序编写;

    库文件:功能模块,在编程中可调用;

Bash:调用机器上命令程序文件进行程序编写;

外部命令:各应用程序提供;

2.原理

当shell运行一个程序时,会要求内核启动一个新的进程(process),以便在该进程里执行所指定的程序。内核知道如何为"编译型"程序做这件事。shell脚本并非编译型程序;当shell要求内核执行它时,内核将无法做这件事,并回应"not executable format file"错误信息。shell收到此错误信息时,就会认为"这不是编译型程序,那么一定是shell脚本","退回到shell"接着会启动一个新的/bin/sh 副本来执行该程序。当系统只有一个shell时,"退回到/bin/sh"的机制非常方便。但现行的系统都会拥有好几个shell,因此需要通过一种方式,告知内核应该以哪个shell来执行所指定的shell脚本。事实上,这么做有助于执行机制的通用化,让用户得以直接引用任何的程序语言解释器,而非只是一个命令shell。方法是,通过脚本文件特殊的第一行来设置:在第一行的开头处使用 #!这两个字符



二、shell编程中基础知识点



1.程序组织方式:

过程式编程:以指令为中心,设计算法,数据服务于算法;

对象式编程:以数据为中心,设计数据结构(类),程序服务于数据结构;

2.bash过程式编程的控制逻辑

顺序执行:逐个执行

选择执行:只执行其中一个分支

循环执行:一段代码要执行0,1或多遍

3.编程元素

变量、流程、函数

3.1变量:可变化的量,命名的内存空间

本地变量:作用范围是当前shell进程;

环境变量:作用范围是当前shell进程及其子进程;

局部变量:作用范围是某个函数执行过程;

位置参数变量:在脚本中引用传递给脚本的参数;在函数中引用传递给函数的参数;

特殊变量:$?, $*, $@, $#, $$


3.2变量类型:数值,字符;

  • 数值:整数,浮点数

  • 字符: ASCII

    例如:

    120:

    字符:1, 2, 0

    数值:120 -->

3.3变量类型的作用:定义存储空间、参与的运算类型、存储格式

3.4语言对变量类型的支持力度:

强类型:变量类型严格区分

弱类型:变量类型不严格区分;因此有默认存储机制,对于bash而言,默认为为字符

3.5 bash的变量使用特点:弱类型、无需事先声明;

4.本地变量

4.1定义: name=value

name: 变量名        =:赋值符号            value:值

4.2变量名命名要求:只能包含数字、字母和下划线;且不能以数字开头;尽量不使用大写字母,可能与环境变量冲突

4.3引用变量:${name}, $name

弱引用: "", 其内部的变量引用会被替换为变量值;

强引用:'',其变量的变量引用会保持原有字符;

命令引用:`COMMAND`,或者$(COMMAND),引用命令的执行结果;

示例:

[root@localhost ~]# animal=pig
[root@localhost ~]# echo $animal
pig
[root@localhost ~]# echo "there are some $animals;"      
there are some ;不加{},bash以为animals是变量,但是animals没有定义,所以无法输出
[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;


4.4本地变量声明为整型:

declare -i name[=value]

let name=value

4.5查看所有变量:set

[root@localhost ~]# set | grep "^\banimal\b"
animal=dog

4.7本地变量的生命周期:从创建开始,到销毁结束

销毁:

  • 自动销毁:shell进程终止;

  • 手动销毁:unset name


5.环境变量

5.1 定义

环境变量可以理解为被"导出"的本地变量

export name[=value]

declare -x name[=value]

示例:

[root@localhost ~]#export animal='dog'
[root@localhost ~]#echo 
[root@localhost ~]#echo $animal
dog
[root@localhost ~]#bash
[root@localhost ~]#echo $animal
dog


5.2查看所有环境变量:env, printenv,export

5.3销毁: unset name

 

6.脚本

6.1脚本是文本文件,不可直接运行。

6.2运行脚本:事实上是运行一个bash进程,此进程负责从脚本文件中读取一个执行逻辑,而后由bash进程负责解析并运行此逻辑;

6.3运行脚本有2种方法:

 (1) 作为解释器参数  bash/PATH/TO/SCRIPT_FILE

/bin/bash  sh01.sh

 (2)脚本作为可执行程序,给脚本文件一个执行权限,    ./PATH/TO/SCRIPT_FILE

chmod +x ./sh01.sh      # 使脚本具有执行权限

./sh01.sh               # 执行脚本

6.4 shebang(或者sha-bang)是一个由井号和叹号构成的字符串行(#!),其出现在文本文件的第一行的最前两个字符。 在文件中存在sha-bang的情况下,类Unix操作系统的程序载入器会分析sha-bang后的内容,将这些内容作为解释器指令,并调用该指令,并将载有sha-bang的文件路径作为该解释器的参数[1]。#! (magic number)为了让Linux内核识别这是什么格式的文件。

6.5 bash的常用选项:

-n: 检查脚本中的语法错误;

-x:调试执行脚本,就是单步执行

示例:作为解释器参数

[root@localhost bashtest]# vim sh01.sh
[root@localhost bashtest]# bash -n sh01.sh
[root@localhost bashtest]# bash -x sh01.sh
+ useradd shtest1
+ echo shtest1
+ passwd --stdin shtest1
Changing password for user shtest1.
passwd: all authentication tokens updated successfully.
[root@localhost bashtest]# cat sh01.sh
#!/bin/bash
useradd shtest1
echo shtest1 | passwd --stdin shtest1


示例:作为可执行文件

[root@localhost bashtest]# vim sh02.sh
[root@localhost bashtest]# chmod +x sh02.sh
[root@localhost bashtest]# ll
total 8
-rw-r--r--. 1 root root 67 Sep 25 17:50 sh01.sh
-rwxr-xr-x. 1 root root 66 Sep 25 17:54 sh02.sh
[root@localhost bashtest]# ./sh02.sh
Changing password for user shtest2.
passwd: all authentication tokens updated successfully.
[root@localhost bashtest]# cat ./sh02.sh
#!/bin/bash
useradd shtest2
echo shtest1 | passwd --stdin shtest2


6.7命令状态结果:

bash进程用于追踪执行的命令成功与否的状态:

0: 成功           1-255:失败

6.8特殊变量: $?:上一条命令的执行状态结果;属于布尔型

6.9自定义脚本的状态结果: exit [n]

注意:脚本中任何位置执行了exit命令即会终止当前shell进程;

 

三、条件测试

 

1.条件测试作用:界定程序执行环境;

2.测试机制:

 (1)根据运行的命令的状态结果;

 (2) 测试表达式

test EXPRESSION

 [EXPRESSION ]

 `EXPRESSION `

3.shel下测试的命令: test, [ 和关键字 ` `, 可以针对 数字、字符串、文件进行测试。

"["是一个可执行程序,路径是"/bin/[", [ 是一个命令,它是内置命令test的简写形式,只不过它要求最后一个参数必须是 ]。在使用[ ] 进行判定的时候有一个事项要注意的是,在括号两边以及符号两边均要有空格

3.1 bash [ ] 单双括号

基本要素:

l  [ ] 两个符号左右都要有空格分隔

l  内部操作符与操作变量之间要有空格:如  [  “a”  =  “b”  ]

l  字符串比较中,> < 需要写成\> \< 进行转义

l  [ ] 中字符串或者${}变量要使用"" 双引号扩住,避免值未定义引用而出错

l  [ ] 中可以使用 –a –o进行逻辑运算

l  [ ] bash 内置命令:[ is a shell builtin

l  -a 逻辑与(and缩写),操作符两边均为真,结果为真,否则为假

l  -o 逻辑或(or缩写),操作符两边有一边为真,结果为真

 

3.2 bash  [[  ]] 双方括号

基本要素:

l  ` ` 两个符号左右都要有空格分隔

l  内部操作符与操作变量之间要有空格:如  [[  “a” =  “b”  ]]

l  字符串比较中,可以直接使用 > < 无需转义

l  ` ` 中字符串或者${}变量要使用"" 双引号扩住,否则会进行模式和元字符匹配

l  [[] ] 内部可以使用&&  || 进行逻辑运算

l  ` ` bash keyword[[ isa shell keyword

 

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

                 $A -gt $B:是否大于;是则为“真”,否则为“假”;

                 $A -ge $B: 是否大于等于;

                 $A -lt $B:是否小于(less than);

                 $A -le $B: 是否小于等于;

                 $A -eq $B: 是否等于;

                 $A -ne $B:是否不等于;

5. 字符串测试:ASCII数值越大,字符比较时其值越大;

                 "$A" >"$B":是否大于;

                 "$A" <"$B":是否小于;

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

                 "$A" !="$B":是否不等于;

                

注意:应该使用` EXPRESSION `

例如:

[root@localhostbashtest]# [[ a > c ]]
[root@localhostbashtest]# echo $?
1
[root@localhostbashtest]# [[ a < c ]]
[root@localhostbashtest]# echo $?
0
[root@localhostbashtest]# [[ a == c ]]
[root@localhostbashtest]# echo $?
1
[root@localhostbashtest]# [[ a != c ]]
[root@localhostbashtest]# echo $?
0

                 -z "$A":是否为空;空则为“真”,否则为“假”

                 -n "$A":是否不空;不空则“真”,空则为“假”

例如:

[root@localhostbashtest]# name=mage
[root@localhostbashtest]# [[ -z $name ]]
[root@localhostbashtest]# echo $?
1
[root@localhostbashtest]# [[ -n $name ]]
[root@localhostbashtest]# echo $?
0


6.文件测试:测试文件的存在性以及属性;

                 -e $file: 是否存在;存在则为“真”,否则为“假”;

                 -a $file: 同上;

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

                 -d $file:文件是否存在且为目录;

                 -h $file:是否存在且为符号链接文件;

                 -L $file: 同上

                 -b $file:是否存在且为块设备文件;

                 -c $file:是否存在且为字符设备文件;

                 -S $file:是否存在且为套接字文件;

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

                 -r $file: 当前用户对文件是否拥有读权限;

                 -w $file:当前用户对文件是否拥有写权限;

                 -x $file:当前用户对文件是否拥有执行权限;

                 -u $file:文件是否拥有SUID权限;

                 -g $file:文件是否拥有SGID权限;

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

                 -O $file: 当前用户是否为指定文件的属主;

                 -G $file: 当前用户是否为指定文件的属组;

7.双目操作符:

                 $file1 -nt $file2: file1是否新于file2, file1的最近一次的修改时间戳是否晚于file2的;

                 $file1 -ot $file2: file1是否旧于file2, file1的最近一次的修改时间戳是否早于file2的;

                 $file1 -ef $file2:file1与file2是否指向了同一个inode;测试二者是否为同一个文件的硬链接;

例如:

[root@localhosttmp]# cd ./bashtest/
[root@localhostbashtest]# ll
total 8
-rw-r--r--. 1 rootroot 67 Sep 25 17:50 sh01.sh
-rwxr-xr-x. 1 rootroot 66 Sep 25 17:54 sh02.sh
[root@localhostbashtest]# file1="/tmp/bashtest/sh01.sh" 
 [root@localhost bashtest]#file2="/tmp/bashtest/sh02.sh" 
[root@localhostbashtest]# [ -d "$file1" ]
[root@localhostbashtest]# echo $?
1
[root@localhostbashtest]# [ -f "$file1" ]
[root@localhostbashtest]# echo $?
0
[root@localhostbashtest]# [ "$file1" -nt "$file2" ]
[root@localhostbashtest]# echo $?
1
[root@localhostbashtest]# [ "$file1" -ot "$file2" ]
[root@localhostbashtest]# echo $?
0


8.特殊设备:

              /dev/null: 空,bit buckets,吞下所有数据,并直接丢弃;

           /dev/zero:吐出一堆0;

 

四、条件判断

 

1.单分支if语句

           if CONDITION; then

                 if-true-分支

           fi

2.双分支if语句

           if CONDITION; then

                 if-true-分支

           else

                 if-false-分支

           fi

3.多分支if语句格式:

if CONDITION1; then

if-CONDITION1-true-分支

elif CONDITION2; then

if-CONDITION2-true-分支

else

if-ALL-false-分支

fi

示例:通过脚本参数传递一个文件路径给脚本,判断其类型;

#!/bin/bash
if [ $# -lt 1 ]; then
         echo"Usage: $0 "
exit 1
fi
if [ -f $1 ]; then
   echo "Rgular file."                         
elif [ -d $1 ]; then
   echo "Directory."                          
elif [ -h $1 ]; then
   echo "Symbolic link."                              
elif [ -b $1 ]; then
   echo "Block special."
elif [ -c $1 ]; then
   echo "Charactoer special."
elif [ -S $1 ]; then
   echo "Socket file."
else                               
   echo "file not exist or unknown type."
fi



练习1

写一个脚本如果某路径不存在,则将其创建为目录;否则显示其存在,并显示内容类型;

[root@localhost bashtest]# vim test1.sh
#!bin/bash
fname="/tmp/testdir"
if [ -e $fname ]; then
   echo "$fname exists."
   file $fname
else
   mkdir -p $fname
fi
[root@localhost bashtest]# bash -n test1.sh
[root@localhost bashtest]# bash -x test1.sh
+ fname=/tmp/testdir
+ '[' -e /tmp/testdir ']'
+ mkdir -p /tmp/testdir
[root@localhost bashtest]# echo $?
0

        

3.脚本参数(位置参数变量):

3.1 # ./script.sh  /etc/fstab  /etc/grub2.cfg

$0                $1              $2

位置参数变量:$1, $2, ……两位数字以后加花括号${10}

例如:传递一个文件路径给脚本,如果文件存在且为普通文件,就显示文件不存在或不是普通文件

[root@localhost bashtest]# vim test2.sh
#!/bin/bash
if [ -f $1 ];then
       wc -l $1
else
       echo "$1 not exists or is not a file."
fi
[root@localhost bashtest]# bash -n test2.sh
[root@localhost bashtest]# chmod +xtest2.sh
[root@localhost bashtest]# ./test2.sh/etc/fstab
11 /etc/fstab
[root@localhost bashtest]# ./test2.sh/etc/grub2.cfg
129 /etc/grub2.cfg
[root@localhost bashtest]# ./test2.sh /etc
/etc not exists or is not a file.

3.2.特殊变量:

$?: 命令的状态结果;

$#: 传递给脚本或函数的参数的个数;

$*$@: 列出传递给脚本或函数的参数列表;

例如:

[root@localhost bashtest]# cp ./test3.sh./test4.sh
[root@localhost bashtest]# vim ./test4.sh
#!/bin/bash
if [ -f $1 ];then
       lines=$(wc -l $1 | cut -d' ' -f1)
       echo "$1 has $lines lines."
else
       echo"$1 not exits or is not a file"
fi
echo "Total arguments : $#"
echo "They are $*"
[root@localhost bashtest]# ./test4.sh/etc/fstab /etc/grub2.cfg
/etc/fstab has 11 lines.
Total arguments : 2
They are /etc/fstab /etc/grub2.cfg

例如:

[root@localhost bashtest]# vim ./test4.sh
#!/bin/bash
if [ $# -lt 1 ]; then
       echo "Usage: $0 need a path"
exit 1
fi
if [ -f $1 ];then      
       lines=$(wc -l $1 | cut -d' ' -f1)
       echo "$1 has $lines lines."
else
       echo"$1 not exits or is not a file"
fi
[root@localhost bashtest]# ./test4.sh
Usage: ./test4.sh need a path
[root@localhost bashtest]# ./test4.sh/etc/fstab
/etc/fstab has 11 lines.

                   shift[n]:轮替

例如:

[root@localhost bashtest]# vim shif.sh
#!/bin/bash
echo $*
echo $1
shift
echo $1
shift
echo $1
shift
[root@localhost bashtest]# ./shif.sh a b c
a b c
a
b
c

 

4.与用户交互read命令

4.1作用:Read aline from the standard input and split it into fields.

详情 help read

例如:

[root@localhost bashtest]# read f1 f2
/etc/fstab /etc/grub2.conf
[root@localhost bashtest]# echo $f1
/etc/fstab
[root@localhost bashtest]# echo $f2
/etc/grub2.conf

常用参数:

-p prompt     outputthe string PROMPT without a trailing newline before attempting to read

-ttimeout    time out and return failure ifa complete line of input is

例如:

[root@localhost bashtest]# read -p"What's your name ?" -t 10 name
What's your name ?lan
[root@localhost bashtest]# echo $name
lan

示例:写一个脚本能够添加用户,并提示输入用户名和密码

[root@localhost bashtest]# vim read.sh
#!/bin/bash
read -p "Please input a username:" -t 30 uname
if [ -z "$uname" ];then
      echo “Time out!”
exit 1
fi
 
if id $uname &> /dev/null;then
       echo "$uname has existd."
else
       useradd $uname
       read -p "Please input password : " -t 30 upasswd
       echo $upasswd | passwd --stdin $uname
       echo "Add User:$uname successfully "
fi

 

结果:

Shell脚本编程与sed,awk工具的使用--9月15日课程作业_第1张图片

5. 命令引用

5.1格式:`COMMAND`,$(COMMAND)

5.2引用命令的执行结果示例:

[root@localhost bashtest]# vim test3.sh

#!/bin/bash

if [ -f $1 ];then

       lines=$(wc -l $1 | cut -d' ' -f1)

       echo "$1 has $lines lines."

else

       echo"$1 not exits or is not a file"

fi

[root@localhost bashtest]# bash -n test3.sh

[root@localhost bashtest]# chmod +xtest3.sh

[root@localhost bashtest]# ./test3.sh/etc/fstab

/etc/fstab has 11 lines.

 

练习2:

写一个脚本,完成如下功能,判断给定的两个数值,孰大孰小。给定数值的方法:脚本参数,命令交互。

[root@localhost bashtest]# vim big.sh

#!/bin/bash

read -p " Enter 2 integers : " -t30 num1 num2

if [ -z "$num1" -o -z"$num2" ];then

       echo "Time Out! You need input two integers."

       exit 1

fi

if [ $num1 -gt $num2 ];then

       echo "Max:$num1,Min:$num2"

elif [ $num1 -lt $num2 ];then

       echo "Max:$num2,Min:$num1"

else

       echo "$num1 equal $num2 "

fi

运行结果:

Shell脚本编程与sed,awk工具的使用--9月15日课程作业_第2张图片

五、循环语句

                  

1.for语句

1.1格式:

for VARIABLE in LIST; do

        循环体

done

 

1.1.1     LIST是由一个或多个空格或换行符分隔开的字符串组成;把列表的每个字符串逐个赋值给VARIABLE表示的变量;

进入条件:列表非空;

退出条件:列表遍历结束;

1.1.2     LIST的生成方法:

 (1) 整数列表

         (a) {start..end}

         (b) $(seq [start `step` end)

 (2) 直接给出列表

 (3) glob

 (4) 命令生成

 

例如:    使用添加5个用户,user1-user10

直接给出列表:

[root@localhost bashtest]# vim for1.sh
#!bin/bash
for uname in user1 user2 user3 user4user5;do
       if id $uname &> /dev/null; then
                echo "$uname exists."
       else
               useradd $uname
       echo "Add user $uname successfully."
       fi
done

Shell脚本编程与sed,awk工具的使用--9月15日课程作业_第3张图片

正数列表:

[root@localhost bashtest]# vim ./for2.sh
#!bin/bash
for i in {6..10};do
       if id user$i &> /dev/null; then
                echo "user$i exists."
       else
                useradd user$i
       echo "Add user user$i successfully."
       fi
done

Shell脚本编程与sed,awk工具的使用--9月15日课程作业_第4张图片

示例:glob

[root@localhost bashtest]# vim glob.sh
#!bin/bash
for fname in /tmp/bashtest/*;do
       file $fname
done

Shell脚本编程与sed,awk工具的使用--9月15日课程作业_第5张图片

示例:命令生成列表

[root@localhost bashtest]# vim comand.sh
#!/bin/bash
for username in $(cut -d: -f1 /etc/passwd);do
echo "$username primary group: $(id -n -g $username)."
done

Shell脚本编程与sed,awk工具的使用--9月15日课程作业_第6张图片

1.1.3  for循环的特殊用法

for((expr1;expr2;expr3)); do

  循环体

done

 

 expr1:定义控制变量,并初始赋值;

 expr2:循环控制条件;

 expr3:修正控制变量

 进入条件:控制条件为“真”

 退出条件:控制条件为“假”

 

示例:求100以内所有正整数之和;

#!/bin/bash
declare -i sum=0
for ((i=1;i<=100;i++)); do
         letsum+=$i
done
echo "Sum: $sum."

       

2.算术运算

(1)$[$A+$B]

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

(3)let VARIABLE=$A+$B

(4)VARIABLE=$(expr $A + $B)

例如:

[root@localhost bashtest]# a=3
[root@localhost bashtest]# b=4
[root@localhost bashtest]# sum=$a+$b
[root@localhost bashtest]# echo $sum
3+4
[root@localhost bashtest]# sum=$[$a+$b]
[root@localhost bashtest]# echo $sum
7
[root@localhost bashtest]# sum2=$(($a+$b))
[root@localhost bashtest]# echo $sum2
7
[root@localhost bashtest]# let sum3=$a+$b
[root@localhost bashtest]# echo $sum3
7
[root@localhost bashtest]# sum4=$(expr $a +$b)
[root@localhost bashtest]# echo $sum4
7

 

练习3:

100以内所有偶数之和

1.for语句

[root@localhost bashtest]# vim oddsum1.sh
#!bin/bash
declare -i sum=0
for i in $(seq 1 2  99);do
       sum=$(($sum+$i))
done
       echo $sum

或者

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

 

2.while语句

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

 

3.until语句

#!/bin/bash
declare -i m=1
declare -i sum=0
until [ $m -gt 99 ]
do
       sum+=m
       let m+=2
done
echo $sum

 

3.增强型赋值

3.1 +=

sum=$[$sum+$i]表达式可以写成let sum+=$i

3.2   -=, *=, /=, %=

let count=$[$count+1]  --> let count+=1 --> let count++

let count=$[$count-1] --> let count-=1--> let count--

例如:显示/etc目录下所有普通文件列表,而后统计一共有多少个文件;

#!/bin/bash
declare -i count=0
for file in /etc/*; do
if [ -f $file ]; then
         letcount++
         echo"$count $file"
fi
done
echo"Total: $count files."

 

4.测试表达式

4.1 整数测试:-gt, -lt, -ge,-le, -eq, -ne

4.2字符串测试:==, >, <,!=, -z, -n, =~

注意:

          (1) 字符串等会比较测试:["$hostname" == 'localhost'  ]

          (2) 模式匹配测试:[["STRING" =~ PATTERN ]]

4.3组合测试条件:

4.3.1条件间逻辑运算::多个条件要同时满足;:多个条件满足其一即可;:对指定的条件取反;

4.3.2表达式组合:

与:[[CONDITION1 -a CONDITION2 ]]

或:[[CONDITION1 -o CONDITION2 ]]

非:[ ! CONDITION]

 

4.3.3命令组合:

与:COMMAND1 && COMMAND2

或:COMMAND1 || COMMAND2

非:! COMMAND

 

4.4短路操作符:&&,只要左侧为真时即为真,其余都为假

类似于:

if COMMAND1; then

         COMMAND2

fi

4.6短路操作符:||左侧为真时,或者右侧为真时,即为真;其余

true || true = true           true || false = true             false || true = true          false || false = false

类似于:

if ! COMMAND1; then

         COMMAND2

fi

 

4.7   COMMAND1 && COMMAND2 || COMMAND3 该命令组合类似于:

                                               ifCOMMAND1; then

                                                        COMMAND2

                                               else

                                                        COMMAND3

                                               fi

 

例如:写一个脚本实现如下功能:获取当前主机的主机名,如果当前主机的主机名为空,或者为localhost,则将其修改为www.magedu.com

#!/bin/bash
hostname=$(hostname)
if [ -z "$hostname" -o"$hostname" == "localhost" ]; then
         hostnamewww.magedu.com
fi
if [ $line1 -lt $line2 ];then
       echo "$fpath2 has max total lines : $line2"
elif [ $line1 -gt $line2 ];then
       echo "$fpath2 has max total lines : $line1"
else
       echo "They have the same total lines:$line1"

练习4:

写一个脚本:(1) 传递两个文本文件路径给脚本;(2) 显示两个文件中空白行数较多的文件及其空白行的个数;(3) 显示两个文件中总行数较多的文件及其总行数;

[root@localhost bashtest]# vim zero.sh
#!bin/bash
read -p "Input two file paths:"-t 30 fpath1 fpath2
if [ -z $fpath1 -o -z $fpath2 ];then
       echo "Error!"
       exit 1
fi
if [ ! -e $fpath1 -o ! $fpath2 ];then
       echo "$fpath1 OR $fpath2 does not exist."
       exit 2
fi
blank1=$(grep '^$' $fpath1 | wc -l)
blank2=$(grep '^$' $fpath2 | wc -l)
if [ $blank1 -lt $blank2 ];then
       echo "$fpath2 has max blank lines : $blank2"
elif [ $blank1 -gt $blank2 ];then
       echo "$fpath1 has max blank lines : $blank1"
else
       echo "They have the same blank lines:$blank1"
fi
line1=$(wc -l $fpath1 | cut -d ' ' -f1)
line2=$(wc -l $fpath2 | cut -d ' ' -f1)
if [ $line1 -lt $line2 ];then
       echo "$fpath2 has max total lines : $line2"
elif [ $line1 -gt $line2 ];then
       echo "$fpath1 has max total lines : $line1"
else
       echo "They have the same total lines:$line1"
fi
[root@localhost bashtest]# bash zero.sh
Input two file paths:/tmp/c1 /tmp/c2
/tmp/c2 has max blank lines : 3
/tmp/c2 has max total lines : 6

练习5:

写一个脚本:(1) 提示用户输入一个字符串;(2) 判断:如果输入的是quit,则退出脚本;否则,则显示其输入的字符串内容;

[root@localhost bashtest]# vim test5.sh
#!bin/bash
read -p "Enter a string:" -t 30s1
if [ -z $s1 ];then
       echo "Error:$s1 is blank"
       exit 1
fi
if [ "$s1" == "quit"];then
        echo "Quit script!"
       exit 0
else
       echo $s1
fi
[root@localhost bashtest]# bash test5.sh
Enter a string:s
s
[root@localhost bashtest]# bash test5.sh
Enter a string:quit
Quit script!

练习6:写一个脚本,打印2^n表;n等于一个用户输入的值;

[root@localhost bashtest]# vim test6.sh
#!bin/bash
read -p "Input Number:" -t 20 num
if [ -z "$num" -o $num -lt 0];then
       echo "Error:NO valid Number!"
       exit 1
fi
declare -i sum=1
echo "2^0=1 "
if [ $num -gt 0 ];then
       for m in $(seq 1 $num)
       do
               let sum=$sum*2
                echo  "2^$m = $m "
       done
fi

 

5.循环嵌套

示例:九九乘法表

#!/bin/bash                     
for j in {1..9}; do
 for i in $(seq 1 $j); do
         echo-n -e "${i}X${j}=$[$i*$j]\t"
done
echo
done

结果:

Shell脚本编程与sed,awk工具的使用--9月15日课程作业_第7张图片

                   

6.case语句                    

6.1 使用场景:判断某变量的值是否为多种情形中的一种时使用;

6.2语法:

     case$VARIABLE in

     PATTERN1)

    分支1

              ;;

     PATTERN2)

    分支2

              ;;

     PATTERN3)

    分支3

              ;;

     ...

      *)

    分支n

              ;;

    esac

 

PATTERN可使用glob模式的通配符:

   *:任意长度的任意字符;

   ?:任意单个字符;

   []:指定范围内的任意单个字符;

   a|b:多选1

 

示例:提示键入任意一个字符;判断其类型;

#!/bin/bash
read-p "Plz enter a character: " char
 case$char in
    [a-z])
   echo "A character."
    ;;
    [0-9])
   echo "A digit."
    ;;
   *)
  echo "A special character."
   ;;                                    
esac

7.while语句

7.1语法格式:

while CONDTION; do

         循环体

         (控制变量的修正表达式)

done

 

7.2  进入条件:当CONDITION为“真”;

退出条件:当CONDITION为“假”;

 

示例:求100以内所有正整数之和;

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

 

示例:打印九九乘法表

#!/bin/bash
declare -i i=1
declare -i j=1
while [ $j -le 9 ]; do
         while[ $i -le $j ]; do
                   echo-e -n "${i}X${j}=$[$i*$j]\t"
                   leti++
         done
                echo
         let i=1
         let j++
done

7.3 while循环的特殊用法:遍历文件的每一行

whileread VARIABLE; do

                                               循环体

done< /PATH/FROM/SOME_FILE

 

示例:找出UID为偶数的所有用户,显示其用户名和ID号;

#!/bin/bash
while read line; do
   userid=$(echo $line | cut -d: -f3)
   if [ $[$userid%2] -eq 0 ]; then
       echo $line | cut -d: -f1,3
   fi     
done < /etc/passwd

                                 

结果:

[root@localhost bashtest]# bash while.sh
root:0
daemon:2
lp:4
shutdown:6
mail:8
games:12
ftp:14
colord:998
ntp:38
saslauth:996
rpc:32
rtkit:172
nfsnobody:65534
sssd:994
avahi-autoipd:170
gdm:42
gnome-initial-setup:992
sshd:74
avahi:70
tcpdump:72
lan:1000
apache:48
shtest1:1002
huawei:1004
hua:1006
51cto:1008
mage:1010
user1:1012
user3:1014
user5:1016
user7:1018
user9:1020

8.unitl语句

8.2 语法格式:

until CONDITION; do

         循环体

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

done

 

8.2  进入条件:当CONDITION为“假”时

退出条件:当CONDITION为“真”时

 

示例:求100以内所有正整数之和

#!/bin/bash

declare -i sum=0

declare -i i=1

until [ $i -gt 100 ]; do

         letsum+=$i

         leti++

done

echo "Sum: $sum."

 

9.循环控制

9.1  continue [n]:提前结束本轮循环,而直接进入下一轮;

break [n]:提前结束循环;break 本身是结束本轮循环,加上数值后可以指明跳出哪个循环

 

9.2  while循环:

9.2.1    while CONDITION; do

         .......

         ifCONDITION2; then

                   break[n]

         fi

         done

 

9.2.2   while CONDITION; do

         ......

         ifCONDITION2; then

                   continue[n]

         fi

                   ......

         done

 

示例:求100以内所有偶数之和;

#!/bin/bash
declare -i sum=0
declare -i i=0
while [ $i -le 100 ]; do
         leti++
         if[ $[$i%2] -eq 1 ]; then
                   echo"$i is a odd."
                   continue
         fi
         letsum+=$i
done
echo "Sum: $sum."

9.3 死循环:

9.3.1  while true; do

          循环体

         ifCONDTION; then

                   break

         fi

done

 

9.3.2   until false; do

         循环体

         ifCONDITION; then

                   break

         fi

         done

 

示例:每隔3秒钟查看当前系统上是否有名为“hua”的用户登录;如果某次查看hua登录了,则显示hua已经登录;如果未登录,就显示仍然未来,并显示这是已经是第多少次查看了;

#!/bin/bash
username=$1
declare -i count=0
while true; do
if who | grep "^$username"&> /dev/null; then
           echo "$username is logged."
break
else
         letcount++
         echo"$count $username is not login."
fi
         sleep3
done

 

#!/bin/bash

declare -i count=0
username=$1
if [ $# -lt 1 ]; then
         echo"At lease one argument."
         exit1
fi
if ! id $username &> /dev/null; then
         echo"No such user."
         exit2
fi
 
until who | grep "^$username"&> /dev/null; do
         letcount++
         echo"$count $username is not login."
         sleep3
done
echo "$username is logged on."

五、函数的使用

 

1. 函数

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

1.2  函数作用:代码重用;模块化编程;

1.3函数的使用方法:

      先定义:编写函数代码

      后调用:给出函数名,还可按需传递参数

1.4 定义方法:

(1)function f_name {

                                               函数体

             }

(2)f_name() {

                                               函数体

       }

 

1.5 调用函数: f_name [argu1,argu2, ...]

自定义函数状态返回值:

                            return[#]     0: 成功              1-255:失败

注意:函数代码执行时,一旦遇到return,函数代码终止运行,函数返回;

 

2.模块化编程

2.1 功能:把脚本文件中的代码分隔为多段,放在不同的文件中。假设/root/bin/srv目录有两个文件:(1) 函数文件   (2) 脚本文件

 

2.2为脚本使用配置文件,一个文件中只定义变量,脚本文件source此变量定义的文件

2.3 变量的作用域:

                   局部变量: localVARIABLE=value

 

2.4存活时间:函数执行开始,至函数返回结束;

练习7:写一个脚本,写这么几个函数:函数1、实现给定的两个数值的之和;函数2、取给定两个数值的最大公约数;函数3、取给定两个数值的最小公倍数;关于函数的选定、两个数值的大小都将通过交互式输入来提供。

求解最大公约数思路:辗转相除法

#!/bin/bash
input() {
read -p "Input two integers:" -t30 m n
if [ -z $m -o -z $n ]
then
   echo  "Error:No ValidNumbers"
   exit 1
fi
}
#求和
sum() {
input
sum=$[$m+$n]
echo "$m+$n = $sum"
}
#最大公约数
gcd() {
input
while [ ! $m -eq $n ]; do
  if[[ $m -gt $n ]]; then
   let m-=n
 else
   let n-=m
  fi
done
 echo "gcd:$m"
}
#最小公倍数=====================================================
lcm() {
input
if [ $m -gt $n ]
then
       for j in `seq 1 $[$m*$n]`
       do
       if [ $[$m*$j%$n] -eq 0 ]
       then
                echo "$[$m*$j]"
                break
       fi
       done
elif [ $m -le $n ]
then
                for j in `seq 1 $[$m*$n]`
       do
       if [ $[$n*$j%$m] -eq 0 ]
       then
                echo "$[$n*$j]"
                break
        fi
       done
fi
}
read -p "Choose'sum'or'gcd'or'lcm':" -t 20 cal
case $cal in
sum)
  sum
;;
gcd)
  gcd
;;
lcm)
  lcm
;;
*)
 echo "input error:Usg:'sum'|'gcd'|'lcm'"
esac

结果:

Shell脚本编程与sed,awk工具的使用--9月15日课程作业_第8张图片

六、数组的使用

 

1.数组:连续的多个独立内存空间;每个内存空间相当于一个变量;数组的各个变量称为数组的元素,有时也称为下标变量

 

2.传统数组:索引为数字,从0开始编号;

                            declare-a ARRAY_NAME

关联数组:索引可以自定义,可以使用任意字符串做索引;

                            declare-A ARRAY_NAME

3.数组元素的赋值方式

          (1) 一次只赋值一个元素

                   array[index]=value

          (2) 一次赋值全部元素

                   array=(val1   val2   val3 ...)

          (3) 指定索引进行赋值

                   array=([0]="val1"[3]="val2")

          (4) read -a array

 

4.引用元素:${array[index]}

5.获取长度:${#array[*]},${#array[@]}

6.数组删除:unset 数组[下标]

示例:

[lan@localhost ~]$ animal=(dog cat tigerpanda)
 [lan@localhost~]$ echo ${#animal[*]}
4
 [lan@localhost~]$ echo ${animal[0]}
dog
 [lan@localhost~]$ echo ${animal[1]}
cat
[lan@localhost ~]$ echo ${animal[2]}
tiger
[lan@localhost ~]$ echo ${animal[3]}
panda
[lan@localhost ~]$ echo ${animal[*]}
dog cat tiger panda

 

七、sedawk工具的使用

sed、awk