bash [OPTION] FILENAME

例:bash -x ./test.sh

-n :检查脚本语法错误

注意:不是命令语法错误

-x: 调试运行

 

for循环

 

语法1

for 变量名 in 循环列表; do

循环体

done

 

List的生成方式:

1、整数序列:{start..end},例如:{1..10}

2、整数序列:seq 开始数步长结束数,例如:$(seq 1 2 10)

3、命令的执行结果:例如,$(ls/var) `ls /var`

语法2

for ((变量初始值赋值;循环条件;变量的修正表达式)); do

循环体

done

 

例:1+2+3+…+10                                                                            

(1)      sum=0

for i in {1,10};do

let sum+=$i

done

echo $sum

(2)      sum=0

for i in `seq 1 10`;do

let sum+=$i

done

echo $sum

(3)      sum=0

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

let sum+=$i

done

echo $sum

例:   2+4+6+…+10

(1)       for i in `seq 2 210`;do

let sum+=$i

done

echo $sum

(2)      for ((i=2;i<=10;i=$i+2));do

letsum+=$i

done

echo$sum

 

for遍历

例:

显示当前系统上所有默认shellbash的用户的用户名、UID以及此类所有用户的UID之和

找出默认shellbash的用户名,uid

#!/bin/bash

grep '/bin/bash$' /etc/passwd|cut -d':' -f1,3

uid=`grep '/bin/bash$' /etc/passwd|cut -d':' -f3`

#计算uid之和

for i in $uid;do

        letsum_id+=$i

done

echo $sum_id

 

练习:写一个脚本

(1) 接受一个以上文件路径作为参数;

(2) 显示每个文件拥有的行数;

(3) 总结说明本次共为几个文件统计了其行数;

#!/bin/bash

#

echo"Files: $@."

forfile in $@; do

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

    echo "$file has $lines line(s)."

done

echo"Total files: $#."

 

练习:写一个脚本

(1) 传递两个以上字符串当作用户名;

(2) 创建这些用户;且密码同用户名;

(3) 总结说明共创建了几个用户;

 

#/bin/bash

#

foruser in $@; do

useradd$user

echo$user | passwd --stdin $user &> /dev/null

done

echo"Total users: $#."

 

算术运算:

如何定义整型变量?

(1)let Var_Name=INTEGER_VALUE

letnum1=3

 

(2)declare -i Var_Name=INTEGER_VALUE

declare-i num2=6

 

注意:即使没有定义变量类型为整型,字符型的整数依然可以参与算术运算,是因为bash会进行隐式的数据类型转换

 

bash的算术运算:

(1)let: let Var_Name=ARITHMATIC_EXPRESSION

例:let A=1let B=2let C=$A+$B  ==>C=3

         A=1 ; B=2 ; C=$A+$B==>C=1+2

(2)$[]: Var_Name=$[ARITHMATIC_EXPRESSION]

例:C=$[$A+$B]

(3) $(())Var_Name=$((ARITHMATIC_EXPRESSION))

例: C=$(($A+$B))

(4) expr命令:Var_Name=$(exprEXPRESSION)

  Var_Name=$(expr ARGU1 operator $ARGU2)

例:C=`expr$A+$B`

 

算术运算:+-*/%:取模,取余数;**:幂运算

 

条件测试:

bash命令执行结束后有退出状态码:

0-255

0:成功执行

1-255:执行失败

 

可以用 exit 数字 [例:exit 0] 设置脚本退出时的状态码

 

 

整数测试:

双目操作符:需要两个操作数;

$A -gt $B: 大于

-ge:大于等于

-lt:小于

-le:小于等于

-ne:不等于

-eq:等于

字符串测试:$str1, $str2

双目:

"$str1" >"$str2": 测试str1是否大于str2,是则为真,不是则为假;

<:是否小于;

==: 是否等于;

!=: 是否不等于;

 

单目:

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

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

 

模式匹配测试:

"$str1"=~PATTERN:

如果str1字符串能够被后面的模式所匹配则为真,否则为假;

练习:写一个脚本,完成如下功能

(1) 传递一个磁盘设备文件路径给脚本,判断此设备是否存在;

(2) 如果存在,则显示此设备上的所有分区信息;

#!/bin/bash

#

if! [[ "$1" =~ ^/dev/[sh]d[a-z]$ ]]; then

   echo "Not a device file."

   exit 1

fi

iffdisk -l /dev/[hs]d[a-z] | grep "^Disk" | grep "$1"&> /dev/null; then

    fdisk -l $1

else

    echo "No such disk."

fi

  文件测试:$file

存在性及类型测试:

-e $file: 文件是否存在,存在为真,否则为假;

-a $file: 同上;

-f $file: 文件是否存在且为普通文件,是则为真,不是则假;

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

-h|-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:当前用户是否属于文件的属组;

 

文件时间戳相关:

-N $file:文件自从最近一次的读访问之后,是否又被修改过;

 

$f1 -nt $f2: f1是否新于f2

$f1 -ot $f2: f1是否旧于f2

$f1 -ef $f2: f1f2是否为同一个文件的两个硬链接;

组合条件测试:

组合测试条件1:组合测试表达式

或:-o, [ COND_EXPR1 -o COND_EXPR2 ]

与:-a

非:!         

例:["$hname" == "localhost" -o "$hname" =="(none)" ]

 

组合测试条件2:组合测试语句

与:&&

或:||

非:!

例:["$hname" == "localhost" ] || [ "$hname" =="(none)" ]

 

if语句的语法:

(1)单分支:

if 判断条件; then

分支

fi

(2)双分支

if 判断条件; then

分支1

else

分支2

fi

(3)多分支

if 判断条件1;then

elif 判断条件2;then

elif 判断条件3;then

else

fi

注意:if语句只有在判断两个值比较时,才用[ ]

  若是if`id root &>/dev/null` ;then

  echo root

  fi

             不用加[ ]

if [ $A -lt $B ];then

echo $A

      elif [ $str1 == $str2];then

echo $str1

      fi

 

 

case语句的语法:

case变量引用in

PATTERN1)

分支1

;;

PATTERN2)

分支2

;;

...

*)    *表示都不匹配时                            

分支n

;;

esac

 

注意:

PATTERN可使用通配符:

*: 任意长度任意字符;

?: 任意单个字符;

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

a|b|c:或者

 

例:

练习:写一个脚本

(1) 能接受四个参数:start,stop, restart, status

start: 输出“starting脚本名 finished.”

...

(2) 其它任意参数,均报错退出;

#!/bin/bash

#

svc=$(basename$0)

lockfile=/tmp/${svc}

case$1 in

start)

    if [ -e $lockfile ]; then

         echo "$svc is running"

         exit 0

    else

        echo "starting $svcfinished."

        touch $lockfile && exit 0

    fi

    ;;

stop)

    echo "stopping $svc finished."

    ;;

restart)

    echo "restarting $svc finished."

    ;;

status)

    echo "running or stopped."

    ;;

*)

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

    exit 1

    ;;

esac

 

交互式编程 read

read -p "注释信息" 变量名

例: read -p "Input one Number :" num1

read -t # :指定超时时间

例: read -t 5 "Input one Number :" num1    [5s内用户没输入,则继续向下进行,而num1为空]

-a 数组;-A 关联数组

 

练习:写一个脚本,提示用户输入一个用户名;

在用户存在时,判断其UID是否同GID

同,则显示“Good guy”不同,则显示“Bad guy”

 

#!/bin/bash

#

read-p "Plz enter a user name: " name

 

!id $name &> /dev/null && echo "No this user."&& exit 1

 

if[ `id -u $name` == `id -g $name` ]; then

    echo "Good guy"

else

    echo "Bad guy"

fi

 

while循环语句的语法:

 

whileCONDITION; do

循环体

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

done

 

其中,“CONDITION”,当条件测试结果为“真”,则进入循环;为“假”时,终止循环;循环控制变量要用于CONDITION中,以实现循环条件构建;

 

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

#!/bin/bash

#

declare-i i=1

while[ $i -le 100 ]; do

    let sum+=$i

    let i++

done

echo"$sum"        

 

示例:打印九九乘法法;

#!/bin/bash

#

declare-i i=1

while[ $i -le 9 ]; do

    let j=1

    while [ $j -le $i ]; do

        echo -n -e"${j}X${i}=$[$i*$j]\t"

        let j++

    done

    echo

    let i++

done

 

示例:写一个脚本,判断给定的用户是否登录了当前系统;

(1) 如果登录了,则显示用户登录,脚本终止;

(2) 3秒钟,查看一次用户是否登录;

#!/bin/bash

#

read-p "Plz enter a user name: " name

while! who | grep "^$name\>" &> /dev/null; do

    sleep 3

done

echo"$name is here."        

 

示例:写一个脚本,显示用户选定要查看的信息;

cpu)display cpu info

mem)display memory info

disk)display disk info

quit)quit

非此四项选择,则提示错误,并要求用户重新选择,只到其给出正确的选择为止;

#!/bin/bash

#

cat<< EOF

                cpu) display cpu info

                mem) display memory info

                disk) display disk info

                quit) quit

EOF

read-p "your option: " option

while[ "$option" != 'cpu' -a "$option" != 'mem' -a"$option" != 'disk' -a "$option" != 'quit' ]; do

    echo "Wrong option."

    read -p "your option again: "option

done

case$option in

cpu)

     lscpu ;;

mem)

     free ;;

disk)

     fdisk -l /dev/[sh]d[a-z] ;;

quit)

     exit 0

esac        

 while循环的特殊用法:

 

(a)用于遍历指定文件的每一行;

while read变量名; do

循环体

done < /PATH/TO/SOMEFILE

 

变量用于保存一行数据;直到遇到文件尾,数据终止,循环退出;

 

示例:找出其UID为偶数的用户,显示用户名和其UID

#!/bin/bash

#

[$UID -ne 0 ] && echo "Only root can excute this script."&& exit 1

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        

 

示例:将给定的偶数行内字符统统修改为大写之后输出;

#!/bin/bash

#

declare-i linenumber=1

read-p "Plz enter a file path: " file

while read line; do

    if [$[$linenumber%2] -eq 0 ]; then

        echo -n"$linenumber "

        echo$line |tr 'a-z' 'A-Z'

    fi

    letlinenumber++

done < $file

 

(b) 死循环

while true; do

循环体

done

 

循环控制:

break [N]:结束循环;

退出当前循环;N表示循环嵌套层次;

continue [N]:提前结束本轮循环,而直接进入下一轮循环条件判断

 

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

 

#!/bin/bash

#

declare-i evensum=0

declare-i i=0

while[ $i -le 100 ]; do

    let i++

    if [ $[$i%2] -eq 1 ]; then

        continue

    fi

    let evensum+=$i

done

echo"$evensum"

 

示例:判断centos是否登录,如果登录则显示之;否则,每3秒查看centos是否登录;

 

#!/bin/bash

#

user='centos'

whiletrue; do

    if who | grep "^$user\>"&> /dev/null; then

        break

    fi

    sleep 3

done

echo"$user is here."

 

 

until循环语句的语法:

 

until CONDITION; do

循环体

控制变量修正语句

done

 

CONDTION条件为“假”时,执行循环;为“真”时退出循环;while相反

 

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

#!/bin/bash

#

declare-i i=1

declare-i sum=0

until[ $i -gt 100 ]; do

    let sum+=$i

    let i++

done

echo"$sum"

 

函数function的语法:

语法结构:

(1) function 函数名 {

 函数体

       }

 

(2) 函数名() {

 函数体

      }

 

调用函数:函数名        例: checkUser centos [checkUser 为函数名centos 为传入的参数]

 

函数的返回值:

函数执行过程结果向调用者的回馈;

(a) 显式使用echoprintf命令;

(b) 函数中某些命令运行结果产生的输出;

函数退出状态码:

默认其退出码取决于函数体最后执行的那个语句的退出状态码;

自定义退出状态码:

return[N]

0-255

0:成功

1-255: 失败

注意:函数运行时,一旦遇到return语句即会退出整个函数的运行;

 

函数可接受参数:

调用函数时,可以传递参数给函数,传递方法类似向脚本传递参数的方法,例如

函数名 arg1 arg2 arg3...

在函数中可使用$1,$2, ...来调用这些参数;

可在函数中使用特殊变量:$#,$@, $*

 

 

练习:写一个脚本

(1) 用函数实现返回一个用户的UIDSHELL;用户名通过参数传递而来

(2) 提示用户输入一个用户名或输入“quit”退出;

当输入的是用户名,则调用函数显示用户信息;

当用户输入quit,则退出脚本;

进一步地:显示键入的用户相关信息后,再次提醒输出用户名或quit

#!/bin/bash

checkUser(){

userid=`id -u $1`

usershell=`grep"^$1" /etc/passwd |cut -d: -f7`

echo"$1 uid :$userid "

echo"$1 shell :$usershell"

}

whiletrue ;do

read-p "input username: " name

if! [ -z $name ];then

if`id $name&>/dev/null`;then

checkUser $name

elif[ $name == 'q' ];then

exit0

else

echo"Input or quit"

fi

else

echo"Input or quit"

fi

  done

 

 

数组语法:

数组:变量阵列,通过同一个名字进行存取操作;

连续的多个独立的内存空间(元素),每个内存空间相当于一个变量;

数组元素:数组名[索引]

索引:从0开始编号

 

声明数组:

declare-a Array_Name

 

bash的数组支持稀疏格式;

 

数组元素赋值:

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

a_name[index]=value

 

weekday[0]="Sunday"

weekday[1]="Monday"

 

(2) 一次赋值全部元素

weekday=("Sunday""Monday" "Tuesday")

 

(3) 指定索引进行赋值

weekdays=([0]="Sunday"[3]="Thu" [6]="Sat")

 

(4)read -a a_name

 

引用数组元素:${array_name[index]}

 

获取数组长度:${#array[*]}, ${#array[@]}

即数组中元素的个数;

 

练习:写一个脚本,生成10个随机数,保存至数据组中;而后显示数组索引为偶数的元素的值;

 

#!/bin/bash

#

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

    rand[$i]=$RANDOM

    echo ${rand[$i]}

done

 

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

 

fori in `seq 0 2 9`; do

    echo ${rand[$i]}

done

 

 

练习:写一个脚本

定义一个数组,数组元素为/var/log目录下所有以.log结尾的文件的名字;显示每个文件的行数;

 

#!/bin/bash

#

declare-a files

 

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

 

fori in `seq 0 $[${#files[*]}-1]`; do

    wc -l ${files[$i]}

done

 

数组切片:从数组中挑选指定的某个或某些元素:

${array[@]:offset:number}

 

offset: 偏移的元素的个数;

number:要取出的元素的个数;

 

${array[@]:offset}

取出偏移量之后剩余所有的元素;

 

${array[@]}

 

从数组中删除元素:

unsetarray[index]

 

bash4.0版本起支持关联数组:

数组索引可为自定的字符串;

 

定义方法:declare -A array_name

 

练习:写一个脚本,能从所有同学中随机挑选一个同学回答问题;

进一步地:可接受一个参数,做为要挑选的同学的个数;

 

 

bash的字符串处理:

 

字符串切片:${var:offset:length}

 

取出字符串的最后几个字符:${var: -length}

注意:-length之前有空白字符;

 

基于模式取子串:

${var#*word}:自左而右,查找var变量中存储的字符串中第一次出现的由word所指明的字符,删除此字符及其左侧的所有内容;

${var##*word}:自左而右,查找var变量中存储的字符串中最后一次出现的由word所指明的字符,删除此字符及其左侧的所有内容;

${var%word*}:自右而左,查找var变量中存储的字符串中第一次出现的由word所指明的字符,删除此字符及其右侧的所有内容;

${var%%word*}:自右而左,查找var变量中存储的字符串中最后一次出现的由word所指明的字符,删除此字符及其右侧的所有内容;

 

示例:

url="http://www.magedu.com:80"

 

取端口:echo ${url##*:}

取协议:echo ${url%%:*}

 

查找替换:

${var/pattern/replacement}:查找var变量存储的字符中第一次由pattern匹配到的内容,并替换为replacement

${var//pattern/replacement}:查找var变量存储的字符中所有能够由pattern匹配到的内容,并替换为replacement

${var/#pattern/replacement}:查找var变量存储的字符中最开始处能够由pattern匹配到的内容,并替换为replacement

${var/%pattern/replacement}:查找var变量存储的字符中最后位置能够由pattern匹配到的内容,并替换为replacement

 

查找删除:

 

${var/pattern}:查找var变量存储的字符中第一次由pattern匹配到的内容,并删除;

${var//pattern}:查找var变量存储的字符中所有能够由pattern匹配到的内容,并删除;

${var/#pattern}:查找var变量存储的字符中最开始处能够由pattern匹配到的内容,并删除;

${var/%pattern}:查找var变量存储的字符中最后位置能够由pattern匹配到的内容,并删除;

 

字符串大小写转换:

${var^^}:把var变量中的所有小写字母,统统替换为大写;

${var,,}:把var变量中的所有大写字母,统统替换为小写;

 

变量赋值:

${var:-word}:如果变量var为空或未声明,则返回word所表示的字符串;否则,则返回var变量的值;

${var:=word}:如果变量var为空或未声明,则返回word所表示的字符串,并且把word赋值为var变量;否则,则返回var变量的值;

${var:?error}:如果变量var为空或未声明,则返回error为错误信息;否则,则返回var变量的值;

${var:+word}:如果变量var为空或未声明,忽略;否则,则返回word