Linux入门——shell进阶

Posted angge

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux入门——shell进阶相关的知识,希望对你有一定的参考价值。

流程控制

顺序执行

If

单分支

if 判断条件;then
    条件为真的分支代码
fi  

双分支

if 判断条件;then
  条件为真的分支代码

else

  条件为假的分支代码
fi

多分支

  if 判断条件1; then
    条件为真的分支代码
  elif判断条件2; then
    条件为真的分支代码
  elif判断条件3; then
    条件为真的分支代码
  else
    以上条件都为假的分支代码
  fi

elif下同样可接else。表示第一个条件为真,第二条件为假的情况

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

 if  echo $name|grep "$n" &> /dev/null ;then

选择执行

Case

case 变量引用in
  PAT1)分支1;;
  PAT2)分支2;;   #注意分支结束一定要能够用;;
  ...
  *)默认分支;;
esac

case支持glob风格的通配符:

  *: 任意长度任意字符

  ?: 任意单个字符

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

  a|b: a或b

循环执行

将某代码段重复运行多次

重复运行多少次:

循环次数事先已知

循环次数事先未知

有进入条件和退出条件

For

for 变量名in 列表;do
  循环体
done

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

例:利用for打印九九乘法表

#!/bin/bash
for a in {1..9} ;do
  b=$a
  for b in $(seq $b 9);do
    echo -n "$a*$b=$[$a*$b] "
  done
  echo
done

结果为

[[email protected] shell0]# ./99f.sh
1*1=1 1*2=2 1*3=3 1*4=4 1*5=5 1*6=6 1*7=7 1*8=8 1*9=9
2*2=4 2*3=6 2*4=8 2*5=10 2*6=12 2*7=14 2*8=16 2*9=18
3*3=9 3*4=12 3*5=15 3*6=18 3*7=21 3*8=24 3*9=27
4*4=16 4*5=20 4*6=24 4*7=28 4*8=32 4*9=36
5*5=25 5*6=30 5*7=35 5*8=40 5*9=45
6*6=36 6*7=42 6*8=48 6*9=54
7*7=49 7*8=56 7*9=63
8*8=64 8*9=72
9*9=81

While

while CONDITION; do
  循环体
done

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

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

进入条件:CONDITION为true

退出条件:CONDITION为false

例:利用while打印九九乘法表

#!/bin/bash
i=1
while [ $i -lt 10 ];do
  b=$i
  while [ $b -lt 10 ];do
    echo -n "$i*$b=$[$i*$b] "
    b=$[$b+1]
  done
  i=$[$i+1]
  echo
done

输出结果

[[email protected] shell0]# ./99w.sh
1*1=1 1*2=2 1*3=3 1*4=4 1*5=5 1*6=6 1*7=7 1*8=8 1*9=9
2*2=4 2*3=6 2*4=8 2*5=10 2*6=12 2*7=14 2*8=16 2*9=18
3*3=9 3*4=12 3*5=15 3*6=18 3*7=21 3*8=24 3*9=27
4*4=16 4*5=20 4*6=24 4*7=28 4*8=32 4*9=36
5*5=25 5*6=30 5*7=35 5*8=40 5*9=45
6*6=36 6*7=42 6*8=48 6*9=54
7*7=49 7*8=56 7*9=63
8*8=64 8*9=72
9*9=81

例:编写脚本/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
echo "停止请输入quit"
while true;do  #永真循环
    read -p 请输入一个命令: cmd
    if [[ $cmd =~ quit ]];then #输入exit退出
        exit
    fi
    a=$(which $cmd| tail -1)  #查询命令来自哪个文件
    mkdir -p /app/sysroot$(echo $a|egrep -o "/.*/") &> /dev/null
    cp -v $a /app/sysroot/$a  #复制命令
    b=$(ldd /usr/bin/ls|cut -d  -f3)  #查询命令需要哪些库文件,这里有一个库文件是集成在内核里的不需要拷贝
    for n in $b;do
        if [ $n !=   ];then
            mkdir -p /app/sysroot$(echo $n|egrep -o "/.*/") &> /dev/null
             cp -v $n /app/sysroot$n  #复制库文件
        fi
    done
done

 

Until

与while完全一样只是判断条件不同

until CONDITION; do
  循环体
done

进入条件:CONDITION 为false

退出条件:CONDITION 为true

Select

select variable in list
do
  case 变量引用in
    PAT1)分支1;;
    PAT2)分支2;; #select 经常与case一起使用
    ...
    *)默认分支;;
  esac
done

select 循环主要用于创建菜单,按数字顺序排列的菜单项将显示在标准错误

上,并显示PS3 提示符,等待用户输入

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

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

循环控制语句

Continue

用于循环体中

continue [N]:提前结束第N层的本轮循环,而直接进入下一轮判断;最内层为第1层(不写默认为1)

Break

用于循环体中

break [N]:提前结束第N层循环,最内层为第1层

Shift

shift [n]

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

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

函数

语法一:

  function f_name{

    ...函数体...

  }

语法二:

  function f_name(){

    ...函数体...

  }

语法三:

  f_name(){

    ...

  }

数组

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

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

数组名和索引

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

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

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

声明数组:

declare -a ARRAY_NAME

declare -A ARRAY_NAME: 关联数组

注意:两者不可相

例:1

为某餐馆用Shell制作一个点菜系统。功能如下:执行脚本会列出主菜单,如下

请选择您要吃的菜品

(消费满80打8折)

1)饭      2)面、

3)饺子  4)结账

等待用户选择
如选择1,则再问                  如选择2,则再问                               如选择3,则再问                                    如选择4,则直接退出
1)炒饭 10元                       1)炒面 10元                                     1)猪肉大葱 18元
2)盖饭 10元                       2)热干面 9元                                    2)素三鲜 15元
3)拌饭 12元                       3)烩面 15元                                      3)韭菜鸡蛋 18元
4)返回主菜单                     4)重庆小面 9元                                4)返回主菜单
                                            5)返回主菜单
选择后,提示用户,再吃几份,输入完后提示是否还要继续选,如继续选,则返回主菜单,然后继续以上过程,如不要继续选,则会打印消费单后退出,消费单格式如下

炒饭 5份 50元
热干面 1份 9元
素三鲜 1份 15元
总计 74元
折后 59.2元

#!/bin/bash
PS3=请您点餐:   #定义select的输入格式
echo "消费满80打8折,亲!"
declare -a fen                                                  #定义份数数组
caidan=(炒饭 盖饭 拌饭 炒面 热干面 烩面 小面 猪肉大葱 素三鲜 韭菜鸡蛋)  #定义菜单名数组
danjia=(10 10 12 10 9 15 9 18 15 18)                           #定义价格数组
while true ;do                              #定义永真循环方便循环跳出
  select variable in 饭 面 饺子 不吃滚蛋 结账                   #select列表,主菜单
  do
    case $REPLY in                                         #定义饭子菜单
      1)select riable in 炒饭 盖饭 拌饭 返回               
      do
        case $REPLY in
          1)echo "炒饭10元每份"
           read -p 您要几份: fen[0]        
           break 2;;                                 #跳出两层循环回到主菜单
          2)echo "盖饭10元每份"
           read -p 您要几份: fen[1]
           break 2;;
          3)echo "拌饭10元每份"
           read -p 您要几份: fen[2]
           break 2;;
          4)break 2;;
        esac
      done;;
      2)select riable in 炒面 热干面 烩面 小面 返回            #定义面子菜单
      do
        case $REPLY in
          1)echo "炒面10元份"
           read -p 您要几份: fen[3]
           break 2;;
          2)echo "热干面9元每份"
           read -p 您要几份: fen[4]
           break 2;;
          3)echo "烩面15元每份"
           read -p 您要几份: fen[5]
           break 2;;
          4)echo "小面9元每份"
           read -p 您要几份: fen[6]
           break 2;;
          5)break 2;;
        esac
      done;;
      3)select riable in 猪肉大葱 素三鲜 韭菜鸡蛋 返回                   #定义饺子子菜单
      do
        case $REPLY in
          1)echo "猪肉大葱18元每份"
           read -p 您要几份: fen[7]
           break 2;;
          2)echo "素三鲜15元每份"
           read -p 您要几份: fen[8]
           break 2;;
          3)echo "韭菜鸡蛋18元每份"
           read -p 您要几份: fen[9]
           break 2;;
          4)break 2;;
        esac
      done;;
      4)exit;;                                              #退出菜单
      5)echo "清单";a=0                                       #清单
       for ((i=0;i<10;i++));do
        if [ "${fen[$i]}" != "" ];then                      #判断fen[$i]是否非空,非空则证明点了该菜 
          echo "${caidan[$i]} ${danjia[$i]}/份 $[${fen[$i]}*${danjia[$i]}]元"
          a=$[$a+$[${fen[$i]}*${danjia[$i]}]]
        fi
       done
       echo "总计: $a"
       if [ $a -ge 80 ];then                               #判断是否打折
        echo "折后: $[$a*8/10]"
       else
        echo "折后: $[a]"
       fi
       exit;;
    esac

  done
done

例2:

写一个创建用户的脚本,要求如下
1.执行时会询问,请输入要创建的用户名称(当用户超时8秒不输入,提示超时并退出),脚本会检测用户名是否已经存在,若已存在,则提示用户已存在,是否要为其设置密码,如否,则继续回到第1步,询问用户另一个用户名。如是,进入第3步。
2.如第1步完成,则会创建指定用户,然后脚本继续问用户是否为新用户设置密码,如否,则返回第1步继续创建其他用户,如是,则进入第3步
3.为用户设置密码,要求密码要验证2次,2次一致才可通过。同时密码要满足复杂性要求,要求如下:
密码至少8个字符长度,密码必须包含 大写,小写字母,数字,和#@!,._ 这之中的三种字符。 如不满足,则提示用户密码太短或太简单,返回第3步。
且密码不能是 /usr/share/dict/words 已存在的字符,或是,则提示用户密码是个常见单词,然后返回第3步。
用户只能尝试设置新密码3次,若超过3次仍然没有设置出满足要求的密码,则直接返回第1步。
4.在任何步骤,只要输入exit均可退出脚本

#!/bin/bash
while true;do
    read -p "请输入用户名:" name
    a=$(cat /etc/passwd |cut -d: -f1)
    for n in $a;do                                                              #判断用户名是否合法
        if  echo $name|grep "$n" &> /dev/null ;then
            echo "用户名不合法"
            continue 2
        elif echo "$name" |egrep "[[:upper:]].{4,}";then
            true
            break 2
        else
            echo "请输入首字母大写且高于5位的字符"
            continue 2
        fi
    done
done
while true;do                                                                 #判断性别是否合法
    read -p "请输入性别male/female:" male
    case $male in
        male|female)break 2;;
        *)echo "输入错误请重新输入";continue 2;;
    esac
done
while true ;do                                                               #判断生日是否合法
    read -p "请输入生日例YYYY-MM-DD:" bir 
    if echo $bir|egrep "[[:digit:]]{4}(-[[:digit:]]{1,2}){2}" &> /dev/null;then
        true
    else
        echo "输入不合法";continue 2
    fi
        bird=$(echo $bir|cut -d- -f3)
        birm=$(echo $bir|cut -d- -f2)
        biry=$(echo $bir|cut -d- -f1)
    if cal $bird $birm $biry &> /dev/null;then
        break 2
    else
        echo "输入不合法";continue 2
    fi
done
while true;do                                                                #判断手机号是否合法
    read -p "请输入手机号:" ph
    if echo $ph |egrep <1[3857][[:digit:]]{9}> &> /dev/null;then
        break
    else
        echo "格式不对"
        continue
    fi
done
while true;do                                                          #判断身份证号是否合法
    read -p "请输入身份证号:" card        
    echo $id |egrep [[:digit:]]{17}[[:digit:]x] &> /dev/null
        if [ $? -eq 0 ];then
                biry1=`echo $id |cut -c 7-10`
                birm1=`echo $id |cut -c 11-12`
                bird1=`echo $id |cut -c 13-14`
                if [ $(echo -n $birm|wc -m) -eq 1 ];then
                        birm=0$birm
                fi
                if [ $(echo -n $bird|wc -m) -eq 1 ];then
                        birm=0$bird

                fi
                if [ "$biry$birm$bird" == $biry1$birm1$bird1 ];then
                        shu=$[$(echo $id|cut -c 17)%2]
                        case $shu in
                                1)xingbie=male
                                if [ "$xingbie" != "$sex" ];then
                                        echo "性别错了"
                                        continue
                                else
                                        break
                                fi;;
                                *)xingbie=female
                                if [ "$xingbie" != "$sex" ];then
                                        echo "性别错了"
                                        continue
                                else
                                        break
                                fi;;
                        esac

                else
                        echo "日期不符"
                        continue
                fi
        else
                echo 格式不对
                continue
        fi
done
echo $name:$male:$dete:$card >> /root/info.txt

例3:

1.执行时会询问,请输入要创建的用户名称(当用户超时8秒不输入,提示超时并退出),脚本会检测用户名是否已经存在,若已存在,则提示用户已存在,是否要为其设置密码,如否,则继续回到第1步,询问用户另一个用户名。如是,进入第3步。
2.如第1步完成,则会创建指定用户,然后脚本继续问用户是否为新用户设置密码,如否,则返回第1步继续创建其他用户,如是,则进入第3步
3.为用户设置密码,要求密码要验证2次,2次一致才可通过。同时密码要满足复杂性要求,要求如下:
密码至少8个字符长度,密码必须包含 大写,小写字母,数字,和#@!,._ 这之中的三种字符。 如不满足,则提示用户密码太短或太简单,返回第3步。
且密码不能是 /usr/share/dict/words 已存在的字符,或是,则提示用户密码是个常见单词,然后返回第3步。
用户只能尝试设置新密码3次,若超过3次仍然没有设置出满足要求的密码,则直接返回第1步。
4.在任何步骤,只要输入exit均可退出脚本

#!/bin/bash
password() {                                        #函数判断密码的合法性
for ((i=0;i<=2;i++));do
    b=0
    read -p "请输入密码:" pass
    read -p "请确认密码:" rpass
    if echo $pass|grep [a-z] &> /dev/null;then            
        b=$[$b+1]
    fi
    if echo $pass|grep [A-Z] &> /dev/null;then
         b=$[$b+1]
    fi
    if echo $pass|grep [~!@#$\%^&*] &> /dev/null;then
        b=$[$b+1]
    fi
    if echo $pass|grep [0-9] &> /dev/null;then 
        b=$[$b+1]
    fi
    lens=$(echo $pass |grep -o .|wc -l)
    if [ "$pass" != "$rpass" ];then
         echo "两次密码不一致"
    elif [ $lens -lt 8 ];then
        echo "密码太短"
        echo "密码位数至少为8位"
    elif [ $b -lt 3 ];then        
        echo "密码太过简单"
        echo "密码至少由大写,小写字母,数字,和特殊字符这之中的三种字符"
    elif cat /usr/share/dict/words | grep ".*$pass.*" &> /dev/null;then     #写的有点问题,应该用字典过滤密码,不是用密码过滤字典
        echo "密码中包含常用单词"
    else
        echo $pass |passwd --stdin $username &> /dev/null
        echo "密码修改成功"
        exit
    fi
done
}
while true ;do
read -p "请输入用户名:" -t 8 username                                     #超时输入则退出
a=$(echo $?)
if [ $a != 0 ];then
    echo 
    echo "超时";exit
fi
if [ $username == exit ];then
    exit                                                           #输入exit退出
fi
if id $username &> /dev/null ;then
    echo "该用户已存在"
    read -p "是否为其设置密码y/n:" b
    case $b in
        y|Y)password "$username";;                                #调用passwd函数
        n|N)continue;;                                           
        exit) exit;;                                             #输入exit退出
    esac
else
    useradd $username
    read -p "是否为新用户设置密码y/n:" c
    case $c in
          y|Y)password "$username";;
          n|N)continue;;
          exit) exit;;
      esac
    
fi

 

附:

eval命令

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

创建临时文件

mktemp命令:创建并显示临时文件,可避免冲突

mktemp[OPTION]... [TEMPLATE]

TEMPLATE: filename.XXX

X至少要出现三个

OPTION:

-d: 创建临时目录

-p DIR或--tmpdir=DIR:指明临时文件所存放目录位置

示例:

mktemp/tmp/test.XXX

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

mktemp--tmpdir=/testdirtest.XXXXXX

安装复制文件

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 srcfile desfile

install –m –d /testdir/installdir





















































































































































以上是关于Linux入门——shell进阶的主要内容,如果未能解决你的问题,请参考以下文章

Atom编辑器入门到精通 Atom使用进阶

linux进阶之路:linux入门

Atom编辑器入门到精通 Atom使用进阶

Atom编辑器入门到精通 Atom使用进阶

Atom编辑器入门到精通 Atom使用进阶

第14章,Shell脚本编程进阶