shell脚本进阶

一、流程控制

过程式编程语言:顺序执行、选择执行、循环执行

1.1 条件语句

1.1.1 if条件选择语句

 选择执行: (注意: if 语句可嵌套)

v  单分支:if  判断条件;then

          条件为真的分支代码

            fi

v 双分支:if  判断条件; then

           条件为真的分支代码

           else

           条件为假的分支代码

           fi

v 多分支: if  判断 条件 1 ; then

                if-true

           elif  判断 条件 2 ; then

                if-ture

           elif  判断 条件 3 ; then

                if-ture

                …

               else

               all-false

           fi

v  逐条 件进行判断,第一次遇为“真”条件时,执行其分支,而后结束整个if 语句

1.1.2 if 实例

v       根据命令的退出状态来执行命令

if ping -c1 -W2 station1 &> /dev/null; then

echo 'Station1 is UP'

elif grep "station1" ~/maintenance.txt &> /dev/null;then

echo 'Station1 is undergoing maintenance

else

echo 'Station1 is unexpectedly DOWN!'

exit 1

fi

1.1.3 case条件判断

适合于离散值的匹配;如:value 135 cmd1value 246 cmd2…   read p please input a number  value

case  变量引用($)  in                如:case  $value  in  

PAT1)                 1|3|5)

分支1                 echo a

        ;;                  ;;

PAT2)                 2|4|6)

分支2                 echo b

;;                  ;;

*)                  *)

默认分支                 echo d

;;                  ;;

esac                  esac

case 支持glob 风格的通配符:

*:  任意长度任意字符  ;  ?:  任意单个字符  ;  [] :指定范围内的任意单个字符  ;  a|b: a b

1.1.4 练习

1 、编写脚本/root/bin/createuser.sh ,实现如下功能:使用一个用户名做为参数,如果指定参数的用户存在,就显示其存在,否则添加之;显示添加的用户的id 号等信息

   read -p "please give me a user:" u

   useradd $u &> /dev/null

  if [ $? -eq 0 ];then

      echo "user $u is already added sucessfully"  ;  id $u

  else

      echo "user is exit"

   fi

2、编写脚本/root/bin/yesorno.sh ,提示用户输入yes no,并判断用户输入的是yes 还是no, 或是其它信息

   read -p "please input yes or no:" i

   case $i in

  [yY]|[yY][eE][sS])

      echo yes

      ;;

  [nN]|[nN][oO])

      echo no

      ;;

  *)

      echo your answer is wrong

      ;;

  esac

3 、编写脚本/root/bin/filetype.sh, 判断用户输入文件路径,显示其文件类型(普通,目录,链接,其它文件类型)

  for file in $D

  do

      if [ -L $file ];then

          echo -e "\033[32m$file\033[0m is a link file"

      elif [ ! -L $file -a -d $file ];then

          echo -e "\033[34m$file\033[0m is a directory"

      elif [ ! -L $file -a -f $file ];then

          echo -e "\033[37m$file\033[0m is a normal file"

      elif [ ! -L $file -a -c $file ];then

          echo -e "\033[47;33m$file\033[0m is a character device file"

      elif [ ! -L $file -a -b $file ];then

          echo -e "\033[33m$file\033[0m is a block device file"

      elif [ ! -L $file -a -p $file ];then

          echo -e "\033[35m$file\033[0m is a pipe file"

      elif [ ! -L $file -a -s $file ];then

          echo -e "\033[31m$file\033[0m is a socket file"

      else

          echo -e "\033[47;30mIt's not a directory\033[0m"

      fi

  done

4 、编写脚本/root/bin/checkint.sh, 判断用户输入的参数是否为正整数。

  read -p "please input a number:" n

  [ -z $n ] && echo "please input a int" && exit 123

  num=`echo $n | egrep -o "\-?[[:digit:]]+"` #\-? 判断是否出现\为转义符号

 if [ "$num" == "$n" ];then

      if [ "$num" -le 0 ];then

          echo your input is a negative integer   #负整数

      elif [ "$num" == 0 ];then

          echo your input is zero                 #0

      else

          echo your input is positive integer     #正整数

      fi

 else

      echo your input is not a integer            #不是一个整数

 fi

1.2 循环语句

循环执行:将某代码段重复运行多次

重复运行多少次:循环次数事先已知;循环次数事先未知

有进入条件和退出条件:for, while, until

1.2.1 for循环

for  变量名  in 列表;do

循环体

done

执行机制

依次将列表中的元素赋值给“变量名”;  每次赋值后即执行一次循环体;  直到列表中的元素耗尽,循环结束

列表生成方式:

(1)  直接给出列表

(2)  整数列表:(a) {start..end}  (b) $(seq [start [step]] end)  如:seq 1 10 表示1-10列表竖行显示

             (适用于数值运算)        seq 0 2 10 表示显示0 2 4 6 8 102为步长

(3)  返回列表的命令:$(COMMAND)

(4)  使用glob, 如:*.sh

(5)  变量引用;$@, $*

1.2.2 for练习

v 1、判断/var/ 目录下所有文件的类型

v 2、 添加10个用户user1-user10 ,密码为8 位随机字符

  read -p "how many users do you want to add: " N

  #1-$N中所有数据添加

 for j in $(seq 1 $N)

  do

  #添加用户,但不显示过程

      useradd user$j &> /dev/null

  #取随机8位数,作为用户的密码

      cat /dev/urandom |tr -dc "[^[:alnum:]]"|head -c 8|passwd user$j &> /dev/null

  done

v 3/etc/rc.d/rc3.d 目录下分别有多个以K 开头和以S 开头的文件 ;分别 读取每个文件,以K 开头的文件输出为文件加stop,以S 开头的文件输出为文件名加start

K34filename stop”  “S66filename start

  for j in $(ls -1 /etc/rc.d/rc3.d/);do

      if [[ $j =~ ^S.* ]];then           FileC1=`echo $j | cut -c1`

          echo -e "$j\tstart"   case $FileC1 in

      elif [[ $j =~ ^K.* ]];then   K)

          echo -e "$j\tstop"    echo -e $j\tstop

      else         ;;

          echo "it's not service file"

      fi

   done

v 4、编写脚本 ,提示输入正整数n 的值,计算1+2++n 的总和

  read -p "give me a number:" n

  sum=0

  for((i=0;i<=$n;i++))

  do

     sum=$[$sum+$i]

  done

  echo $sum

v 5、编写脚本 ,提示请输入网络地址,如192.168.0.0 ,判断输入的网段中主机在线状态

   read -p "Please give a network:" ip

   pingip=`echo "$ip"| cut -d. -f1-3`.

   if echo "$ip" | egrep'\<(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}

              ([0-9]|[1-9 ][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\>' &>/dev/null;then

  #test only 15 host

      for i in `seq 240 255`

      do

          if ping -c1 -W1 $pingip$i &> /dev/null;then

              echo "$pingip$i is on work"

          else

              echo "$pingip$i is out of work"

          fi

      done

  else

      echo "$ip is useless"

  fi

v 6、 打印 九九 乘法表

 #方法1for循环 if语句             #方法2for循环 for语句

 for i in {1..9}                 for i in {0..9}

 do                   do

     for j in {1..9}                 for j in $(seq $i)

     do                   do

     if [ $j -le $i ];then                echo -ne $i*$j=$[$i*$j]\t

         echo -ne "$i*$j=$[i*j]\t"   #不换行打印i*j=*TAB        done

     else                  echo

         break                   #结束这一层的循环;一般在if中使用   done

     fi

     done

     echo                        #显示打印内容

 done

 

v 7、在/testdir 目录下创建10html文件, 文件名格式为数字N(从1 10 )加随机8个字母,如1AbCdeFgH.html

  if [ -a /testdir ];then

      echo "/testdir is alreadly existing"

  else

      mkdir /testdir

  fi

  for i in $(seq 10);do

      for j in `cat /dev/urandom|tr -dc "a-zA-Z"|head -c 8`

      do

          cd /testdir

          touch $i$j.html

      done

      echo "$i$j.html has been created"

  done

1.2.3 while 循环

while CONDITION; do

循环体

done

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

v  因此:CONDTION 一般应该有循环控制变量;而此变量的值会在循环体不断地被修正

v  进入条件:CONDITION true

v  退出条件:CONDITION false

1.2.4 while练习

1、编写脚本,求100 以内 所有正奇数之和.

  sum=0;i=1

  while ((i<$n))

  do

      sum=$[$sum+$i]  

      let i=$[$i+2]

  done

  echo "sum=$sum"

v  2、编写脚本, 提示请输入网络地址,如192.168.0.0 ,判断输入的网段中主机在线状态 ,并统计在线主机和离线主机各多少。

  read -p "Please give a network:" ip

  pingip=`echo "$ip"| cut -d. -f1-3`.

  if echo "$ip" | egrep '\<(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9

  ][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\>' &>/dev/null;then

  #test only 15 host

      for i in `seq 240 255`

      do

          if ping -c1 -W1 $pingip$i &> /dev/null;then

              echo "$pingip$i is on work"

          else

              echo "$pingip$i is out of work"

          fi

      done

  else

      echo "$ip is useless"

  fi     unset ip pingip i

v  3、编写脚本,打印 九九乘法表

  #方法3while循环             #方法3until循环

  i=1                i=1

  while [ $i -le 9 ];do               #$i的值小于9,就执行以下循环  until ((i>9))

      j=1               do

      while [ $j -le $i ];do          #$j的值小于$i,就执行以下循环   let j=1

          echo -en "$i*$j=$[i*j]\t"           until ((j>i))

          let j++                     #$j=$j+1       do

      done                echo -en $i*$j=$[$i*$j]\t

      let i++                         #$i=$i+1,重新开始循环    let j++

      let j=1                         #j的值从1开始,重新开始循环  echo

      echo               done

  done

v  4、编写脚本,利用变量RANDOM 生成10 个随机数字,输出这个10 数字,并显示其中的最大值和最小值

  max=$[$RANDOM]           #变量赋值也可以用let max=$RANDOM

  min=$[$RANDOM]           #变量赋值也可以用$(( ));

  #[ $max -lt $min ]&&let mid=$min&&let min=$max&&let max=$mid  #举一个例子

  #echo "random number 1 is : $max"

  #echo "random number 2 is : $min"

  for ((i=1;i<=10;i++));do

          ran=$[$RANDOM]          #与随机生成的中间参数做比较

          echo "random number $i is : $ran"

          [ $max -lt $ran ]&&let max=$ran

          [ $min -gt $ran ]&&let min=$ran

  done

  echo "======================================="

  echo "max number is $max , min number is $min"

v  5、编写脚本,实现打印国际象棋棋盘

  read -p "How many lines will you want to make for chess:" line

  for i in $(seq $line);do                    #控制行数

      for j in $(seq $line);do                #控制列数

          let sum=$[i+j]                      #把不同的两行合并成一个单元

          if [[ $sum%2 -eq 0 ]];then          #取余,当为单数的话打印一种颜色,为双数打印另一种

              echo -en "\e[47m  \e[0m"        #打印空格颜色为黑色

          else

              echo -en "\e[40m  \e[0m"        #打印空格颜色为白色

          fi         z=$[$sum%2]

      done       [ $z -eq 0 ] && echo -ne "\033[47;1m  \033[0m"||echo -ne "\033[40;1m  \033[0m"

      echo

  done

  unset i j sum

v  6、后续六个字符串:154773ae5d bc3f3efe68ada7aa2054 4ee771de1f 2ebd3caa45 3417171ac1是通过对RANDOM 随机数变量执行命令:md5sum|cut -c1-10后 的结果,请破解这些字符串对应的的RANDOM值。

  a=(

  154773ae5d

  bc3f3efe68

  ada7aa2054

  4ee771de1f

  2ebd3caa45

  3417171ac1

  )                     #a是一个数组

  for n in {0..65535};do

      md5=`echo $n | md5sum |cut -c1-10`    

      for m in ${a[@]};do                    #${a[@]} 表示数组中的子集   

          if [[ "$md5" == "$m" ]];then

              echo -e $md5 "–>" $n

          fi

      done

  done

  unset n m md5

1.2.5 until 循环

v until CONDITION; do

循环体

v done

v 进入条件: CONDITION  false

v 退出条件: CONDITION  true

1.3 循环控制语句

1.3.1 continue循环控制语句

v continue [N] :提前结束第N 层的本轮循环,而直接进入下一轮循环判断;最内层为第1层;用于循环体中

while CONDTIITON1; do

CMD1

if  CONDITION2; then

continue

fi

CMDn

done

1.3.2 break循环控制语句

v break [N] :提前结束第N 层循环,最内层为第1层 ;用于循环体中,退出整个循环,继续执行循环后的命令

while CONDTIITON1; do

CMD1

if CONDITION2; then

break

fi

CMDn

done

1.3.3 shift循环控制命令

v shift [n]

v 用于将参量列表 list  左移指定次数,缺省为左移一次。

v 参量列表 list 一旦被移动,最左端的那个参数就从列表中删除。while 到循环遍历位置参量列表时,常用到 shift

v    ./doit.sh a b c d e f g h

v    ./shfit.sh a b c d e f g h

示例:doit.sh

#!/bin/bash

# Name: doit.sh

# Purpose: shift through command line arguments

# Usage: doit.sh [args]

while (( $# > 0 )) # or [ $# -gt 0 ]do

echo $*

shift

done

示例:shift.sh

#!/bin/bash

# Using 'shift' to step through all thepositional parameters.

until [ -z "$1" ]do

echo "$1"

shift

done

echo

exit 0

1.3.4创建无限循环

while true; do

循环体

done

until false; do

循环体

done

1.3.5 练习

1、每隔3秒钟到系统上获取已经登录的用户的信息;如果发现用户hacker 登录,则将登录时间和主机记录日志/var/log/login.log , 并退出脚本。     until falsedo

while true ;do            until who | grep ^hacker\> &> /dev/null;do

if who | grep ^hacker\> &> /dev/null;then             sleep 3

break           done

fi           echo $(date +: %F &T)hacker logged on >> /var/log/login.log

sleep 3

done

echo $(date +: %F &T)hacker logged on >> /var/log/login.log

v 2、随机生成10 以内的数字,实现猜字游戏,提示比较大或小,相等则退出。

#!/bin/bash

suiji=$[$RANDOM%10+1]            #随机生成10以内的数字

read -p "我猜:" shuru

until [[ $suiji -eq $shuru ]]; do

  [ $shuru -lt $suiji ] && echo "小了"

  [ $shuru -gt $suiji ] && echo "大了"

  read -p "我猜:" shuru

done

echo "猜中了,退出"

v 3、用文件名做为参数,统计所有参数文件的总行数特殊用法。

 

1.3.6 循环的特殊用法

while特殊用法

遍历文件的每一行:while read line; do

循环体

done < /PATH/FROM/SOMEFILE

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

练习

扫描/etc/passwd 文件每一行,如发现GECOS 字段为空,则填充用户名和单位电话为62985600 ,并提示该用户的GECOS信息修改成功。

while read line;do

if [ -z `awk -F: {print $5} $line` ] ; then              if [-z `echo $line|cut -d: -f5`] ;then

chfn -p 62985600 -f 62985600 `awk -F: {print $1} $line` &> /dev/null

echo The user GECOS have been changed successful

fi

done < etc/passwd

unset line

for特殊用法

v  双小括号方法,即(()) 格式,也可以用于算术运算

v  双小括号方法也可以使bash Shell 实现C 语言风格的变量操作

#I=10    ;    #((I++))

for 循环的特殊格式:

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

do

循环体

done

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

v  控制变量的修正表达式:每轮循环结束会先进行控制变量修正运算,而后再做条件判断

1.3.7 select 循环与菜单

select variable(变量)  in list

do

循环体命令

done

v  select循环主要用于创建菜单,按数字顺序排列的示 菜单项将显示在标准错误上,并显示 PS3提示符,等待用户输入。

用户输入菜单列表中的某个数字,执行相应的命令

v  用户输入被保存在内置变量 REPLY  中。

select case

vselect 是个无限循环,因此要记住用 break 命令退出循环,或用exit按命令终止脚本。也可以按 ctrl+c退出循环。

vselect 和经常和 case 联合使用

v与 for循环类似,可以省略 in list ,此时使用位置参量

1.3.8 trap信号捕捉

trap ' 触发指令'  信号

自定义进程收到系统发出的指定信号后,将执行触发指令,而不会执行原操作

trap ' ' 信号:忽略信号的操作

trap '-' 信号:恢复还信号的操作

trap -p:列出自定义信号操作

1.3.9 trap 示例

#!/bin/bash

trap 'echo signal:SIGINT"' int

trap -p

for((i=0;i<=10;i++))

do

sleep 1

echo $i

done

trap '' int

trap -p

for((i=11;i<=20;i++))

do

sleep 1

echo $i

done

trap '-' int

trap -p

for((i=21;i<=30;i++))

do

sleep 1

echo $i

done

二、函数介绍

v  函数function 是由若干条shell 命令组成的语句块,实现代码重用和模块化编程。

v它与shell 程序形式上是相似的,不同的是它不是一个单独的进程,不能独立运行,而是shell 程序的一部分。只能被调用。

v  函数和shell 程序比较相似,区别在于:

Ø Shell 程序在子Shell 中运行。

Ø Shell 函数在当前Shell 中运行。因此在当前Shell 中,函数可以对shell 中变量进行修改。

匿名函数:{cmd1cmd2cmd3},不定义在set的函数变量中。

2.1定义函数

v  函数由两部分组成:函数名f_name和函数体。

v  语法一:

function f_name {

函数体

}

v  语法二:

function f_name () {

函数体

}

v最常用语法三:

f_name (){

函数体

}

2.2函数使用

v  函数的定义和使用:

1.可在交互式环境下定义函数 ; 2.可将函数放在脚本文件中作为它的一部分 ; 3.可放在只包含函数的单独文件中。

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

函数的调用:给定函数名;函数名出现的地方,会被自动替换为函数代码。

函数的生命周期:被调用时创建,返回时终止,只能在当前shell中进行。

2.2.1函数返回值

函数有两种返回值:

v   函数的执行结果返回值:

(1)  使用echo等命令进行输出  ;  (2)  函数体中调用命令的输出结果。

v  函数的退出状态码:

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

(2)  自定义退出状态码, 其格式为:在函数中 return #从函数中返回,用最后状态命令决定返回值。

return 0 无错误返回。退出函数,但是继续执行函数后面的命令;return 1-255 有错误返回。不执行后面任务。

echo $? 的值为return 返回的值)

function函数存放目录:/etc/init.d/functions在自己脚本中调用的话,用source  /root/bin/f_name 就可执行函数

 交互式环境下定义和使用函数(不常用)

v  示例:

$dir() {

> ls -l

> }

v  定义该函数后,若在$ 后面键入dir ,其显示结果同ls -l的作用相同。

$dir 函数:

v 该dir 函数将一直保留到用户从系统退出(仅对当前终端有效),或执行了如下所示的unset 命令:$ unset dir

unset f_name :删除函数 ;set f_name :查看定义的函数

在脚本中定义及使用函数

v  函数在使用前必须定义,因此应将函数定义放在脚本开始部分,直至shell 首次发现它后才能使用

v  调用函数仅使用其函数名即可。

v  示例:

$cat func1

#!/bin/bash

# func1

hello()

{

echo "Hello there today's date is `date +%F`"

}

echo "now going to the function hello"

hello

echo "back from the function"

2.2.2使用函数文件

v 可以将经常使用的函数存入函数文件,然后将函数文件载入shell

v 文件名可任意选取,但最好与相关任务有某种联系。例如:functions.main

v 一旦函数文件载入shell ,就可以在命令行或脚本中调用函数。可以使用set 命令查看所有定义的函数,其输出列表包括已经载入shell 的所有函数。

v 若要改动函数,首先用unset 命令从shell 中删除函数。改动完毕后,再重新载入此文件。

创建函数文件

v  函数文件示例:

$cat functions.main

#!/bin/bash

#functions.main

findit()

{

if [ $# -lt 1 ] ; then

echo "Usage:findit file"

return 1

fi

find / -name $1 print

}

载入函数

v  函数文件已创建好后,要将它载入shell

v  定位函数文件并载入shell 的格式:.  filename source filename

随后,函数就被载入到set 所定义的函数里了,就可以在脚本里直接使用函数名了

v  注意:此即< > < 空格> < 文件名>

这里的文件名要带正确路径

v  示例:上例中的函数,可使用如下命令: .  functions.main

检查载入函数

v  使用set 命令检查函数是否已载入。set 命令将在shell 中显示所有的载入函数。

v  示例:

$set

findit=( )

{

if [ $# -lt 1 ]; then

echo "usage :findit file";

return 1

fi

find / -name $1 -print

}

2.2.3执行shell 函数

v  要执行函数,简单地键入函数名即可:

$findit groups

/usr/bin/groups

/usr/local/backups/groups.bak

删除shell 函数

v  现在对函数做一些改动。首先删除函数,使其对shell 不可用。使用unset 命令完成此功能.

v  命令格式为:unset function_name

v  示例 :$unset findit   ; 再键入set 命令,函数将不再显示

2.3函数参数

v  函数可以接受参数:

传递参数给函数:调用函数时,在函数名后面以空白分隔给定参数列表即可;例如testfunc arg1 arg2 …

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

2.3.1函数变量

变量作用域:

环境变量:当前shell 和子shell 有效

本地变量:只在当前shell 进程有效,为执行脚本会启动专用子shell 进程;因此,本地变量的作用范围是当前shell 脚本程序文件,包括脚本中的函数。

局部变量:函数的生命周期;函数结束时变量被自动销毁

v   注意:如果函数中有局部变量,如果其名称同本地变量,使用局部变量。

v  在函数中定义局部变量的方法:local NAME=VALUE

2.3.2函数递归

函数直接或间接调用自身;注意递归层数

v  递归实例:阶乘是基斯顿·于卡曼于1808年发明的运算符号,是数学术语一个正整数的阶乘(factorial )是所有小于及等于该数的正整数的积,并且有0 的阶乘为1 。自然数n 的阶乘写作n!

n!=1 ×2 ×3 ××n

阶乘亦可以递归方式定义:0!=1 n!=(n-1)! ×n

n!=n(n-1)(n-2)…1   n(n-1)! = n(n-1)(n-2)!

函数递归示例: fact.sh

#!/bin/bash

#

fact() {

if [ $1 -eq 0 -o $1 -eq 1 ]; then

echo 1

else

echo $[$1*$(fact $[$1-1])]

fi

}

fact $1

注意:引入变量先要用read

2.3.3练习

1 、编写服务 脚本/root/bin/testsrv.sh ,完成如下要求:

(1)  脚本可接受参数:start, stop, restart, status

(2)  如果参数非此四者之一,提示使用格式后报错退出

(3)  如是start:则创建/var/lock/subsys/ SCRIPT_NAME ,  并显示“启动成功”

考虑:如果事先已经启动过一次,该如何处理?

(4)  如是stop:则删除/var/lock/subsys/ SCRIPT_NAME ,  并显示“停止完成”

考虑:如果事先已然停止过了,该如何处理?

(5)  如是restart ,则先stop,  start

考虑:如果本来没有start ,如何处理?

(6)  如是status,  则如果/var/lock/subsys/ SCRIPT_NAME 文件存在,则显示“ SCRIPT_NAME is running…

如果/var/lock/subsys/ SCRIPT_NAME 文件不存在,则显示“ SCRIPT_NAME is stopped…

其中: SCRIPT_NAME 为当前脚本名

(7) 在所有模式下禁止启动该服务,可用chkconfig service 命令管理

lockfile=/var/log/subsys/SCRIPT_NAME

usagte(){

    echo "Usage: $prog {start|stop|restart|status}"

}

if [ $# -lt 1 ]; then

    usage

    exit 1

fi

start(){

    if [ -e $lockfile ]; then

        echo "$prog is already running."

        return 0

    else

        touch $lockfile

        [ $? -eq 0 ] && echo "Starting $prog finished."

    fi

}

stop(){

    if [ -e $lockfile ]; then

        rm -f $lockfile && echo "Stop $prog ok."

    else

        echo "$prog is stopped yet."

    fi

}

status(){

    if [ -e $lockfile ]; then

        echo "$prog is running."

    else

        echo "$prog is stopped."

    fi

}

case $1 in

start)

       start

       ;;

stop)

       stop

       ;;

restart)

       stop

       start

       ;;

status)

       status

       ;;

*)

       usage

esac

2 、编写脚本/root/bin/copycmd.sh

(1)  提示用户输入一个可执行 命令名称

(2)  获取此命令所依赖到的所有库文件列表

(3)  复制命令至某目标目录( 例如/mnt/sysroot) 下的对应路径下;

如:/bin/bash ==> /mnt/sysroot/bin/bash

/usr/bin/passwd ==> /mnt/sysroot/usr/bin/passwd

(4)  复制此命令依赖到的所有库文件至目标目录下的对应路径下:

如:/lib64/ld-linux-x86-64.so.2 ==> /mnt/sysroot/lib64/ld-linux-x86-64.so.2

(5) 每次复制完成一个命令后,不要退出,而是提示用户键入新的要复制的命令,并重复完成上述功能;直到用户输入quit.

#!/bin/bash

ch_root="/mnt/sysroot"

[ ! -d $ch_root ] && mkdir $ch_root

bincopy() {

      if which $1 $>/dev/null; then

          local cmd_path="which –skip-alias $1"

          local bin_dir="dirname $cmd_path"

          [ -d ${ch_root}${bin_dir} ] || mkdir -p ${ch_root}${bin_dir}

          [ -f ${ch_root}${cmd_path}] || cp $cmd_path ${ch_root}${bin_dir}

          return 0

      else

          echo "Command not found."

          return 1

      fi

}

libcopy() {

      local lib_list=$(ldd `which –skip-alias $1` | grep -Eo '/[^[:space:]]+')

      for loop in $lib_list;do

          local lib_dir=`dirname $loop`

          [ -d $ {ch_root}${lib_dir} ] || mkdir -p ${ch_root}${lib_dir}

          [ -f $ {ch_root}${loop} ] || cp $loop ${ch_root}${lib_dir}

      done

}

read -p "Please input a command:" command

while [ "$command" != "quit" ]; do

     if bincopy $command ; then

        libcopy $command

     fi

     read -p "Please input a command or quit: "command

done

3 、编写函数实现两个数字做为参数,返回最大值.

#!/bin/bash

source funs.sh

funs.sh

#!/bin/bash

echo "please enter two number"

read a

read b

if test $a -eq $b;then

echo "two same: "$a

elif test $a -gt $b;then

echo "big is: "$a

else

 echo "big is: "$b

fi

4 、编写函数实现数字的加减乘除运算,例如输入 1 + 2 ,将得出正确结果。

#!/bin/bash

source funs.sh

jia   $1 $2

jian  $1 $2

cheng $1 $2

chu   $1 $2

 

funs.sh

#!bin/bash

echo "1st arg is $1"

echo "2nd arg is $2"

jia   (){

          local a=$[$1+$2]

          echo $a

         }

jian  (){

          local a=$[$2-$1]

          echo $a

         }

cheng (){

          local a=$[$1*$2]

          echo $a

         }

chu   (){

          local a=$[$1/$2]

          echo $a

         }

5 、斐波那契数列又称黄金分割数列,因数学家列昂纳多· 斐波那契以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:0 1 1 2 3 5 8 13 21 34 、…… ,斐波纳契数列以如下被以递归的方法定义:F 0 =0 F 1 =1 F n =F(n-1)+F(n-2) n2)利用函数 ,求n 阶斐波那契数列。

fact() {

    if [ $1 -eq 0 ]

    then

        echo 0

    elif [ $1 -eq 1 ]

    then

        echo 1

    else

        echo $[$(fact $[$1-2])+$(fact $[$1-1])]

    fi

}

6 、汉诺塔(又称河内塔)问题是源于印度一个古老传说。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64 片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。

利用函数,实现N 片盘的汉诺塔的移动步骤。

#!/bin/bash

step=0

hanoi(){

[[ ! $1 = ~ ^[1-9][0-9]*$ ]]&&echo "error! please input a positive interger" && exit

if [ $1 -eq 1 ]; then

         let step++

         echo "$step: move plate $1   $2 —–> $4"

else

         hanoi "$[$1-1]" $2 $4 $3

         let step++

         echo "$step: move plate $1   $2 —–> $4"

         hanoi "$[$1-1]" $3 $2 $4

fi

}

read -p "please input the number of plates: "number

hanoi $number A B C

三、数组

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

v  数组:存储多个元素的连续的内存空间,相当于多个变量的集合

数组名和索引

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

注意:索引可支持使用自定义的格式,而不仅是数值格式,即为关联索引 bash4.0 版本之后开始支持。

bash 的数组支持稀疏格式(索引之间不连续)

声明数组:

declare -a ARRAY_NAME 可以不声明,但是建议声明

declare -A ARRAY_NAME:  关联数组 ;必须要先声明才能使用

3.1数组赋值

v  数组元素的赋值:

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

ARRAY_NAME[INDEX]下标=VALUE

weekdays[0]="Sunday" weekdays[4]="Thursday"  稀疏格式

(2)  一次赋值全部元素:ARRAY_NAME=("VAL1" "VAL2" "VAL3" …)

生成列表可以用ARRAY_NAME=ARRAY_NAME=({1..10});ARRAY_NAME=*

(3)  只赋值特定元素:指定下标,直接赋值

ARRAY_NAME=[0]="VAL1" [3]="VAL2" …

(4)  交互式数组值对赋值:read -a ARRAY

3.2引用数组

v  引用数组元素:${ARRAY_NAME[INDEX]}   注意:省略[INDEX] 表示引用下标为0 的元素

数组的长度( 数组中元素的个数): 当不知道数组中存在多少元素

${ARRAY_NAME[*]}${ARRAY_NAME[@]}:查看显示数组中所有元素

${#ARRAY_NAME[*]}${#ARRAY_NAME[@]}:查看显示数组的元素个数 ,新加的元素的下标就等于元素的个数

如果数组要新添加一个元素 name[${ARRAY_NAME[@]}]=var

v  示例:生成10 个随机数保存于数组中,并找出其最大值和最小值

#!/bin/bash

declare -a rand

declare -i max=0

declare -i min=32767

for i in {0..9}; do

rand[$i]=$RANDOM

echo ${rand[$i]}

[ ${rand[$i]} -gt $max ] && max=${rand[$i]}

[ ${rand[$i]} -lt $min ] && min=${rand[$i]}

done

echo "Max: $max Min:$min"

v  编写脚本 ,定义一个数组,数组中的元素是/var/log 目录下所有以.log 结尾的文件;要统计其下标为偶数的文件中的行数之和。

#!/bin/bash

#

declare -a files

files=(/var/log/*.log)

declare -i lines=0

for i in $(seq 0 $[${#files[*]}-1]); do fileline=`wc -l ${file[$i]} |cut -d " " -f1`

if [ $[$i%2] -eq 0 ];then            ——》for i in $(seq 0 2 $[${#file[*]}-1];do

let lines+=$(wc -l ${files[$i]} | cut -d' ' -f1) [ $[$i%2] -eq 0 ] && let lines+=$fileline

fi

done

echo "Lines: $lines."

3.3数组数据处理

引用数组中的元素:

所有元素:${ARRAY[@]}  ${ARRAY[*]}

数组切片${ARRAY[@]:offset:number} offset个元素;取随后的number个元素

offset:  要跳过的元素个数 ; number:  要取出的元素个数

取偏移量之后的所有元素:${ARRAY[@]:offset}

向数组中追加元素:ARRAY[${#ARRAY[*]}]=var

删除数组中的某元素:导致稀疏格式:unset ARRAY[INDEX]

关联数组:declare -A ARRAY_NAME  注意:必须先声明,再调用

  ARRAY_NAME=([idx_name1]='val1' [idx_name2]='val2…)

现有的数组只能先unset ,再重新开始设置

练习1 、输入若干个数值存入数组中,采用 冒泡算法进行升序或降序排序

#!/bin/sh

echo "please input a number list:"

read -a arr

 for (( i=0 ; i<${#arr[@]} ; i++ ))

do

  for (( j=${#arr[@]}- 1 ; j>i ; j– ))

  do

    #echo $j

    if  [[ ${arr[j]} -lt ${arr[j-1]} ]]

    then

       t=${arr[j]}

       arr[j]=${arr[j-1]}

       arr[j-1]=$t

    fi

  done

done

echo "after ascending sorting:"

echo ${arr[@]}

=================================================

for (( i=0 ; i<${#arr[@]} ; i++ ))

do

  for (( j=${#arr[@]}- 1 ; j>i ; j– ))

  do

    #echo $j

    if  [[ ${arr[j]} -gt ${arr[j-1]} ]]

    then

       t=${arr[j]}

       arr[j]=${arr[j-1]}

       arr[j-1]=$t

    fi

  done

done

echo "after descending sorting:"

echo ${arr[@]}

3.4字符串处理

 bash的字符串处理工具

3.4.1字符串切片

${#var}: 返回字符串变量var 的长度

${var:offset}: 返回字符串变量var 中从第offset 个字符后(不包括第offset 个字符)的字符开始,到最后的部分,offset的取值在0 ${#var}-1 之间(bash4.2 后,允许为负值)

${var:offset:number} :返回 字符串变量var 中从第offset 个字符后(不包括第offset 个字符)的字符开始长度为number 的部分

${var: -lengh} :取字符串的最右侧几个字符

注意:冒号后必须有一空白字符

${var:offset: -lengh} :从最左侧跳过offset 字符,一直取到字符串的最右侧lengh个字符之前

3.4.2基于模式取子串

${var#*word} :其中word 可以是指定的任意字符

功能:自左而右,查找var 变量所存储的字符串中,第一次出现的word,  删除字符串开头至第一次出现word 字符之间的所有字符。

${var##*word} :同上,不同的是,删除的是字符串开头至最后一次由word 指定的字符之间的所有内容

示例:file="var/log/messages 

echo ${file#*/}: log/messages echo ${file##*/}: messages

${var%word*} :其中word 可以是指定的任意字符;

功能:自右而左,查找var 变量所存储的字符串中,第一次出现的word,  删除字符串最后一个字符向左至第一次出现word 字符之间的所有字符;

${var%%word*} :同上,只不过删除字符串最右侧的字符向左至最后一次出现word 字符之间的所有字符;

file="/var/log/messages"

echo ${file%/*}: /var/log ; echo ${file%%/*}:

示例:url=http://www.magedu.com:80

echo ${url##*:} 80 echo ${url%%:*} http

3.4.3查找替换

${var/pattern/substi} :查找var 所表示的字符串中,第一次被pattern 所匹配到的字符串,以substi 替换之

${var//pattern/substi}:  查找var 所表示的字符串中,所有能被pattern 所匹配到的字符串,以substi 替换之

${var/#pattern/substi} :查找var 所表示的字符串中,行首被pattern 所匹配到的字符串,以substi 替换之

${var/%pattern/substi} :查找var 所表示的字符串中,行尾被pattern 所匹配到的字符串,以substi 替换之

3.4.4查找并删除

${var/pattern} :查找var 所表示的字符串中,删除第一次被pattern 所匹配到的字符串

${var//pattern} :所有

${var/#pattern} :行首

${var/%pattern} :行尾

3.4.5字符大小写转换

${var^^} :把var 中的所有小写字母转换为大写

${var,,} :把var 中的所有大写字母转换为小写

3.5变量赋值

v ${var:-value} :如果var 为空或未设置,那么返回value ;否则,则返回var的值

v ${var:+value} :如果var 不空,则返回value ,否则返回空值

v ${var:=value} :如果var 为空或未设置,那么返回value ,并将value 赋值给var ;否则,则返回var的值

v ${var:?error_info} :如果var 为空或未设置 ,那么在当前终端打印error_info;否则,则返回var的值

为脚本程序使用配置文件, 实现变量赋值

(1)  定义文本文件,每行定义“name=value

(2)  在脚本中source 此文件即可

高级变量用法有类型变量

v Shell 变量一般是无类型的,但是bash Shell 提供了declaretypeset 两个命令用于指定变量的类型,两个命令是等价的。

3.5.1 declare变量

v declare [ 选项]  变量名

-r:将变量设置为只读属性 ; -x:将变量声明为环境变量 ;-f:显示此脚本前定义过的所有函数名及其内容

-i:将变量定义为整型数 ; -a:将变量定义为数组 ;-F:仅显示此脚本前定义过的所有函数名

-A:将变量定义为关联数组

-l:将变量值转为小写字母 declare -l var=UPPER -u:将变量值转为大写字母 declare -u var=lower

3.5.2间接变量引用

v  如果第一个变量的值是第二个变量的名字,从第一个变量引用第二个变量的值就称为间接变量引用。

v variable1=variable2 variable2=value

v variable1 的值是variable2 ,而variable2 又是变量名,variable2 的值为value ,间接变量引用是指通过variable1获得变量值value 的行为

v bash Shell 提供了两种格式实现间接变量引用

eval tempvar=\$$variable1 tempvar=${!variable1}

示例: [root@server ~]# N=NAME

[root@server ~]# NAME=wangxiaochun

[root@server ~]# N1=${!N}

[root@server ~]# echo $N1

wangxiaochun

[root@server ~]# eval N2=\$$N

[root@server ~]# echo $N2

wangxiaochun

3.5.3 eval 命令

v eval 命令将会首先扫描命令行进行所有的置换,然后再执行该命令。该命令适用于那些一次扫描无法实现其功能的变量. 该命令对变量进行两次扫描(如果变量是命令,则显示执行的结果)。

示例: [root@server ~]# CMD=whoami

[root@server ~]# echo $CMD

whoami

[root@server ~]# eval $CMD

root

3.5.4 mktemp创建临时文件

mktemp 命令:创建的临时文件可避免冲突

v  mktemp [OPTION]… [TEMPLATE]

TEMPLATE: filename.XXX 注意:X 至少要出现三个,创建并显示创建的临时文件的名字

-d:  创建临时目录  ; -p DIR –tmpdir=DIR :指明临时文件所存放目录位置

示例

#mktemp /tmp/test.XXX  计划任务crontab -e 输出重定向到/dev/null;或者用此临时文件爱你

#tmpdir=`mktemp d /tmp/testdir.XXX`

#mktemp –tmpdir=/testdir test.XXXXXX

3.5.5 install安装复制文件

v install 命令:适合安装程序,能直接给予文件的所有者,所有组以及权限

install [OPTION]… [-T] SOURCE DEST  单文件 ; install [OPTION]… SOURCE… DIRECTORY

install [OPTION]… -t DIRECTORY SOURCE… install [OPTION]… -d DIRECTORY… 创建空目录

-m MODE:指明权限 ,默认755 -o OWNER:指明属主 ; -g GROUP:指明属组

示例:

install -m 700 -o wang -g admins file1 file2 相当于cp,不过给予了权限

install m d /testdir/installdir 直接创建目录

原创文章,作者:lyx,如若转载,请注明出处:http://www.178linux.com/61073

(0)
lyxlyx
上一篇 2016-11-27
下一篇 2016-11-27

相关推荐

  • 用户与组

    一、用户与组的概念 1、Linux多用户,多任务的特性    Linux是多用户多任务操作系统,多用户多任务就是可在系统上建多个用户,多个用户可同一时间同一个系统互不影执行的响各自不同的任务。例如linux服务器上有root、www、ftp,四个用户。同一时间内,root用户在查看系统日志,管理维护系统,www用户在修改自己的网页程序,f…

    Linux干货 2016-10-25
  • Linux基础学习总结(三)

    1、列出当前系统上所有已经登录的用户的用户名,注意:同一个用户登录多次,则只显示一次: [leexide@leeblog ~]$who | cut -d" " -f1 | uniq 2、取出最后登录到当前系统的用户的相关信息。 last -n&nbs…

    Linux干货 2016-10-03
  • grep,find用法-2

    1、显示当前系统上root、fedora或user1用户的默认shell; grep -E “^(root|fedora|user1)>” /etc/passwd | cut -d: -f1,7 [root@bogon Desktop]# grep -E “^(root|fedora|user1)\>” /etc/pass…

    Linux干货 2017-08-04
  • 正则表达式字符集

    POSIX Description ASCII Unicode Shorthand Java [:alnum:] Alphanumeric characters [a–zA–Z0–9] [\p{L}\p{Nl}  \p{Nd}] \p{Alnum} [:alpha:] Alphabetic characters …

    Linux干货 2017-07-07
  • Linux基础知识(六)

    本问主要讲述Linux的基础知识,具体包一下内容: 1、复制/etc/rc.d/rc.sysinit文件至/tmp目录,将/tmp/rc.sysinit文件中的以至少一个空白字符开头的行的行首加#; cp /etc/rc.d/rc.sysinit /tmp sed "s/^[[:space:]]\?/#/g" /tmp/rc.sysini…

    Linux干货 2016-10-30
  • 马哥linux运维第一天感受

    101个人的大家庭

    Linux干货 2018-03-26