shell脚本编程总结

shell脚本是什么?

 (1)命令的堆积;        

 (2)程序逻辑;    

如何写shell脚本:

 脚本文件的第一行,顶格:给出shebang,解释其路径,用于指明解释执行当前脚本的解释器程序文件。

 常见的解释器:

    #!/bin/bash

    #!/usr/bin/python    

    #!/usr/bin/perl

运行脚本:

    (1)赋予执行权限,并直接运行此程序文件;

    (2)直接运行解释器,将脚本以命令行参数传递给解释器程序:

        bash /PATH/TO/SCRIPT_FILE

    注:脚本中的空白行会被解释器忽略。

  • 脚本中,除了shebang,余下所有以#开头的行,都会被视作注释行而被忽略;此即为解释行。

  • shell脚本的运行是通过一个子shell进程实现的。


bash的算术运算及条件测试

(1)实现算术运算

let var=算术表达式

var=$[算术表达式]

var=$((算术表达式))

var=$(expr arg1 arg2 arg3)

例:mul=$(expr $num1 \* $num2)

注:称号在某些场景中需要转义

(2)增强型赋值符号

     +=, -=, *=, /=, %=

     let varOPERvalue

    例如:let count+=1

自增,自减:

    let var+=1

    let var++

    let var-=1

    let var--

(3)条件测试:

  判断某需求是否满足,需要由测试机制来实现;

 如何编写测试表达式以实现所需的测试:

    (1)执行命令;并利用命令状态返回值来判断。

        0:成功  1-255:失败

    (2)测试表达式:[ EXPRESSION ]

            [[ EXPRESSION ]]

        注:EXPRESSION前后必须有空白字符;

        ①数值测试:

            -eq:是否等于;

            -ne:是否不等于;

            -gt:是否大于;

            -lt:是否小于;

            -le:是否小于等于;

        ②字符串测试:

             ==:是否等于;

            >:是否大于;

            <:是否小于;

            !=:是否不等于;

            =~:左侧字符串是否能够被右侧的PATTERN所匹配;

                注:此表达式一般用于[[ ]]中;

            -z "STRING":测试字符串是否为空,空则为真,不空则为假;

            -n "STRING":测试字符串是否不空,不空则为真,空则为假;

                     注:用于字符串比较时用到的操作数东应该使用引号;

           ③自定义退出状态码:
            exit [n]:自定义推出状态码;

      注:脚本中一旦遇到exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字;如果未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码;

    

        ④文件测试:

           存在性测试:

            -a FILE

            -e FILE:文件存在性测试,存在为真,否则为假;

           存在性及类别测试:

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

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

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

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

            -h FILE 或 -L FILE:存在且为符号链接文件;

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

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

           文件权限测试:

            -r FILE:是否存在且为可读;

            -w FILE:是否存在且为可写;

            -x FILE:是否存在且为可执行

           文件特殊权限测试:

            -g FILE:测试是否存在且拥有sgid权限;

            -u FILE:是否存在且拥有suid权限;

            -k FILE:是否存在且拥有sticky权限;

           文件大小测试:

            -s FILE:是否存在且非空

           文件是否打开:

            -t fd:fd表示文件描述符是否已经打开且与某终端相关

            -N FILE:文件上一次读取之后是否被修改过 

                -O FILE: 当前有效用户是否为文件属主

                -G FILE: 当前有效用户是否为文件属组

              双目测试:

            FILE1 -ef FILE2:两个文件是否指向同一个设备上相同的inode

            FILE1 -nt FILE2:FILE1是否新于FILE2

            FILE1 -ot FILE2:FILE1是否旧于FILE2

         组合测试条件:

            逻辑运算

                第一种方式

                    COMMAND1 && COMMAND2

                    COMMAND1 || COMMAND2

                    !COMMAND

                第二种方式

                    EXPRESSION1 -a EXPRESSION2

                    EXPRESSION1 -o EXPRESSION2

                    !EXPRESSION

(4)shell脚本变量:  

   1)变量赋值:name='value'

  其中的value可以为

a.可以使直接字串:name="username"

b.变量引用:name="$username"

c.命令引用:name=`COMMAND`(单反引号)

   name=$(COMMAND)

   2)变量引用:${name},$name

" ": 弱引用,其中的变量引用会被替代为变量值

' ': 强引用,其中的变量引用不会被替换为变量值,而保持原字符串

   3)位置变量

在脚本代码中调用通过命令行传递给脚本的参数

$1,$2,...:对应调用第1,第2等参数

     $#:它可抓出positional parameter的数量,即在脚本后面的参数有几个

$0:脚本本身的名字

$@:传给脚本的所有参数

$*:是以一个单字符串显示里所有向脚本传递的参数,与位置参数不同,参数可超过9个

$$:是脚本运行的当前进程的ID号

$?:显示最后命令的退出状态

bash脚本编程:

(1)用户交互

    read [option]... [name]...

      -p 'PROMPT'

      -t TIMEOUT

   bash -n /PATH/TO/SOME_SCRIPT

    检查脚本中有无语法错误;

   bash -x /PATH/TO/SOME_SCRIPT

    分步执行脚本内容;

(2)脚本编程结构:

    ①条件测试

      运行命令或[[ EXPRESSION ]]

        执行状态返回值

      if

      case

    ②循环执行

      将某代码段重复运行多次

     必须有进入条件和退出条件

       for ,while, until

      ③函数:结构化编程及代码复用

       function

(3)if语句

    单分支:

      if CONDITION; then

        ...

      fi

    双分支:

      if CONDITION; then

        ...

      else

        ...

      fi

    多分支:

      if CONDITION1;then

        ...

      elif CONDITION2; then

        ...

      elif CONDITION3; then

        ...

      ....

      else

        ...

      fi

       逐条件进行判断,第一次判断为“真”时,执行其分支,而后结束;

    if语句可嵌套;

示例1:提示用户键入文件路径,用脚本判断文件类型:

#!/bin/bash

#judge type about a file what user give

read -p "Please Enter a filename:" filename

if [ -z "$filename" ]; then
        echo "Usage: Enter a file path"
        exit 2
fi

if [ ! -e $filename ]; then
        echo "Usage: No such file, check it and enter again"
        exit 3
fi

if [ -f $filename ]; then
        echo "It's a common file"
elif [ -d $filename ]; then
        echo "It's a directory"
elif [ -L $filename ]; then
        echo "It's a symbolic file"
else
        echo "Other type"
fi

[root@localhost ~]# bash file.sh 
Please Enter a filename:file.sh
It's a common file

示例2:传递给脚本文件名,判断两个文件的行数多少: 

#!/bin/bash
#compare two files lines

if ! [ -e $1 ] || ! [ -e $2 ]; then
        echo "Please enter a exist filename"
        exit 2
fi


A=` wc $1  &> /dev/null `
B=` wc $2  &> /dev/null `

if [ $A -gt $B ]; then
  echo "$1 has more lines" 
else
  echo "$2 has more lines"
fi

[root@localhost ~]# bash first.sh /etc/fstab /etc/passwd
/etc/fstab has more lines

       

 (4)for循环语句:

    ①for VARAIBLE in LIST; do

        循环体

     done

       进入条件:只要列表有元素,即可进入循环;

       退出条件:列表中的元素遍历完成;

    ②列表生成方式:

      直接给出

      整数列表

        {start..end}

          ${seq [start [incremtal]] last} 

      返回列表的命令

         $(COMMAND)

      glob

        /etc/rc.d/rc1.d/k*(遍历该路径下的以k开头的所有文件)

      变量引用

         $@,$*

示例1:添加10个用户,user1-user10;密码同用户名;

#!/bin/bash
# add 10 user, username:user1~user10,password:user1~user10;


for i in {1..10}; do
     if id user$i &> /dev/null; then
        echo "user$i is exist"
     else
        useradd user$i && echo "user$i" | passwd --stdin user$i &> /dev/null
        echo " add user$i is successed"
     fi
done

示例2:判断某路径下所有文件的类型  

#!/bin/bash 
#judge type of file in some  path

for file in $(ls /etc); do
     if [ -f /etc/$file ]; then
        echo "$file is common file"
     elif [ -d /etc/$file ]; then
        echo "$file is directory file"
     elif [ -L /etc/$file ]; then
        echo "$file is symbolic file"
     else
        echo "$file is oth type"
     fi
done

示例3:打印九九乘法表:

#!/bin/bash
#9X9 multiplication

for i in {1..9}; do
        for j in $(seq 1 $i); do
         echo -e -n "${i}x${j}=$[$i*$j]\t"
         done
         echo
done

(4)for循环的特殊用法:

    for ((控制变量初始化;条件判断表达式;控制变量的修正表达式));do

        循环体

    done 

   控制变量初始化:尽在运行到循环代码段时执行一次

   控制变量的修正表达式:每轮循环结束后会先对控制变量进行修正运算

示例:九九乘法表

for ((i=1; i<=9; i++)); do
         for ((j=1; j<=$i; j++)); do
         echo -e -n "${i}x${j}=$[$i*$j]\t"
         done
         echo
done


(5)while循环:

    ①while CONDITION; do

      循环体

     done

     CONDITION:循环控制条件,进入循环前,先做一次判断;每一次循环之后会再次做判断,条件为“true”,则执行一次循环;直到条件测试状态为“false”终止循环;


示例:求100以内的偶数和以及奇数和

#!/bin/bash
#even number sum and odd number sum

declare -i i=0
declare -i evensum=0
declare -i oddsum=0
while [ $i -le 100 ]; do
        if [ $[$i%2] -eq 0 ]; then
         evensum=$[$evensum+$i]
        else
         oddsum=$[$oddsum+$i]
        fi
let i++
done
echo "even number sum is $evensum"
echo "odd number sum is $oddsum"

[root@localhost ~]# bash  sum.sh
even number sum is 2550
odd number sum is 2500

    ②while循环的特殊用法(遍历文件中的每一行)

        while read line; do

           循环体

       done < /PATH/FROM/SOMEFILE

     依次读取/PATH/FROM/SOMEFILE文件中的每一行,且将行赋值给变量line;

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

#!/bin/bash
#ouput even number id

while read line; do
        if [ $[`echo $line | cut -d: -f3` % 2] -eq 0 ]; then
          echo -e -n "username: `echo $line | cut -d: -f1`\t"
          echo "uid: `echo $line | cut -d: -f3`"
        fi
done < /etc/passwd

(6)until循环

    until CONDITION; do

       循环体

    done

   进入条件:false

   退出条件:true

(7)循环控制语句(用于循环体中)

   1)continue [N]

    提前结束由内而外的第N层的本轮循环,并且直接进入下一轮循环判断

       while CONDITION1; do

         ...

        if CONDITION2; then

           continue

        fi

        ...

       done

   2)break [N]:提前结束循环

示例:只求100内的偶数和

#!/bin/bash
#output even numner sum

declare -i i=0
declare -i sum=0
until [ $i -gt 100 ]; do
        let i++
        if [ $[$i%2] -eq 1 ]; then
           continue
        fi
        let sum+=$i
done
echo "even number sum is $sum"


(8)条件判断:case语句

    case 变量引用 in

    PAT1)

      分支1

      ; ;

    PAT2)

      分支2

      ; ;

     PAT3)

      分支3

      ; ;

      ...

    *)

      默认分支

      ; ;

    esac

case支持glob风格的通配符

   *:任意长度任意字符

   ?:任意单个字符

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

   a|b:a或b

示例:提示用户键入字符,并判断类型:

#!/bin/bash
#input a key,judge type

read -p "Please enter a key:" key

case $key in
[[:lower:]] )
echo "Lowercase letter"
;;
[[:upper:]] )
echo "Uppercase letter"
;;
[0-9] )
echo "Digit"
;;
*)
echo "others"
esac

[root@localhost ~]# bash case.sh
Please enter a key:a
Lowercase letter

(9)函数:function

    对于过程式编程,可以实习代码重写

     模块化编程

     结构化编程

把一段独立功能的代码当做一个整体,并为之起个名字;命名的代码段,纪委函数。

注:定义函数的代码段不会自动执行,在调用时执行;所谓调用函数,在代码中给定函数名即可;函数名出现的任何位置,在代码执行时,都会被自动被替换为函数代码。

  1)语法一

    function f_name{

      函数体

    }

    语法二

    f_name{

      函数体

    }

  2)调用:函数只有被调用才会执行

    调用方法:给定函数名即可

           函数名出现的地方,会被自动替换为函数代码

    函数生命周期:被调用时创建

  3)函数返回值

    函数执行结果返回值

        使用echo或print命令进行输出

        函数体中调用命令的执行结果

    函数的退出状态码

        默认取决于函数体中执行的最后一条命令的退出状态码

        自定义退出状态码:return

  4)函数可以接受参数

    传递参数给函数:调用函数时,在函数名后面以空白分隔给定参数列表即可

    在函数体中,可使用$1,$2...调用这些参数,还可以使用$@,$*,$#等特殊变量

  5)函数递归:

    函数直接或间接调用自身

示例:简单使用函数:

#!/bin/bash
#show function

print(){
 echo "your choice is $1"
}
read -p "Please enter one、two、three or four:"  num
case $num in
one)
   print 1
;;
two)
   print 2
;;
three)
   print 3
;;
four)
   print 4
;;
esac
[root@localhost ~]# bash function.sh 
Please enter one、two、three or four:one
your choice is 1
[root@localhost ~]# bash function.sh 
Please enter one、two、three or four:two
your choice is 2

(10)数组

 基本概念:

    变量:存储单个元素的内存空间

    数组:存储多个元素的连续的内存空间

       数组名

       索引:编号从0开始,是数值索引

    引用数组中的元素:${ARRAY_NAME[INDEX]}

 1)声明数组

     declare -a ARRAY_NAME

     declare -A ARRAY_NAME 关联数组

   注:索引也可支持使用自定义的格式,而不仅仅是数值格式

    bash数组支持稀疏格式

 2)数组元素的赋值

    ①一次只赋值一个元素

       ARRAY_NAME[INDEX]=VALUE

         例: weekday[0]="Sunday"

            weekday[0]="Thursday"

    ②一次赋值全部元素

      ARRAY_NAME=("VAL1" "VAL2" "VAL3"...)

    ③只赋值特定元素

      ARRAY_NAME=([0]="VAL1" [3]="VAL2")

    ④read -a ARRAY

 3)引用数组元素

    ${ARRAY_NAME [INDEX]}

     注:引用时,只给数组名,表示引用下标为0的元素;

    ${ARRAY_NAME[*]}:引用数组中的所有元素;

    ${ARRAY_NAME[@]}

 4)数组的长度:(数组中元素的个数)

    ${#ARRAY_NAME[*]},${#ARRAY_NAME[@]}


引用数组中的元素

    数组切片;${ARRAY[@]:offset:number}

        offset:要跳过的元素的个数

        number:要去除的元素个数,取偏移量之后的所有元素

        ${ARRAY[@]:offset}

向数组中追加元素

    ARRAY[${#ARRAY[*]}]

删除数组中的某元素

    unset ARRAY[INDEX]

关联数组

    declare -A ARRAY_NAME

    ARRAY_NAME=([index_name1]='val1'[index_name]='val2'...)



示例:使用数组

#!/bin/bash
#

declare -a arry
arry=(tom jery joe)
for ((i=0; i<=${#arry[@]}; i++)) do
echo  "${arry[$i]}"
done
echo "array length: ${#arry[@]}"
[root@localhost ~]# bash array.sh
tom
jery
joe

array length: 3

    

示例:生成10个随机数并从小到大排序

#!/bin/bash
#
declare -a num
for i in {0..9}; do
        num[$i]=$RANDOM

done
echo ${num[*]}
for((i=0;i<=9;i++)); do
        for((j=$[${i}+1];j<10;j++)); do
                if [ ${num[$i]} -gt ${num[$j]} ]; then
                a=${num[$i]}
                num[$i]=${num[$j]}
                num[$j]=$a
                fi
        done
done
echo ${num[*]}

[root@localhost ~]# bash paixu.sh 
6670 15462 25364 28280 14556 4667 24577 7020 22897 5217
4667 5217 6670 7020 14556 15462 22897 24577 25364 28280





你可能感兴趣的:(shell,脚本,bash)