Linux Shell脚本编程提高

Shell脚本
378
0
0
2023-02-08

Shell是一个命令解释器,它解释由用户输入的命令并且把它们送到内核,不仅如此,Shell有自己的编程语言用于对命令的编辑,它允许用户编写由shell命令组成的程序.Shel编程语言具有普通编程语言的很多特点,比如它也有循环结构和分支控制结构等,用这种编程语言编写的Shell程序与其他应用程序具有同样的效果,下面我们会介绍Shell-Script的编写.

文章声明 该系列文章部分文字描述,整理于以下文献,化繁为简. 《鸟哥的Linux私房菜 (基础学习篇 第三版)》 - 作者:鸟哥 《Linux就该这么学》 - 作者:刘遄

Shell 条件判断

◆按文件类型判断◆

img

1.使用 [] 来执行判断,并使用echo $? 判断执行结果

[root@localhost ~]# ls
lyshark.log  passwd.awk

[root@localhost ~]# [ -e ./lyshark.log ]
[root@localhost ~]# echo $?              #返回0表示文件 ./lyshark.log 存在
0

[root@localhost ~]# [ -e ./shar ]        #返回1表示文件 ./shar 不存在
[root@localhost ~]# echo $?
1

2.我们可以结合之前所学的 && 和 || 实现判断后输出判断结果

[root@localhost ~]# ls
lyshark.log  passwd.awk

[root@localhost ~]# [ -e ./lyshark.log ] && echo "check ok" || echo "None"
check ok

[root@localhost ~]# [ -e ./shar ] && echo "check on" || echo "None"
None

◆按文件权限判断◆

img

1.使用 -r 判断文件是否可读,和可执行

[root@localhost ~]# ls
lyshark.log passwd.awk

[root@localhost ~]# [ -r ./lyshark.log ] && echo "check ok" || echo "None"
check ok

[root@localhost ~]# [ -x ./lyshark.log ] && echo "check ok" || echo "None"
None

[root@localhost ~]# [ -r ./lyshark.log ] && [ -x ./lyshark.log ] && echo "可读,可执行" || echo "可读,不可执行" || echo
"不可读,不可执行"

可读,不可执行

◆两文件之间的比较◆

img

1.通过 -ef 判断是否是相同的I节点

[root@localhost ~]# ls -li
total 0
8409155 -rw-r--r-- 2 root root 0 Sep 25 21:39 lyshark_ln.log
8409155 -rw-r--r-- 2 root root 0 Sep 25 21:39 lyshark.log

[root@localhost ~]# [ ./lyshark.log -ef lyshark_ln.log ] && echo "ok" || echo "no"
ok

◆数值之间的比较◆

img

1.比较两数字,大小关系

[root@localhost ~]# [ 23 -ge 24 ] && echo "23 >= 24 " || echo "23 <= 24"
23 <= 24

[root@localhost ~]# [ 30 -ge 24 ] && echo "30 >= 24 " || echo "30 <= 24"
30 >= 24

◆字符串之间的比较◆

img

1.字符串之间的比较例子,字符串是否为空

[root@localhost ~]# name=LyShark
[root@localhost ~]# age=""
[root@localhost ~]# unset sex

[root@localhost ~]# [ -z "$name" ] && echo "字符串空" || echo "不为空"
不为空

[root@localhost ~]# [ -z "$age" ] && echo "字符串空" || echo "不为空"
字符串空

[root@localhost ~]# [ -z "$sex" ] && echo "字符串空" || echo "不为空"
字符串空

2.两个字符串相等比较

[root@localhost ~]# x=100
[root@localhost ~]# y=200
[root@localhost ~]# z=100

[root@localhost ~]# [ "x" == "y" ] && echo "yes" || echo "no"
no
[root@localhost ~]# [ "$x" == "$y" ] && echo "yes" || echo "no"
no

[root@localhost ~]# [ "$x" == "$z" ] && echo "yes" || echo "no"
yes

◆多重条件判断◆

img

1.逻辑与:判断变量x不为空,并且x大于20

[root@localhost ~]# declare -i x=10
[root@localhost ~]# [ -n "$x" -a "$x" -gt 20 ] && echo "yes" || echo "no"
no
[root@localhost ~]# declare -i x=30
[root@localhost ~]# [ -n "$x" -a "$x" -gt 20 ] && echo "yes" || echo "no"
yes

2.逻辑非:判断x变量不为空,或!取反

[root@localhost ~]# declare -i x=30

[root@localhost ~]# [ -n "$x" ] && echo "yes" || echo "no"
yes

[root@localhost ~]# [ ! -n "$x" ] && echo "yes" || echo "no"
no

IF条件判断

if、then、else语句用于判断给定的条件是否满足,并根据测试条件的真假来选择相应的操作.if/else仅仅用于两分支判断,多分支的选择时需要用到if/else语句嵌套、if/elif/else和case多分支选择判断结构.

IF结构例子: 一个就简单的单分支结构.测试条件后如果没有";"则then语句要换行.

#!/bin/sh
echo "Please input a Number:"
read Num
 
if [ "$Num" -lt 15 ]
then
	echo "$Num <= 15"
fi

IF/IF-ELSE结构: 一个双分支结构,如下执行删除文件,成功返回then是否返回else.

#!/bin/sh
 
echo "Please input the file which you want to delete:"
read file
 
if rm -f "$file"
then
  echo "Delete the file $file  sucessfully!"
else
  echo "Delete the file $file failed!"
fi

IF/ELSE嵌套: 可同时判断三个或三个以上条件,但要注意if与else配对关系,else语句总是与它上面最近的未配对的if配对.

#!/bin/bash
#提示用户输入分数(0-100)
echo "Please Input a integer(0-100): "
read score

#判断学生的分数类别
if [ "$score" -lt 0 -o "$score" -gt 100 ]
then
   echo "The score what you input is not integer or the score is not in (0-100)."
else
     if [ "$score" -ge 90 ]
     then
         echo "The grade is A!"
     else
          if [ "$score" -ge 80 ]
          then
              echo "The grade is B!"
          else
               if [ "$score" -ge 70 ]
              then
                   echo "The grade is C!"
              else
                   if [ "$score" -ge 60 ]
                   then
                        echo "The grade is D!"
                   else
                        echo "The grade is E!"
                   fi
              fi
         fi
    fi
fi

IF/ELIF/ELSE结构: if/else嵌套在编程中很容易漏掉then或fi产生错误,而且可读性很差,因此引入if/elif/else结构针对某一事件的多种情况进行处理,fi只出现一次,可读性也提高了.

#!/bin/bash  
 
echo "Please Input a integer(0-100): "
read score
 
if [ "$score" -lt 0 -o "$score" -gt 100 ]
then
    echo "The score what you input is not integer or the score is not in (0-100)."
elif [ "$score" -ge 90 ]
then
    echo "The grade is A!"
elif [ "$score" -ge 80 ]
then
    echo "The grade is B!"
elif [ "$score" -ge 70 ]
then
    echo "The grade is C!"
elif [ "$score" -ge 60 ]
then
    echo "The grade is D!"
else
    echo "The grade is E!"
fi

实例1: 通过单分支判断磁盘是否已满.

[root@localhost ~]# cat if.sh

#!/bin/bash

ret=$( df -h | grep "/dev/sda1" | awk '{printf $5}' | cut -d "%" -f 1 )
if [ $ret -ge 80 ]
then
        echo -e "/dev/sda1 is full !!"
fi

实例2: 通过多分支判断磁盘是否已满.

[root@localhost ~]# cat if.sh
#!/bin/bash

ret=$( df -h | grep "/dev/sda1" | awk '{printf $5}' | cut -d "%" -f 1 )
if [ $ret -ge 80 ]
then
        echo -e "/dev/sda1 is full !"
else
        echo -e "/dev/sda1 is not full !"
fi

实例3: 多分支判断小例子.

[root@localhost ~]# cat if.sh
#!/bin/bash

read -p "输入一个数字:" num

if [ "$num" -gt "100" ]
then
        echo -e "这个数大于100"
elif [ "$num" -lt "100" ]
then
        echo -e "这个数小于100"
elif [ "$num" -eq "100" ]
then
        echo -e "这个数等于100"
fi

实例4: 判断输入的年份是否是润年(润年条件:1、能被4整除,但不能被100整除的年份.2、能被100整除,又能被400整除的年份)

#!/bin/sh
  
echo "Please Input a year: "
read year
 
let "n1=$year % 4"
let "n2=$year % 100"
let "n3=$year % 400"
if [ "$n1" -ne 0 ]
then
    leap=0
elif [ "$n2" -ne 0 ]
then
    leap=1
elif [ "$n3" -ne 0 ]
then
    leap=0
else
    leap=1
fi

CASE分支结构

case结构变量值依次比较,遇到双分号则跳到esac后的语句执行,没有匹配则脚本将执行默认值*"后的命令,直到"';;"为止.case的匹配值必须是常量或正则表达式.

#!/bin/bash

read -p "输入一个符号:" temp

case $temp in

        "-print")
                echo -e "您执行了print函数"
        ;;
        "-save")
                echo -e "您执行了save函数"
        ;;
        "-help")
                echo -e "您执行了help函数"
        ;;
        *)
                echo -e "您执行了其他操作"
        ;;
esac

FOR循环结构

Shell编程中循环命令用于特定条件下决定某些语句重复执行的控制方式,有三种常用的循环语句:for、while和until.while循环和for循环属于"当型循环",而until属于"直到型循环",循环控制符:break和continue控制流程转向.

列表FOR循环: 循环打印数据分别从1遍历到5.

do和done之间的命令称为循环体,执行次数和list列表中常数或字符串的个数相同.for循环,首先将in后list列表的第一个常数或字符串赋值给循环变量,然后执行循环体,以此执行list,最后执行done命令后的命令序列.

#!/bin/bash
 
for tmp in {1..5}
do
     echo "Hello, Welcome $tmp times"
done

FOR循环累加: 通过FOR循环累加打印数据.

通过i的按步数2不断递增,计算sum值为2500.同样可以使用seq命令实现按2递增来计算1~100内的所有奇数之和,for i in $(seq 1 2 100),seq表示起始数为1,跳跃的步数为2,结束条件值为100.

#!/bin/bash
sum=0
 
for i in {1..100..2}
do
    let "sum+=i"
done

echo "sum=$sum"

FOR循环遍历目录: 通过for循环显示当前目录下所有的文件.

#!/bin/bash
 
for file in $( ls )
#for file in *
do
   echo "file: $file"
done

不带列表FOR: 由用户制定参数和参数的个数,与上述的for循环列表参数功能相同.

#!/bin/bash

echo "number of arguments is $#"
echo "What you input is: "

for argument
do
    echo "$argument"
done

C风格FOR循环: 也被称为计次循环.

#!/bin/bash
for((integer = 1; integer <= 5; integer++))
do
    echo "$integer"
done

实例1: 通过for循环打印1-10.

[root@localhost ~]# cat for.sh
#!/bin/bash

for temp in `seq 1 10`
do
        echo -e "打印数据: $temp"
done

实例2: 通过for循环计算1-100的累加和.

[root@localhost ~]# cat for.sh
#!/bin/bash

declare -i sum=0

for temp in `seq 1 100`
do
        sum=$sum+$temp
done

echo -e "从1+到100的结果是: $sum"

实例3: 从列表中选择数据.

[root@localhost ~]# cat for.sh
#!/bin/bash

for temp in 1 3 5 7 9
do
        echo -e "打印: $temp"
done

[root@localhost ~]# bash for.sh
打印: 1
打印: 3
打印: 5
打印: 7
打印: 9

实例4: 读取文件内容并打印.

[root@localhost ~]# cat for.sh
#!/bin/bash

world=`cat /etc/passwd`
for temp in $world
do
        echo -e "打印: $temp"
done

WHILE 循环

也称为前测试循环语句,重复次数是利用一个条件来控制是否继续重复执行这个语句.为了避免死循环,必须保证循环体中包含循环出口条件即表达式存在退出状态为非0的情况.

计数控制: 指定了循环的次数500,初始化计数器值为1,不断测试循环条件i是否小于等于100.在循环条件中设置了计数器加2来计算1~100内所有的奇数之和.

#!/bin/bash
 
sum=0
 
i=1
 
while(( i <= 100 ))
do
     let "sum+=i"
     let "i += 2"   
done
 
echo "sum=$sum"

结束标记控制的while循环: 设置一个特殊的数据值(结束标记)来结束while循环.

#!/bin/bash
 
echo "Please input the num(1-10) "
read num
 
while [[ "$num" != 4 ]]
do 
   if [ "$num" -lt 4 ]
   then
        echo "Too small. Try again!"
        read num
   elif [ "$num" -gt 4 ]
   then
         echo "To high. Try again" 
         read num
   else
       exit 0
    fi
done 
 
echo "Congratulation, you are right! "

标志控制的while循环: 使用用户输入的标志值来控制循环的结束(避免不知道循环结束标志的条件).

#!/bin/bash
 
echo "Please input the num "
read num
 
sum=0
i=1
 
signal=0
 
while [[ "$signal" -ne 1 ]]
do
    if [ "$i" -eq "$num" ]
    then 
       let "signal=1"
       let "sum+=i"
       echo "1+2+...+$num=$sum"
    else
       let "sum=sum+i"
       let "i++"
    fi
done

命令行控制的while循环: 使用命令行来指定输出参数和参数个数,通常与shift结合使用,shift命令使位置变量下移一位(2代替1、3代替2,并使#变量递减),当最后一个参数显示给用户,#会等于0,

#!/bin/bash

echo "number of arguments is $#"
echo "What you input is: "
 
while [[ "$*" != "" ]]
do
    echo "$1"
    shift
done

while计算: 通过while循环,计算1-100的累加和.

[root@localhost ~]# cat while.sh
#!/bin/bash

declare -i x=0
declare -i num=0

while [ "$x" -lt "100" ]
do
        x=$x+1
        num=$num+$x
done

echo -e "从1-100结果是: $num"

[root@localhost ~]# bash while.sh
从1-100结果是: 5050

UNTIL 循环

until命令和while命令类似,while能实现的脚本until同样也可以实现,但区别是until循环的退出状态是不为0,退出状态是为0(与while刚好相反),即whie循环在条件为真时继续执行循环而until则在条件为假时执行循环.

#!/bin/bash
 
i=0
 
until [[ "$i" -gt 5 ]]    #大于5
do
    let "square=i*i"
    echo "$i * $i = $square"
    let "i++"
done
#!/bin/bash
 
for (( i = 1; i <=9; i++ ))
do
    
    for (( j=1; j <= i; j++ ))
    do
        let "temp = i * j"     
        echo -n "$i*$j=$temp  "
     done 
     
     echo ""   #output newline
done
#!/bin/bash

i=0

until [[ "$i" -gt 5 ]]    #大于5

do

    let "square=i*i"

    echo "$i * $i = $square"

    let "i++"

done

跳出语句(break,continue)

break: 在for、while和until循环中break可强行退出循环,break语句仅能退出当前的循环,如果是两层循环嵌套,则需要在外层循环中使用break.

#!/bin/bash
 
sum=0
for (( i=1; i <= 100; i++))
do 
    let "sum+=i"
 
    if [ "$sum" -gt 1000 ]
    then
        echo "1+2+...+$i=$sum"
        break
    fi
done

continue: 在for、while和until中用于让脚本跳过其后面的语句,执行下一次循环.continue用于显示100内能被7整除的数.

#!/bin/bash
 
m=1
for (( i=1; i < 100; i++ ))
do
    let "temp1=i%7"         #被7整除
 
    if [ "$temp1" -ne 0 ]
    then
        continue
    fi
    
    echo -n "$i  "
    
    let "temp2=m%7"          #7个数字换一行
    
    if  [ "$temp2" -eq 0 ]
    then
        echo ""
    fi
    
    let "m++"
done

SELECT 语句

select结构从技术角度看不能算是循环结构,只是相似而已,它是bash的扩展结构用于交互式菜单显示,功能类似于case结构比case的交互性要好.

[root@localhost ~]# cat select.sh
#!/bin/bash

echo -e "请选择系统类型?"

select var in "Linux" "GNU HURD" "FreeBSD" "Other";
do
        if [ $var == "Linux" ]
        then
                echo -e "您的系统是Linux"
        elif [ $var == "FreeBSD" ]
        then
                echo -e "您的系统是FreeBSD"
        fi
break
done
[root@localhost ~]# bash select.sh
请选择系统类型?
1) Linux
2) GNU HURD
3) FreeBSD
4) Other

#? 1
您的系统是Linux
#!/bin/bash

declare -a serial
serial=(1 2 3 4)
PS3="Enter a number: "


select var in "a" "b" "c" "d"
do
    if ! echo ${serial[@]} | grep -q $REPLY
        then
                echo "please enter [1-4]."
                continue
    fi

        echo "your anwser is: $var"
        break
done

[root@localhost ~]# bash select.sh
1) a
2) b
3) c
4) d
Enter a number: 1
your anwser is: a

FUNCTION 函数

[root@localhost ~]# cat function.sh
#!/bin/bash

function my_print()
{
        echo -e "------------------------"
        echo -e "hello world"
        echo -e "------------------------"
        return "127"
}


my_print
echo $?

[root@localhost ~]# bash function.sh
------------------------
hello world
------------------------
127

标准输入输出

标准输出: echo命令直接输出指定的字符串,或在Shell环境下直接打印字符串变量中的数据.

[root@localhost ~]# echo --help

语法格式:[ echo [选项] [输出内容] ]

        -e              #支持反斜线控制的字符转换
        -n              #取消输出后行尾的换行符号

该命令的控制字符如下所示.

-----------------------------------------------------------------------------------------
        \\              #输出\本身
        \a              #输出警告音 
        \b              #退格键,也就是向左删除键 
        \c              #取消输出行末的换行符,和"-n"选项一致 
        \e              #ESCAPE键    
        \f              #换页符     
        \n              #换行符       
        \r              #回车键    
        \t              #制表符,也就是Tab键 
        \v              #垂直制表符 
        \0nnn           #按照八进制ASCII码输出字符,其中0为数字零,nnn是三位八进制数
        \xhh            #按照十六进制ASCII码表输出字符,其中hh是两位十六进制数 
-----------------------------------------------------------------------------------------

一些特殊的输出格式.

        \\              #输出\本身
        "\e[1"          #代表颜色输入开始
        "\e[0m"         #代表颜色输入结束
        \e[0m           #关闭所有属性
        \e[1m           #设置高亮度
        \e[4m           #下划线 
        \e[5m           #闪烁
        \e[7m           #反显 
        \e[8m           #消隐
        \e[nA           #光标上移n行 
        \e[nB           #光标下移n行 
        \e[nC           #光标右移n行 
        \e[nD           #光标左移n行 
        \e[y;xH         #设置光标位置 
        \e[2J           #清屏 
        \e[K            #清除从光标到行尾的内容 
        \e[s            #保存光标位置 
        \e[u            #恢复光标位置 
        \e[25l          #隐藏光标 
        \e[25h          #显示光标
-----------------------------------------------------------------------------------------
文字颜色和底纹颜色:

文字颜色代码:30=黑色,31=红色,32=绿色,33=黄色,34=蓝色,35=洋红,36=青色,37=白色,39=结束
底纹颜色代码:40=黑色,41=红色,42=绿色,43=黄色,44=蓝色,45=洋红,46=青色,47=白色,49=结束
-----------------------------------------------------------------------------------------

输出一个红字,并且黄底的"hello world"

[root@localhost ~]# echo -e "\e[31;43m hello world \e[0m"

hello world

输出\a本身

[root@localhost ~]# echo -e "\\\a"

\a

文本格式化: printf 文本格式化输出命令

[root@localhost ~]# printf --help

输出类型:
        %ns:            #输出字符串,n是数字,指代输出几个字符
        %ni:            #输出证书,n是数字,指代输出几个数字
        %m.nf:          #输出浮点数,m和n是数字,指代输出整数位和小数位

输出格式:
        \a:             #输出警告音
        \b:             #输出退格键,也就是Backspace键
        \f:             #清屏
        \n:             #换行符
        \r:             #回车,也就是Enter键
        \t:             #水平输出退格键,也就是Tab键
        \v:             #垂直输出退格键,也就是Tab键

#注意:print 和 printf 的主要区别在有 printf 是标准的格式化输出,必须手动指定换行和tab.

在进行实验之前,首先创建一个文件,来做测试用

[root@localhost ~]# cat lyshark.log

ID      NAME    AGE     Gender  Mark
1       WR      22      m       100
2       LC      26      m       90
3       LY      23      m       88
4       XDL     40      b       100

通过 printf 函数打印文本内容,如果不指定输出格式,则会把所有输出内容连在一起输出

[root@localhost ~]# printf '%s' $(cat lyshark.log)

IDNAMEAGEGenderMark1WR22m1002LC26m903LY23m884XDL40b100

通过 printf 格式化后输出一段文本

[root@localhost ~]# printf '%s\t %s\t %s\t %s\t %s\t \n' $(cat lyshark.log)

ID       NAME    AGE     Gender  Mark
1        WR      22      m       100
2        LC      26      m       90
3        LY      23      m       88
4        XDL     40      b       100

通过 printf 按照整数型和浮点型输出,则需要修改

[root@localhost ~]# printf '%i\t %s\t %8.2f\t %s\t %s\t \n' $(cat lyshark.log |grep -v ID)
1        WR         22.00        m       100
2        LC         26.00        m       90
3        LY         23.00        m       88
4        XDL        40.00        b       100

标准输入: read命令从键盘读取变量的值,通常用在shell脚本中与用户进行交互的场合.

[root@localhost ~]# read --help

语法格式:[ read [选项] [输出内容] ]

        -p              #"提示信息",指定读取值时的提示符
        -t              #指定读取值时等待的时间(秒),read等待的秒数
        -n              #指定最多能接收的字符数(达到即执行)
        -s              #隐藏输入信息

通过Read命令读取一个值

[root@localhost ~]# read temp
hello Lyshark

[root@localhost ~]# echo $temp
hello Lyshark

通过read命令读取,并输出一段提示信息,且10秒等待输入时间

[root@localhost ~]# read -p "please input name" -t 10 temp
wangrui

[root@localhost ~]# echo $temp
wangrui

变量与运算符

基本的变量声明:

变量名称可以由字母、数字和下划线组成,但是不能以数字开头,变量中间不能有空格

[root@localhost ~]# name=lyshark
[root@localhost ~]# echo $name
lyshark

[root@localhost ~]# 1name=lyshark
-bash: 1name=lyshark: command not found

在bash中变量的默认类型都是字符串型,如果要进行数值运算,则必修指定变量类型为数值型

[root@localhost ~]# x=10
[root@localhost ~]# y=20
[root@localhost ~]# sum=$x+$y

[root@localhost ~]# echo $sum
10+20

变量用等号连接值,等号左右两侧不能有空格,如出现空格汇报错误

[root@localhost ~]# name=lyshark
[root@localhost ~]# echo $name
lyshark

[root@localhost ~]# name = lyshark
-bash: name: command not found

[root@localhost ~]# temp="hello lyshark"
[root@localhost ~]# echo $temp
hello lyshark

反引号引起来的会当作命令执行并返回结果,单引号则为输出字符串,反斜线为脱意字符

[root@localhost ~]# echo `date`
Sun Sep 23 23:51:38 EDT 2018

[root@localhost ~]# echo "`date`"
Sun Sep 23 23:51:45 EDT 2018

[root@localhost ~]# echo "$(date)"
Sun Sep 23 23:51:52 EDT 2018

[root@localhost ~]# echo "\$(date)"
$(date)

变量叠加:如果需要增加变量的值,那么可以进行变量值的叠加,叠加需要使用双引号包括.

[root@localhost ~]# name=lyshark
[root@localhost ~]# temp="$name is"
[root@localhost ~]# echo $temp
lyshark is

[root@localhost ~]# end="${temp} hack"
[root@localhost ~]# echo $end
lyshark is hack

[root@localhost ~]# PATH=$PATH:/usr/bin/myapp
[root@localhost ~]# echo $PATH

参数传递:通过命令行传递参数,并打印这写参数的详细信息.

[root@localhost ~]# cat shell.sh
#!/bin/bash
echo -e "脚本名称:" $0
echo -e "参数1:" $1
echo -e "参数2: " $2
echo -e "参数3: " $3
echo -e "显示所有参数(作为一个整体显示): $*"
echo -e "显示所有参数(分页显示/分开接收): $@"
echo -e "显示参数个数: $#"
echo -e "实现计算器功能: $(($1$2$3))"

[root@localhost ~]# bash shell.sh 10 + 20
脚本名称: shell.sh
参数1: 10
参数2:  +
参数3:  20
显示所有参数(作为一个整体显示): 10 + 20
显示所有参数(分页显示/分开接收): 10 + 20
显示参数个数: 3
实现计算器功能: 30

$? 判断上一条命令执行状态,成功返回0,失败返回非0

[root@localhost ~]# ls -l
[root@localhost ~]# echo $?   ←上条执行成功返0
0

$$ 返回自身进程号

[root@localhost ~]# cat d.sh
#!/bin/bash
echo "本脚本的进程号是: $$"

[root@localhost ~]# bash d.sh
本脚本的进程号是: 1540

$! 返回最后一个后台执行的命令进程ID号

[root@localhost ~]# cat e.sh
#!/bin/bash
find / -name "*.log" &
echo "最后一个后台执行的命令进程是: $!"

[root@localhost ~]# bash e.sh
最后一个后台执行的命令进程是: 1546

SHELL实现定义数组:

声明定义数组:声明数组并定义元素,打印

[root@localhost ~]# declare -a name           #声明name为数组
[root@localhost ~]# name[0]="hello world"     #给数组name[0]赋值
[root@localhost ~]# name[1]="hello lyshark"   #给数组name[1]赋值

[root@localhost ~]# echo $name                #默认打印name[0],因为下标从0开始
hello world

[root@localhost ~]# name[2]="hello myblog"    #给数组name[2]赋值
[root@localhost ~]# echo "${name[*]}"         #打印数组全部元素
hello world hello lyshark hello myblog

[root@localhost ~]# echo ${#name[*]}          #统计数组元素个数
3
[root@localhost ~]# echo ${#name[@]}          #统计数组元素个数
3

[root@localhost ~]# declare -p name           #打印数组元素
declare -a name='([0]="hello world" [1]="hello lyshark" [2]="hello myblog")'

[root@localhost ~]# unset name                #使用结束,撤销数组
[root@localhost ~]# declare -a num=(1 2 3 4 5)   #声明num为数组,并在声明是赋值
[root@localhost ~]# echo ${#num[*]}              #正确,一共有5个元素
5

[root@localhost ~]# declare -p num               #分别打印数组中的元素
declare -a num='([0]="1" [1]="2" [2]="3" [3]="4" [4]="5")'
[root@localhost ~]# unset num                    #使用结束,撤销数组

定义初始化数组: 数组定义的多种方法(定义并初始化)

----------------------------------------------------------------------------------------------------
[方法1]
[root@localhost ~]# declare -a array_name=(Jerry Alice David LyShark)           #通过命令行一部到位
[root@localhost ~]# declare -p array_name                                       #打印查看结果
declare -a array_name='([0]="Jerry" [1]="Alice" [2]="David" [3]="LyShark")'
----------------------------------------------------------------------------------------------------
[方法2]
[root@localhost ~]# declare -a array_name                                       #先声明array_name数组
[root@localhost ~]# array_name=(Jerry Alice David LyShark)                      #紧接着打印
[root@localhost ~]# declare -p array_name
declare -a array_name='([0]="Jerry" [1]="Alice" [2]="David" [3]="LyShark")'
----------------------------------------------------------------------------------------------------
[方法3]
[root@localhost ~]# string="Jerry Alice Daid LyShark"                           #声明一个字符串
[root@localhost ~]# array_name=($string)                                        #直接转换成数组
[root@localhost ~]# declare -p array_name
declare -a array_name='([0]="Jerry" [1]="Alice" [2]="Daid" [3]="LyShark")'
----------------------------------------------------------------------------------------------------
[方法4]
[root@localhost ~]# declare -a array_name                                         #声明一个字符串
[root@localhost ~]# array_name=([0]="LyShark" [1]="Daid" [2]="Alice" [3]="Jerry") #直接转换成数组

[root@localhost ~]# echo ${array_name[@]}                                        #查询数组元素
LyShark Daid Alice Jerry

[root@localhost ~]# declare -p array_name                                        #命令行查询
declare -a array_name='([0]="LyShark" [1]="Daid" [2]="Alice" [3]="Jerry")'

[root@localhost ~]# unset array_name                                             #使用结束,撤销数组
----------------------------------------------------------------------------------------------------

求数组长度: 求数组长度,和单个数组元素长度.

[root@localhost ~]# declare -a array_name=(Jerry Alice David LyShark)            #声明测试数组
[root@localhost ~]# echo ${array_name[*]}                                        #打印全部数组元素
Jerry Alice David LyShark

[root@localhost ~]# echo ${#array_name[*]}                                       #显示全部数组数
4
[root@localhost ~]# echo ${#array_name[@]}                                       #显示全部数组数
4

[root@localhost ~]# echo ${#array_name[0]}                                       #显示name[0]字符个数
5
[root@localhost ~]# echo ${#array_name[3]}                                       #显示name[3]字符个数
7

[root@localhost ~]# unset array_name                                             #使用结束,撤销数组

声明实现算数: 通过声明实现,整数计算

[root@localhost ~]# x=10
[root@localhost ~]# y=20
[root@localhost ~]#
[root@localhost ~]# declare -i temp=$x+$y
[root@localhost ~]#
[root@localhost ~]# echo $temp
30

声明环境变量: 通过declare声明环境变量,和export是同样的效果

[root@localhost ~]# declare -x NAMES="LyShark"
[root@localhost ~]# env |grep NAMES
NAMES=LyShark

[root@localhost ~]# unset NAMES

设置只读变量: 设置只读变量,只能读取变量,无法改变其数值

[root@localhost ~]# declare -r test="hello world"
[root@localhost ~]# echo $test
hello world

[root@localhost ~]# test=1234           #这里由于设置了只读属性,所以无法修改
-bash: test: readonly variable

SHELL实现算数运算:

expr加法乘法运算:使用expr完成一次加法和乘法运算,(运算符两边必须要有空格,否则不会运算)

[root@localhost ~]# declare -i x=10
[root@localhost ~]# declare -i y=20

[root@localhost ~]# temp=$(expr $x + $y )
[root@localhost ~]# echo $temp
30
[root@localhost ~]# temp=$(expr $x * $y )
[root@localhost ~]# echo $temp
200

let加法乘法运算: 使用let完成一次加法与乘法运算,(此处并没有严格规定运算符两边的距离)

[root@localhost ~]# declare -i x=100
[root@localhost ~]# declare -i y=200

[root@localhost ~]# let temp=$x+$y
[root@localhost ~]# echo $temp
300
[root@localhost ~]# let temp=$x*$y
[root@localhost ~]# echo $temp
20000

let ++ --: let实现数值每次递增或递减1,类似C语言中的 x++

[root@localhost ~]# declare -i num=1

[root@localhost ~]# let num++
[root@localhost ~]# echo $num
2
[root@localhost ~]# let num++
[root@localhost ~]# echo $num
3
[root@localhost ~]# let num--
[root@localhost ~]# echo $num
2
[root@localhost ~]# let --num
[root@localhost ~]# echo $num
1

let x+=y: let数值每次递增或递减x,类似C语言中的 x+=y

[root@localhost ~]# declare -i num=1

[root@localhost ~]# let num+=1
[root@localhost ~]# echo $num
2
[root@localhost ~]# let num-=1
[root@localhost ~]# echo $num
1
[root@localhost ~]# let num+=10
[root@localhost ~]# echo $num
11

特殊格式运算: 使用"

[root@localhost ~]# declare -i x=100
[root@localhost ~]# declare -i y=150

[root@localhost ~]# temp=$(( $x+$y ))
[root@localhost ~]# echo $temp
250
[root@localhost ~]# temp=$[ $x*$y ]
[root@localhost ~]# echo $temp
15000

单行条件判断

按文件类型判断:

-b     #判断文件是否为块设备
-c     #判断是否为字符设备文件
-d     #判断是否为目录文件
-e     #判断文件是否存在
-f     #判断是否为普通文件
-L     #判断是否为符号链接文件
-p     #判断是否为管道文件
-s     #判断文件是否为非空(非空为真)
-S     #判断是否为套接字文件

使用-e判断文件是否存在,返回0表示存在,非零为不存在,可以检测$?来确定是否成功.

[root@localhost ~]# [ -e /etc/passwd ]
[root@localhost ~]# echo $?
0
[root@localhost ~]# [ -e /etc/lyshark ]
[root@localhost ~]# echo $?
1

除此之外,我们还可以结合之前所学的&&||实现判断后输出判断结果.

[root@localhost ~]# [ -e /etc/passwd ] && echo "ok" || echo "no"
ok
[root@localhost ~]# [ -e /etc/lyshark ] && echo "ok" || echo "no"
no

按文件权限判断:

-r     #判断文件是否具有读权限
-w     #判断文件是否拥有写权限
-x     #判断文件是否拥有执行权限
-u     #判断文件是否具有SUID权限
-g     #判断文件是否具有SGID权限
-k     #判断文件是否具有SBIT权限

判断文件是否可读,且是否可被执行,如下:

[root@localhost ~]# [ -r /bin/bash ] && echo "ok" || echo "no"
ok
[root@localhost ~]# [ -r /bin/bash ] && [ -x /bin/bash ] && echo "ok" || echo "no"
ok

两文件之间的比较:

file1 -nt file2     #判断文件1的修改时间是否比文件2新(如果新则返回真)
file1 -ot file2     #判断文件1的修改时间是否比文件2旧(如果旧则返回真)
file1 -ef file2     #判断文件1是否和文件2的I节点是否一致,两文件是否为同一个文件.

通过使用-ef判断两个文件是否拥有同一个I节点号.

[root@localhost ~]# ls -li
total 0
8409155 -rw-r--r-- 2 root root 0 Sep 25 21:39 lyshark_ln.log
8409155 -rw-r--r-- 2 root root 0 Sep 25 21:39 lyshark.log

[root@localhost ~]# [ ./lyshark.log -ef lyshark_ln.log ] && echo "ok" || echo "no"
ok

数值之间的比较:

number1 -eq number2     #判断number1是否等于number2
number1 -ne number2     #判断number1是否不等于number2
number1 -gt number2     #判断number1是否大于number2
number1 -lt number2     #判断number1是否小于number2
number1 -ge number2     #判断number1是否大于等于number2
number1 -le number2     #判断number1是否小于等于number2

比较两数字,大小关系

[root@localhost ~]# [ 23 -ge 24 ] && echo "23 >= 24 " || echo "23 <= 24"
23 <= 24

[root@localhost ~]# [ 30 -ge 24 ] && echo "30 >= 24 " || echo "30 <= 24"
30 >= 24

字符串之间比较:

-z string          #判断字符串是否为空
-n string          #判断字符串是否非空
string1 == string2 #判断string1等于string2
string1 != string2 #判断string1不等于string2

字符串之间的比较例子,字符串是否为空

[root@localhost ~]# name=LyShark
[root@localhost ~]# age=""
[root@localhost ~]# unset sex

[root@localhost ~]# [ -z "$name" ] && echo "字符串空" || echo "不为空"
不为空
[root@localhost ~]# [ -z "$age" ] && echo "字符串空" || echo "不为空"
字符串空
[root@localhost ~]# [ -z "$sex" ] && echo "字符串空" || echo "不为空"
字符串空

两个字符串相等比较

[root@localhost ~]# x=100
[root@localhost ~]# y=200
[root@localhost ~]# z=100

[root@localhost ~]# [ "x" == "y" ] && echo "yes" || echo "no"
no
[root@localhost ~]# [ "$x" == "$y" ] && echo "yes" || echo "no"
no
[root@localhost ~]# [ "$x" == "$z" ] && echo "yes" || echo "no"
yes

多重条件判断:

逻辑与:判断变量x不为空,并且x大于20

[root@localhost ~]# declare -i x=10
[root@localhost ~]# [ -n "$x" -a "$x" -gt 20 ] && echo "yes" || echo "no"
no
[root@localhost ~]# declare -i x=30
[root@localhost ~]# [ -n "$x" -a "$x" -gt 20 ] && echo "yes" || echo "no"
yes

逻辑非:判断x变量不为空,或!取反

[root@localhost ~]# declare -i x=30

[root@localhost ~]# [ -n "$x" ] && echo "yes" || echo "no"
yes
[root@localhost ~]# [ ! -n "$x" ] && echo "yes" || echo "no"
no

IF条件判断

IF结构例子: 一个就简单的单分支结构.测试条件后如果没有";"则then语句要换行.

#!/bin/sh
echo "Please input a Number:"
read Num
 
if [ "$Num" -lt 15 ]
then
    echo "$Num <= 15"
fi

IF/IF-ELSE结构: 一个双分支结构,如下执行删除文件,成功返回then是否返回else.

#!/bin/sh
 
echo "Please input the file which you want to delete:"
read file
 
if rm -f "$file"
then
  echo "Delete the file $file  sucessfully!"
else
  echo "Delete the file $file failed!"
fi

IF/ELSE嵌套: 可同时判断三个或三个以上条件,但要注意if与else配对关系,else语句总是与它上面最近的未配对的if配对.

#!/bin/bash
#提示用户输入分数(0-100)
echo "Please Input a integer(0-100): "
read score

#判断学生的分数类别
if [ "$score" -lt 0 -o "$score" -gt 100 ]
then
   echo "The score what you input is not integer or the score is not in (0-100)."
else
     if [ "$score" -ge 90 ]
     then
         echo "The grade is A!"
     else
          if [ "$score" -ge 80 ]
          then
              echo "The grade is B!"
          else
               if [ "$score" -ge 70 ]
              then
                   echo "The grade is C!"
              else
                   if [ "$score" -ge 60 ]
                   then
                        echo "The grade is D!"
                   else
                        echo "The grade is E!"
                   fi
              fi
         fi
    fi
fi

IF/ELIF/ELSE结构: if/else嵌套在编程中很容易漏掉then或fi产生错误,而且可读性很差,因此引入if/elif/else结构针对某一事件的多种情况进行处理,fi只出现一次,可读性也提高了.

#!/bin/bash  
 
echo "Please Input a integer(0-100): "
read score
 
if [ "$score" -lt 0 -o "$score" -gt 100 ]
then
    echo "The score what you input is not integer or the score is not in (0-100)."
elif [ "$score" -ge 90 ]
then
    echo "The grade is A!"
elif [ "$score" -ge 80 ]
then
    echo "The grade is B!"
elif [ "$score" -ge 70 ]
then
    echo "The grade is C!"
elif [ "$score" -ge 60 ]
then
    echo "The grade is D!"
else
    echo "The grade is E!"
fi

分支语句实现

#!/bin/bash

read -p "输入一个符号:" temp

case $temp in

        "-print")
                echo -e "您执行了print函数"
        ;;
        "-save")
                echo -e "您执行了save函数"
        ;;
        "-help")
                echo -e "您执行了help函数"
        ;;
        *)
                echo -e "您执行了其他操作"
        ;;
esac
[root@localhost ~]# cat select.sh
#!/bin/bash

echo -e "请选择系统类型?"

select var in "Linux" "GNU HURD" "FreeBSD" "Other";
do
        if [ $var == "Linux" ]
        then
                echo -e "您的系统是Linux"
        elif [ $var == "FreeBSD" ]
        then
                echo -e "您的系统是FreeBSD"
        fi
break
done
[root@localhost ~]# bash select.sh
请选择系统类型?
1) Linux
2) GNU HURD
3) FreeBSD
4) Other

#? 1
您的系统是Linux
#!/bin/bash

declare -a serial
serial=(1 2 3 4)
PS3="Enter a number: "


select var in "a" "b" "c" "d"
do
    if ! echo ${serial[@]} | grep -q $REPLY
        then
                echo "please enter [1-4]."
                continue
    fi

        echo "your anwser is: $var"
        break
done

[root@localhost ~]# bash select.sh
1) a
2) b
3) c
4) d
Enter a number: 1
your anwser is: a

FOR循环结构

列表FOR循环: 循环打印数据分别从1遍历到5.

do和done之间的命令称为循环体,执行次数和list列表中常数或字符串的个数相同.for循环,首先将in后list列表的第一个常数或字符串赋值给循环变量,然后执行循环体,以此执行list,最后执行done命令后的命令序列.

#!/bin/bash
 
for tmp in {1..5}
do
     echo "Hello, Welcome $tmp times"
done

FOR循环累加: 通过FOR循环累加打印数据.

通过i的按步数2不断递增,计算sum值为2500.同样可以使用seq命令实现按2递增来计算1~100内的所有奇数之和,for i in $(seq 1 2 100),seq表示起始数为1,跳跃的步数为2,结束条件值为100.

#!/bin/bash
sum=0
 
for i in {1..100..2}
do
    let "sum+=i"
done

echo "sum=$sum"

FOR循环遍历目录: 通过for循环显示当前目录下所有的文件.

#!/bin/bash
 
for file in $( ls )
#for file in *
do
   echo "file: $file"
done

不带列表FOR: 由用户制定参数和参数的个数,与上述的for循环列表参数功能相同.

#!/bin/bash

echo "number of arguments is $#"
echo "What you input is: "

for argument
do
    echo "$argument"
done

C风格FOR循环: 也被称为计次循环.

#!/bin/bash
for((integer = 1; integer <= 5; integer++))
do
    echo "$integer"
done

实例1: 通过for循环打印1-10.

[root@localhost ~]# cat for.sh
#!/bin/bash

for temp in `seq 1 10`
do
        echo -e "打印数据: $temp"
done

实例2: 通过for循环计算1-100的累加和.

[root@localhost ~]# cat for.sh
#!/bin/bash

declare -i sum=0

for temp in `seq 1 100`
do
        sum=$sum+$temp
done

echo -e "从1+到100的结果是: $sum"

实例3: 从列表中选择数据.

[root@localhost ~]# cat for.sh
#!/bin/bash

for temp in 1 3 5 7 9
do
        echo -e "打印: $temp"
done

[root@localhost ~]# bash for.sh
打印: 1
打印: 3
打印: 5
打印: 7
打印: 9

实例4: 读取文件内容并打印.

[root@localhost ~]# cat for.sh
#!/bin/bash

world=`cat /etc/passwd`
for temp in $world
do
        echo -e "打印: $temp"
done

WHILE 循环

计数控制: 指定了循环的次数500,初始化计数器值为1,不断测试循环条件i是否小于等于100.在循环条件中设置了计数器加2来计算1~100内所有的奇数之和.

#!/bin/bash
 
sum=0
 
i=1
 
while(( i <= 100 ))
do
     let "sum+=i"
     let "i += 2"   
done
 
echo "sum=$sum"

结束标记控制的while循环: 设置一个特殊的数据值(结束标记)来结束while循环.

#!/bin/bash
 
echo "Please input the num(1-10) "
read num
 
while [[ "$num" != 4 ]]
do 
   if [ "$num" -lt 4 ]
   then
        echo "Too small. Try again!"
        read num
   elif [ "$num" -gt 4 ]
   then
         echo "To high. Try again" 
         read num
   else
       exit 0
    fi
done 
 
echo "Congratulation, you are right! "

标志控制的while循环: 使用用户输入的标志值来控制循环的结束(避免不知道循环结束标志的条件).

#!/bin/bash
 
echo "Please input the num "
read num
 
sum=0
i=1
 
signal=0
 
while [[ "$signal" -ne 1 ]]
do
    if [ "$i" -eq "$num" ]
    then 
       let "signal=1"
       let "sum+=i"
       echo "1+2+...+$num=$sum"
    else
       let "sum=sum+i"
       let "i++"
    fi
done

命令行控制的while循环: 使用命令行来指定输出参数和参数个数,通常与shift结合使用,shift命令使位置变量下移一位(2代替1、3代替2,并使#变量递减),当最后一个参数显示给用户,#会等于0,

#!/bin/bash

echo "number of arguments is $#"
echo "What you input is: "
 
while [[ "$*" != "" ]]
do
    echo "$1"
    shift
done

while计算: 通过while循环,计算1-100的累加和.

[root@localhost ~]# cat while.sh
#!/bin/bash

declare -i x=0
declare -i num=0

while [ "$x" -lt "100" ]
do
        x=$x+1
        num=$num+$x
done

echo -e "从1-100结果是: $num"

[root@localhost ~]# bash while.sh
从1-100结果是: 5050

Break跳出循环

break: 在for、while和until循环中break可强行退出循环,break语句仅能退出当前的循环,如果是两层循环嵌套,则需要在外层循环中使用break.

#!/bin/bash
 
sum=0
for (( i=1; i <= 100; i++))
do 
    let "sum+=i"
 
    if [ "$sum" -gt 1000 ]
    then
        echo "1+2+...+$i=$sum"
        break
    fi
done

continue: 在for、while和until中用于让脚本跳过其后面的语句,执行下一次循环.continue用于显示100内能被7整除的数.

#!/bin/bash
 
m=1
for (( i=1; i < 100; i++ ))
do
    let "temp1=i%7"         #被7整除
 
    if [ "$temp1" -ne 0 ]
    then
        continue
    fi
    
    echo -n "$i  "
    
    let "temp2=m%7"          #7个数字换一行
    
    if  [ "$temp2" -eq 0 ]
    then
        echo ""
    fi
    
    let "m++"
done

FUNCTION函数

实现一个无参数传递的函数.

[root@localhost ~]# cat function.sh
#!/bin/bash

function my_print()
{
        echo -e "------------------------"
        echo -e "hello world"
        echo -e "------------------------"
        return "127"
}

my_print
echo $?

[root@localhost ~]# bash function.sh
------------------------
hello world
------------------------
127

简单实现参数的传递.

[root@localhost ~]# cat function.sh 
#!/bin/bash

function my_print()
{
    echo "参数1:" $1
    echo "参数2:" $2
    echo "参数3:" $3
}

test="1 2 3"
my_print ${test}

[root@localhost ~]# bash function.sh
参数1: 1
参数2: 2
参数3: 3