1、条件判断

条件测试:界定程序执行环境;

实现条件测试方法:

(1) 根据运行的命令的状态结果;

     例:运行id命令,如果成功为真,失败为假

(2) 测试表达式

test+表达式

[ 表达式 ]:命令

[[ 表达式 ]]:关键字

       注:表达式两端需有空格,否则为语法错误

     整数测试:隐含着做数值大小比较,所以不要给变量引用加引用;

$A -gt $B:是否大于;是则为“真”,否则为“假”;

$A -ge $B: 是否大于等于;

$A -lt $B:是否小于;

$A -le $B: 是否小于等于;

$A -eq $B: 是否等于;

$A -ne $B:是否不等于;

     字符串测试:ASCII数值越大,字符比较时其值越大;

"$A" > "$B":是否大于;

"$A" < "$B":是否小于;

"$A" == "$B":是否等于;

"$A" != "$B":是否不等于;

-z "$A":是否为空;空则为“真”,否则为“假”

-n "$A":是否不空;不空则“真”,空则为“假”

        注意:应该使用[[ 表达式 ]]

     文件测试:测试文件的存在性以及属性;

        -e $file: 是否存在;存在则为“真”,否则为“假”;

-a $file: 同上;

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

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

-h $file:是否存在且为符号链接文件;

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


      双目操作符:

$file1 -nt $file2: file1是否新于file2, file1的最近一次的修改时间戳是否晚于file2的;

$file1 -ot $file2: file1是否旧于file2, file1的最近一次的修改时间戳是否早于file2的; 

$file1 -ef $file2:file1与file2是否指向了同一个inode;测试二者是否为同一个文件的硬链接;

      特殊设备:

        /dev/null: 空,bit buckets,吞下所有数据,并直接丢弃;

/dev/zero:吐出一堆0;

2、bash之条件判断(选择执行):

if/then, case


if CONDITION; then        CONDITION:条件

if-true-分支

fi


if CONDITION; then

if-true-分支

else

if-false-分支

fi

       ! CONDITION: 取反

练习:写一个脚本

如果某路径不存在,则将其创建为目录;否则显示其存在,并显示内容类型;

#!/bin/bash
#
filename="/tmp/x/y/z/testdir"
if [ -e $filename ]; then
    echo "$filename exists."
    file $filename
else
    mkdir -p $filename
fi
[root@centos6 tmp]# bash -x 3.sh 
+ filename=/tmp/x/y/z/testdir
+ '[' -e /tmp/x/y/z/testdir ']'
+ mkdir -p /tmp/x/y/z/testdir
[root@centos6 tmp]# tree /tmp
/tmp
├── 1.sh
├── 2.sh
├── 3.sh
├── backup
├── lost+found
├── x
│   └── y
│       └── z
│           └── testdir

脚本参数(位置参数变量):

# ./script.sh /etc/fstab /etc/grub2.cfg
$0          $1          $2

位置参数变量:$1, $2, ...

${10}两位数字以后要加花括号

特殊变量:

$?: 命令的状态结果;

$#: 传递给脚本或函数的参数的个数;

$*和$@: 引用传递给脚本或函数的参数列表;

       shift [n]:轮替

与用户交互:

read命令:

read [options] VAR...

-p "PROMPT"  :添加提示语句

-t timeout   :添加超时时间

示例:由用户输入一个用户名,如果有输入创建,没有输入创建myuser用户

#!/bin/bash
#
read -p "Plz enter a username: " -t 5 username
if [ -z "$username" ]; then
     username="myuser"
fi
if id $username &> /dev/null; then
    echo "$username exists."
else
    useradd $username
fi
[root@centos6 tmp]# bash 4.sh 
please enter a username:myname is exist
[root@centos6 tmp]# bash 4.sh 
please enter a username:test1
[root@centos6 tmp]# id test1
uid=3013(test1) gid=3013(test1) groups=3013(test1)

命令引用:

`COMMAND`, $(COMMAND)

引用命令的执行结果;

示例 

(1) ls `which cat`

[root@centos6 tmp]# ll `which cat`
-rwxr-xr-x. 1 root root 45224 Oct 15  2014 /bin/cat

(2) lines=$(wc -l /etc/fstab | cut -d' ' -f1)

[root@centos6 tmp]# lines=$(wc -l /etc/fstab | cut -d' ' -f1)
[root@centos6 tmp]# echo $lines
20

(3)给一个文件路径输出文件行数

#!/bin/bash
#
if [ -f $1 ]; then
     lines=$(wc -l $1 | cut -d' ' -f1)
     echo "$1 has $lines lines."
else
     echo "$1 not exists or not a file."
fi
[root@centos6 tmp]# ./5.sh /etc/fstab 
/etc/fstab has 20 lines.

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

判断给定的两个数值,孰大孰小;

给定数值的方法:脚本参数,命令交互;

#!/bin/bash
#
read -p "Plz enter two integer: " -t 10 num1 num2
if [ -z "$num1" ]; then
    echo "Plz give two integers."
    exit 1
fi
if [ -z "$num2" ]; then
    echo "Plz give tow integers." 
    exit 1
fi
if [ $num1 -ge $num2 ]; then
    echo "Max: $num1, Min: $num2."
else
    echo "Max: $num2, Min: $num1."
fi

3、循环语句:

for, while, until

循环:将循环体代码执行0、1或多次;

进入条件:进入循环的条件;

退出条件:循环终止的条件;

格式:

for VARIABLE in LIST; do

循环体

done

        LIST:是由一个或多个空格或换行符分隔开的字符串组成;

        把列表的每个字符串逐个赋值给VARIABLE表示的变量;

例:

for username in user1 user2 user3; do

循环体

done

进入条件:列表非空;

退出条件:列表遍历结束;

示例:

添加10个用户,user1-user10;

#!/bin/bash
#
for username in user1 user2 user3 user4 user5; do
    if id $username &> /dev/null; then
echo "$username exists."
    else
useradd $username
        echo "Add user $username finished."
    fi
done

LIST的生成方法:

(1) 整数列表

(a) {start..end}

(b) $(seq [start `step` end)

(2) 直接给出列表

(3) glob

(4) 命令生成

示例:数值列表

#!/bin/bash
#
for i in {1..10}; do
    if id user$i &> /dev/null; then
echo "user$i exists."
    else
useradd user$i
        echo "Add user user$i finished."
    fi
done

示例:glob

#!/bin/bash
#
for filename in /var/log/*; do
    file $filename
done

示例:命令生成列表

#!/bin/bash
#
for username in $(cut -d: -f1 /etc/passwd); do
    echo "$username primary group: $(id -n -g $username)."
done

算术运算:

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

使用格式:

(1) $[$A+$B]

(2) $(($A+$B))

(3) let VARIABLE=$A+$B

(4) VARIABLE=$(expr $A + $B)

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

#!/bin/bash
#
declare -i sum=0
for i in {1..100}; do
   sum=$[$sum+$i]
done
echo $sum

练习:求100以内所有偶数之和;

使用至少三种方法实现;

方法一

#!/bin/bash
#
declare -i sum=0
for i in $(seq 0 2 100); do
    sum=$(($sum+$i))
done
echo "Even sum: $sum."

方法二

#!/bin/bash
#
declare -i sum=0
for i in {1..100}; do
    if [ $[$i%2] -eq 0 ]; then
sum=$[$sum+$i]
    fi
done
echo "Even sum: $sum."

增强型赋值:

+=

sum=$[$sum+$i]

let sum+=$i

-=, *=, /=, %=

let count=$[$count+1]   --> let count+=1 --> let count++

let count=$[$count-1] --> let count-=1 --> let count--

示例:显示/etc目录下所有普通文件列表,而后统计一共有多少个文件;

#!/bin/bash
#
declare -i count=0
for file in /etc/*; do
    if [ -f $file ]; then
 let count++
echo "$count $file"
    fi
done
echo "Total: $count files."

测试表达式:

整数测试:-gt, -lt, -ge, -le, -eq, -ne

字符串测试:==, >, <, !=, -z, -n, =~

注意:

(1) 字符串等会比较测试:[ "$hostname" == 'localhost'  ]

(2) 模式匹配测试:[[ "STRING" =~ PATTERN ]]

4、组合测试条件:

条件间逻辑运算:

与:多个条件要同时满足;

或:多个条件满足其一即可;

非:对指定的条件取反;

表达式组合:

与:` CONDITION1 -a CONDITION2 `

或:` CONDITION1 -o CONDITION2 `

非:[ ! CONDITION ]

命令组合:

与:COMMAND1 && COMMAND2  <-- [ EXPR1 ] && [ EXPR2 ] 

或:COMMAND1 || COMMAND2

非:! COMMAND

短路操作符:&&

false && true = false

false && false = false

true && false = false

true && true = true

if COMMAND1; then
COMMAND2
fi

短路操作符:||

true || true = true

true || false = true

false || true = true

false || false = false

if ! COMMAND1; then
COMMAND2
fi
COMMAND1 && COMMAND2 || COMMAND3
if COMMAND1; then
COMMAND2
else
COMMAND3
fi

示例:写一个脚本实现如下功能;

获取当前主机的主机名;

如果当前主机的主机名为空,或者为localhost,则将其修改为www.magedu.com

#!/bin/bash
#
hostname=$(hostname)
if [ -z "$hostname" -o "$hostname" == "localhost" ]; then
    hostname www.magedu.com
fi

练习:写一个脚本,打印九九乘法表;

循环嵌套

1X1=1

1X2=2 2X2=4

1X3=3 2X3=6 3X3=9

#!/bin/bash
#
for j in {1..9}; do
    for i in $(seq 1 $j); do
echo -n -e "${i}X${j}=$[$i*$j]\t"
    done
    echo 
done

5、多分支的if语句:

单分支:

if CONDITION; then

if-true-分支

fi

双分支:

if CONDITION; then

if-true-分支

else

if-false-分支

fi

多分支:

if CONDITION1; then

if-CONDITION1-true-分支

elif CONDTION2; then

if-CONDITIO2-true-分支

...

else

if-ALL-false-分支

fi

示例:通过脚本参数传递一个文件路径给脚本,判断其类型;

#!/bin/bash
#
if [ $# -lt 1 ]; then
    echo "Usage: $0 <path>"
    exit 1
fi
if [ -f $1 ]; then
    echo "Rgular file."
elif [ -d $1 ]; then
    echo "Directory."
elif [ -h $1 ]; then
    echo "Symbolic link."
elif [ -b $1 ]; then
    echo "Block special."
elif [ -c $1 ]; then
    echo "Charactoer special."
elif [ -S $1 ]; then
    echo "Socket file."
else
    echo "file not exist or unknown type."
fi

示例:脚本可接受四个参数

start: 创建文件/var/lock/subsys/SCRIPT_NAME

stop: 删除此文件

restart: 删除此文件并重新创建

status: 如果文件存在,显示为"running",否则,显示为"stopped"

basename命令:

取得路径的基名;


#!/bin/bash
#
prog=$(basename $0)
lockfile="/var/lock/subsys/$prog"
#echo $lockfile
if [ $# -lt 1 ]; then
    echo "Usage: $prog start|stop|restart|status"
    exit 1
fi
if [ "$1" == "start" ]; then
    if [ -f $lockfile ]; then
echo "$prog is started yet."
    else
touch $lockfile && echo "Starting $prog ok..." || echo "Starting $prog failed..."
    fi
elif [ "$1" == 'stop' ]; then
    if [ -f $lockfile ]; then
rm -f $lockfile && echo "Stop $prog ok...." || echo "Stop $prog failed..."
    else
echo "$prog is stopped yet."
    fi
elif [ "$1" == 'restart' ]; then
    if [ -f $lockfile ]; then
rm -f $lockfile && touch $lockfile && echo "Restarting $porg ok..."
    else
touch $lockfile && echo "$prog is stopped, Starting $prog ok...."
    fi
elif [ "$1" == 'status' ]; then
    if [ -f $lockfile ]; then
echo "Running..."
    else
echo "Stopped..."
    fi
else
    echo "Usage: $prog start|stop|restart|sttus"
    exit 1
fi

6、case语句

简洁版多分支if语句;

使用场景:判断某变量的值是否为多种情形中的一种时使用;

语法:

case $VARIABLE in 

PATTERN1)

分支1

;;

PATTERN2)

分支2

;;

PATTERN3)

分支3

;;

...

*)

分支n

;;

esac


PATTERN可使用glob模式的通配符:

*: 任意长度的任意字符;

?: 任意单个字符;

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

a|b: 多选1;

示例:提示键入任意一个字符;判断其类型

#!/bin/bash
#
read -p "Plz enter a character: " char
case $char in
[a-z])
     echo "A character."
     ;;
[0-9])
     echo "A digit."
     ;;
*)
     echo "A special character."
     ;;
esac

示例:脚本可接受四个参数

start: 创建文件/var/lock/subsys/SCRIPT_NAME

stop: 删除此文件

restart: 删除此文件并重新创建

status: 如果文件存在,显示为"running",否则,显示为"stopped"

#!/bin/bash
#
prog=$(basename $0)
lockfile="/var/lock/subsys/$prog"
#echo $lockfile
if [ $# -lt 1 ]; then
    echo "Usage: $prog start|stop|restart|status"
    exit 1
fi
case $1 in
start)
    if [ -f $lockfile ]; then
echo "$prog is started yet."
    else
touch $lockfile && echo "Starting $prog ok..." || echo "Starting $prog failed..."
    fi
    ;;
stop)
    if [ -f $lockfile ]; then
rm -f $lockfile && echo "Stop $prog ok...." || echo "Stop $prog failed..."
    else
echo "$prog is stopped yet."
    fi
    ;;
restart)
    if [ -f $lockfile ]; then
rm -f $lockfile && touch $lockfile && echo "Restarting $porg ok..."
    else
touch $lockfile && echo "$prog is stopped, Starting $prog ok...."
    fi
    ;;
status)
    if [ -f $lockfile ]; then
echo "Running..."
    else
echo "Stopped..."
    fi
    ;;
*)
    echo "Usage: $prog start|stop|restart|sttus"
    exit 1
esac

7、流程控制:

循环语句:for, while, until

(1)while循环:

while CONDTION; do
循环体
done

进入条件:当CONDITION为“真”;

退出条件:当CONDITION为“假”;

while CONDITION; do
循环体
控制变量的修正表达式
done

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

#!/bin/bash
#
declare -i sum=0
declare -i i=1
while [ $i -le 100 ]; do
    let sum+=$i
    let i++
done
echo "Sum: $sum."

示例:打印九九乘法表

#!/bin/bash
#
declare -i i=1
declare -i j=1
while [ $j -le 9 ]; do
    while [ $i -le $j ]; do
echo -e -n "${i}X${j}=$[$i*$j]\t"
let i++
    done
    echo
    let i=1
    let j++
done

(2)unitl循环:

until CONDITION; do
循环体
循环控制变量的修正表达式
done

进入条件:当CONDITION为“假”时

退出条件:当CONDITION为“真”时

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

#!/bin/bash
#
declare -i sum=0
declare -i i=1
until [ $i -gt 100 ]; do
    let sum+=$i
    let i++
done
echo "Sum: $sum."

循环控制:

continue [n]:提前结束本轮循环,而直接进入下一轮;

break [n]:提前结束循环;


while循环:

while CONDITION; do

.......

if CONDITION2; then

break [n]

fi

done


while CONDITION; do

......

if CONDITION2; then

continue [n]

fi

......

done

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

#!/bin/bash
#
declare -i sum=0
declare -i i=0
while [ $i -le 100 ]; do
    let i++
    if [ $[$i%2] -eq 1 ]; then
echo "$i is a odd."
        continue
    fi
    let sum+=$i
done
echo "Sum: $sum."

死循环:

while true; do

循环体

if CONDTION; then

break

fi

done


until false; do

循环体

if CONDITION; then

break

fi

done


示例:每隔3秒钟查看当前系统上是否有名为“gentoo”的用户登录;

如果某次查看gentoo登录了,则显示gentoo已经登录;

如果未登录,就显示仍然未来,并显示这是已经是第多少次查看了;

#!/bin/bash
#
username=$1
declare -i count=0
while true; do
    if who | grep "^$username" &> /dev/null; then 
echo "$username is logged."
 break
    else
  let count++
echo "$count $username is not login."
    fi
    sleep 3
done
#!/bin/bash
#
declare -i count=0
username=$1
if [ $# -lt 1 ]; then
    echo "At lease one argument."
    exit 1
fi
if ! id $username &> /dev/null; then
    echo "No such user."
    exit 2
fi
until who | grep "^$username" &> /dev/null; do
    let count++
    echo "$count $username is not login."
    sleep 3
done
echo "$username is logged on."

while循环的特殊用法:

遍历文件的每一行:

while read VARIABLE; do

循环体

done < /PATH/FROM/SOME_FILE


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

#!/bin/bash
#
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

for循环的特殊用法:

for ((expr1;expr2;expr3)); do

循环体

done


expr1: 定义控制变量,并初始赋值;

expr2: 循环控制条件;

进入条件:控制条件为“真”

退出条件:控制条件为“假”

expr3: 修正控制变量


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

#!/bin/bash
#
declare -i sum=0
for ((i=1;i<=100;i++)); do
    let sum+=$i
done
echo "Sum: $sum."

8、函数:

function: 功能

把一段具有独立功能代码封装在一起,并给予命名;后续用到时,可直接通过给定函数名来调用整体代码;

函数作用:

   代码重用;

   模块化编程;

函数的使用方法:

   先定义:编写函数代码

   后调用:给出函数名,还可按需传递参数


定义方法:

(1) function f_name {

函数体

}


(2) f_name() {

函数体

}


调用函数:

f_name [argu1, argu2, ...]


自定义函数状态返回值:

return [#]

0: 成功

1-255:失败


注意:函数代码执行时,一旦遇到return,函数代码终止运行,函数返回;


示例:此前的服务脚本

#!/bin/bash
#
prog=$(basename $0)
lockfile="/var/lock/subsys/$prog"
#echo $lockfile
if [ $# -lt 1 ]; then
    echo "Usage: $prog start|stop|restart|status"
    exit 1
fi
start() {
    if [ -f $lockfile ]; then
echo "$prog is started yet."
    else
touch $lockfile && echo "Starting $prog ok..." || echo "Starting $prog failed..."
    fi
}
stop() {
    if [ -f $lockfile ]; then
rm -f $lockfile && echo "Stop $prog ok...." || echo "Stop $prog failed..."
    else
echo "$prog is stopped yet."
    fi
}
restart() {
    if [ -f $lockfile ]; then
rm -f $lockfile && touch $lockfile && echo "Restarting $porg ok..."
    else
touch $lockfile && echo "$prog is stopped, Starting $prog ok...."
    fi
}
status() {
    if [ -f $lockfile ]; then
echo "Running..."
    else
echo "Stopped..."
    fi
}
case $1 in
start)
    start 
    ;;
stop)
    stop
    ;;
restart)
    restart
    ;;
status)
    status
    ;;
*)
    echo "Usage: $prog start|stop|restart|sttus"
    exit 1
esac

示例:判断用户的ID号的奇偶性;

#!/bin/bash
#
evenid() {
    if [ $# -lt 1 ]; then
return 1
    fi
    if ! id $1 &> /dev/null; then 
return 2
    fi
    userid=$(id -u $1)
    if [ $[$userid%2] -eq 0 ]; then
echo "$1, Even user ID."
    else
echo "$1, Odd user ID."
    fi
}
evenid root
evenid 
echo $?
evenid rooooooooooooot
echo $?

模块化编程

功能:把脚本文件中的代码分隔为多段,放在不同的文件中

假设/root/bin/srv目录有两个文件:

(1) 函数文件

(2) 脚本文件


为脚本使用配置文件

一个文件中只定义变量

脚本文件source此变量定义的文件


变量的作用域:

局部变量:

local VARIABLE=value


存活时间:

函数执行开始,至函数返回结束;