shell 脚本编程

变量赋值

(1) 直接赋值,如name="root"

(2) 将变量值赋值,如username="$USER"

(3) 将命令的结果赋值,如date=$(date +%F),$()可以用一对反向单引号代替``

  • 变量弱引用" “,双引号内的变量引用会被替换为变量值
  • 变量强引用’ ‘,单引号内的变量引用会保持原样的字符串

bash中的变量的种类

根据变量的生效范围等标准:

本地变量:生效范围为当前shell进程;对当前shell之外的其它shell进程,包括当前shell的子shell进程均无效;

环境变量:生效范围为当前shell进程及其子进程;

局部变量:生效范围为当前shell进程中某代码片断(通常指函数);

位置变量:

1$1, $2, ...来表示,用于让脚本在脚本代码中调用通过命令行传递给它的参数;

特殊变量:

1$?, $0, $*, $@, $#

本地变量

  • 变量赋值 name=value
  • 变量引用
    1 ${name}, $name
    
  • 查看变量:set
  • 撤销变量 : unset name
  • bash 开启子shell , 子shell 中无法获得父shell中的变量

环境变量

  • 变量声明、赋值:

    • export name=VALUE

    • name=value

      export name

    • declare -x name=VALUE

    • name=value

      declare -x name

    • 变量引用:

    1$name, ${name}
    
  • 显示所有环境变量:

    1env
    2printenv
    3declare -x    
    
  • 销毁: unset name

  • bash有许多内建的环境变量:PATH, SHELL, UID, HISTSIZE, HOME, PWD, OLD, HISTFILE, PS1

     1username=maozhongyu
     2export username
     3bash
     4echo $username    # maozhongyu  子shell中也生效
     5username='maodada'
     6exit
     7echo $username  # maozhongyu    子shell修改全局变量并不会影响父shell的值,
     8bash
     9username='maotiantian'
    10export username 
    11exit
    12echo $username  #maozhongyu  
    13# 子shell重新定义username,并导出变量,但父shell 中的username 还是maozhongyu 另外子shell  unset 变量,无法影响父shell
    

只读变量

只读变量定义后不能修改、不能删除,随所在进程的结束而清除

声明方式2种:

11.readonly name  
2
3#username=maozhongyu
4#readonly username
5#username=maozhongyu1
6-bash: name: readonly variable
7
82. declare -r name
9    unset name //取消

数组变量

 1mytest=(one two three four five)
 2echo $mytest # one
 3echo ${mytest[2]}  #three  
 4echo ${mytest[*]}  # one two three four five
 5mytest[2]=seven  # 修改某个下标
 6echo ${mytest[*]}  #one two seven four five
 7unset mytest[2]
 8echo ${mytest[*]}  #one two four five  
 9# 下表还是保持删除前的
10echo  ${mytest[3]}   #four
11unset mytest  #删除整个数组

位置变量和特殊变量

位置变量和特殊变量

 1 $# 是传给脚本的参数个数
 2 $0 是脚本本身的文件名
 3 $1 是脚本后接的第一个参数
 4 $2 是脚本后接的第二个参数
 5 $@ 是传给脚本的所有参数列表,"$1" "$2" "$3" … "$n"
 6 $* 是以一个单字符串显示传给脚本的所有参数,"$1 $2 $3 … $n"
 7 $$ 是脚本运行的当前进程ID号
 8 $? 是最后运行命令的结束状态码,0表示没有错误,1-255代表出现错误
 9 ${!#} 返回最后一个参数(没有参数就是$0)
10 shift 造成参数变量号码偏移,第二个参数变为$1,以此类推。 
11 - shift [n]:所有参数向左移动n位,$1至$n抛弃 
12 set -- 清空所有位置变量

调试及注意点

检查脚本语法错误 bash -n scriptname

调试执行 bash -x scriptname

注意点:

 11. $10表达的意思是地址变量$1的值和字符0,${10}表达的意思是第10个地址变量。
 2   
 3    使用地址变量时当超出10个时要注意加大括号。
 4
 52. bash和绝对路径及相对路径打开脚本时开了一个子进程,不影响当前进程;source打开脚本时仍
 6   然是在当前进程下,会影响当前进程。所以打开脚本时尽量不要用source。
 7
 83. 脚本里不识别别名
 9
104. 本地变量和环境变量的区别
11
12    本地变量:只对当前shell有效
13   
14    环境变量:父进程定义的变量可以传给子进程,但子进程定义的变量无法传给父进程。
15
165. 如果定义的变量值为多行,echo打印时要加上双引号,否则显示的是一行。
17
186. 声明只读变量后不能修改,不能取消,只能退出后才能取消,如果先定义为只读变量,就无法给只读变量赋值了。
19
207. 小括号的作用
21
22    小括号相当于开了一个子进程,只对当前括号里的环境有效,外面的环境相当于父进程,父进程定义的变量可以传给小括号,而小括号内定义的变量无法传给外面的父进程。

shell脚本的输入输出

echo

语法:echo [-neE][字符串]

echo会将输入的字符串送往标准输出。输出的字符串间以空白字符隔开, 并在最后加上换行号

选项参数

  • -E (默认)不支持\解释功能
  • -n 不自动换行
  • e 启用\字符的解释功能

输出颜色,\033[0m 关闭颜色,恢复正常

1➜  ~ echo '\033[34mhello\033[0m'
2hello
3➜  ~ echo '\033[31mhello\033[0m'
4hello
5➜  ~ echo '\033[32mhello\033[0m'
6hello

printf

1name=maozhongyu
2age=12
3printf "name=%s,age=%d" $name $age

read

-p 指定要显示的提示

-s 静默输入,用于密码输入

-n N 指定输入的字符长度N

-d char 自定输入的结束符

-t N timeout为N秒

read从标准输入读入多个单词,每个单词一个变量,若变量个数少于单词数,则多出的单词也赋给最后一个变量

 1read  name
 2# 输入
 3echo $name
 4
 5echo -n "Enter a user name:"; read name
 6read -p  "Enter a user name:"   name
 7#maozhongyu
 8
 9echo  $name
10read -p "enter  a user name " -t 5   name    ##超时,name 为 空

«

1cat <<EOF
2	内容
3EOF

退出状态码

  • 进程通过退出状态报告进程运行成功或失败
  • echo $?查看上一条命令执行的退出状态
  • 0代表运行成功,1-255代表运行失败
  • exit [n] 自定义退出状态码
  • 如果未指定退出状态码,脚本运行结束后的退出状态码为执行脚本最后一条命令的退出状态码

算术运算

运算符号:+(加),-(减),*(乘),/(除),%(取余),**(乘方)

 11. let varName=算术表达式
 2num1=7;
 3num2=6;
 4let sum=$num1+$num2;
 5echo $sum;
 6
 72. varName=$[算术表达式]
 8num1=7;
 9num2=6;
10varName=$[$num1+$num2]
11echo  $varName 
12
133. varName=$((算术表达式))
14
154. varName=`expr $num1 + $num2`
16# 注意空格 必须空
17
185.declare -i sum=0;

bash内建有随机数生成器$RANDOM,可以用于生成一定范围的随机数,

如生成2-50的随机数

1a=$[$RANDOM%49+2]
2echo $a

增强型赋值:+=,-=,*=,/=,%=,例如a+=3等于a=a+3,其他符号类似

自增、自减:++,–,例如a++,b–

自增自减运算符在变量前后可能导致不同的结果,运算符在前如++a表示对变量a自增1再进行其他操作,运算符在后如b–表示对变量b先进行其他操作再自减1

字符串操作

输出字符串的长度

1a='abc'
2echo ${#a}  #3
3b=`expr length $a` #3

取子串的操作

1`expr substr $string $position $length`   位置编号从1开始
2`echo ${string:position:length}`          位置从0开始
3
4a='abc'
5expr substr $a 1 1    //a
6echo ${string:0:1}    //a 下标从0开始    

字符串连接

1str1='abc'
2str2='def'
3echo $str1$str2

字符串替换

1str1='you and you abcdef'
2echo ${str1/you/You}  #只替换一次
3echo ${str1//you/You} #全部替换

字符串切片

1${var:offset:number}
2取字符串的子串;
3取字符趾的最右侧的几个字符:${var:  -length}
4注意:冒号后必须有一个空白字符;

基于模式取子串

${var#*word}:其中word是指定的分隔符;功能:自左而右,查找var变量所存储的字符串中,第一次出现的word分隔符,删除字符串开头至此分隔符之间的所有字符;

${var##*word}:其中word是指定的分隔符;功能:自左而右,查找var变量所存储的字符串中,最后一次出现的word分隔符,删除字符串开头至此分隔符之间的所有字符;

${var%word*}:其中word是指定的分隔符;功能:自右而左,查找var变量所存储的字符串中,第一次出现的word分隔符,删除此分隔符至字符串尾部之间的所有字符;

${var%%word*}:其中word是指定的分隔符;功能:自右而左,查找var变量所存储的字符串中,最后一次出现的word分隔符,删除此分隔符至字符串尾部之间的所有字符;

1mypath="/etc/init.d/functions"
2echo ${mypath##*/} #  functions
3echo ${mypath#*/}  #etc/init.d/functions
4echo ${mypath%/*} # /etc/init.d
5
6url=http://www.crblog.cc:80
7echo ${url##*:} # 80
8echo ${url%%:*} # http

查找删除

${var/PATTERN}:以PATTERN为模式查找var字符串中第一次的匹配,并删除之;

${var//PATERN}

${var/#PATTERN}

${var/%PATTERN}

字符大小写转换

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

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

变量赋值 (默认值)

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

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

${var:+VALUE}:如果var变量不空,则返回VALUE;

${var:?ERROR_INFO}:如果var为空,或未设置,那么返回ERROR_INFO为错误提示;否则,返回var值;

比较操作

1test 123 -eq 1234
2echo $?     # 比较成功返回0

逻辑运算

逻辑运算的结果只有true或false,但是不同情况下对true或false的数字描述不同

与运算:只有均为true,结果才为true,否则结果均为false

或运算:只有均为false,结果才为false,否则结果均为true

非运算:true取非结果为false,false取非结果为true

短路与 &&:第一个为0,第二个跳过计算,结果一定为0; 第一个为1,继续计算第二个,才能计算出结果

短路或 ||: 第一个为1,第二个跳过计算,结果一定为1; 第一个为0,继续计算第二个,才能计算出结果

异或^:相同为假,不同为真,异或可以用于交换两个值

1a=5;b=3
2a=$[a^b]
3b=$[a^b]
4a=$[a^b]
5echo $a  #3
6echo $b #5

条件测试

 1test expression
 2数值比较
 3字符串比较
 4文件比较
 5
 6[ expression]
 7数值比较
 8字符串比较
 9文件比较
10
11
12((expression ))    
13
14expression:任意的数学赋值或比较表达式 如果表达式中有大于 小于符号 不需要转义。
15
16[[ expression ]]
17
18使用了test命令中采用的标准字符串比较, 但它提供了test命令未提供的另一个特性-模式匹配 ,括号内的expression前后都需要有空格。

数值比较

1n1 -eq  n2 等于
2n1 -ge  n2 大于等于
3n1 -gt  n2 大于
4n1 -le  n2 小于等于
5n1 -lt  n2 小于
6n1 -ne  n2 是否不等于
7-v VAR 变量VAR是否设置

字符串比较

 1test $str1=$str2
 2str1  == str2 相等
 3str1 != str2
 4str1 < str2  str是否比str2小 (在bash中小写字母大于大写字母) 
 5str1 > str2 
 6-n str1    长度是否非0 
 7-z str1    长度是否为0 
 8=~  左侧是字符串,右侧是一个模式,判定左侧的字符串能否被右侧的模式所匹配,
 9    通常只在[[]]中使用模式中可以使用行首,行尾锚定符;但模式不要加引号
10    
11notice: [ $val1 \< $val2 ]   小于符号要转义

字符串测试中的操作数都需要打引号,才能确保结果正确

可以看出变量var非空时为真,空时为假

变量abc为空,测试时使用-n选项应该非空为真,空为假,不会执行第二条命令打印true。但是当变量未有引号包含时,执行打印true,显然不对。变量有引号包含后,第二条命令不运行,符合预期。所以在字符串测试中一定要对变量打引号。

文件测试

  • -a file 测试文件是否存在
  • -e file 测试文件是否存在
  • -f /path/to/file 测试是否是普通文件
  • -d /path/to/somefile 测试是否是目录文件
  • -b /path/to/somefile 测试文件是否存在并且是否是一个块设备文件
  • -c /path/to/comefile 字符 设备文件;
  • -p /path/to/somefile :管道文件;
  • -s /path/to/somefile 测试文件是否存在并且不空
  • -S /path/to/somefile 测试文件是否为套接字文件;
  • -h|-L /path/to/somefile 测试文件是否存在并且为链接文件

文件权限测试

  • -r /path/to/somefile 测试其执行者是否对此文件有读取权限
  • -w /path/to/somefile 写权限
  • -x /path/to/somefile 执行权限

文件特殊权限测试

  • -u 是否存在且拥有suid权限
  • -g 是否存在且拥有sgid权限
  • -k 是否存在且拥有sticky权限

文件属性测试

  • -O 文件上一次被读取后是否被修改过,即mtime是否新于atime
  • -U 当前用户是否为文件属主
  • -G 当前用户是否为文件属组

双目测试

  • file1 -ef file2 两个文件是否属于相同分区同一个inode号
  • file1 -nt file2 file1是否新于file2,比较mtime
  • file1 -ot file2 file1是否旧于file2,比较mtime

bash展开命令行的顺序

把命令行分成单个命令词

展开别名

展开大括号的声明 {}

展开波浪符声明 ~

命令替换 $()和``

再次把命令行分成命令词

展开文件通配 *、?、[abc]等等

准备I/0重导向 <、>

运行命令

bash的退出任务

退出任务保存在~/bash_logout文件中

在退出登录的shell时运行~/bash_logout文件

可用于退出后创建自动备份、清除临时文件等功能

选择执行:if 语句

 1单分支:
 2if 判断条件; then
 3      判断条件为真的代码
 4fi
 5
 6双分支:
 7if 判断条件; then
 8
 9      判断条件为真的代码
10else
11      判断条件为假的代码
12fi
13
14多分支:逐条件判断,直到满足条件执行分支代码,忽略之后的条件判断
15if 判断条件1; then 
16      判断条件1为真的代码 
17elif 判断条件2; then
18      判断条件2为真的代码
19elif 判断条件3; then
20      判断条件3为真的代码
21... 
22else 
23      上述所有条件为假的代码
24fi

case 语句

 1case 变量引用 in 
 2PAT1)
 3        分支语句1
 4        ;;
 5PAT2)
 6        分支语句2
 7        ;;
 8PAT3)
 9        分支语句3
10        ;;
11...
12*)
13        默认分支语句
14        ;;
15esac
16
17PAT:支持glob通配符
18*:任意长度任意字符
19?:任意单个字符
20[]:指定范围内的任意单个字符
21a|b:a或b

for 语句

循环结构的基本组成:初始值、循环开始和结束条件、循环体

语法:

1for 变量名 in 列表; do
2循环体
3done
  • 列表生成方式: 直接给出列表

    整数列表: {start..end}, {start..end…step}

    返回列表的命令:$(COMMAND) 或 COMMAND,如 $(seq start step end)

  • 使用glob

  • 变量引用:

    1$@, $*
    
  • 特殊用法(双小括号方法):

    双小括号法:用两组括号嵌套(()),可以实现C语言风格变量操作,如

  • let i++等同于((i++))

  • for 循环的特殊格式

    语法:

    1for ((控制变量初始化;条件判断表达式;控制变量的修正表达式)); do
    2    循环体
    3done
    

分隔符

bash shell 默认会将 空格,制表符,换行符 作为字段分隔符

临时修改分隔符

1IFS=$'\n'    #那么只采用换行符做分隔了。
2IFS=:   # 采用冒号做分隔
3IFS=$'\n':;"    #那么 换行符,冒号,封号,双引号 都是分隔符号

处理代码较大的脚本,分隔用到多次。

1IFS.OLD=$IFS
2IFS=$'\n'
3# 代码块
4# 改回原来的
5IFS=$IFS.OLD

循环控制:while 语句和until 语句

1while CONDITON ; do
2      循环体
3done
 1until CONDITION ; do
 2      循环体
 3done
 4CONDITION循环控制条件
 5值为false时执行循环体,当值为true时,终止循环
 6
 7进入条件:CONDITION=false
 8退出条件:CONDITION=true
 9
10无限循环:
11until false; do
12      COMMANDs
13done

循环控制:continue, break, shift语句

  • continue 语句

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

    最内层为第1层

    不写层数默认为第1层

  • break 语句

    break [N]:提前结束第N层循环

    最内层为第1层

    不写层数默认为第1层

  • shift 语句

    语法:shift [N]

    用于将参数列表左移N次,缺省为左移一次

    参数列表一旦被移动,最左端的那个参数就从列表中删除

    while 循环遍历位置参量列表时,常用到shift

函数:function

函数的生命周期:每次被调用时创建,返回时终止;

其状态返回结果为函数体中运行的最后一条命令的状态结果;

自定义状态返回值,需要使用:return

return [0-255]

  • 0: 成功
  • 1-255: 失败

语法

1语法一:
2    function  f_name  {
3        ...函数体...
4    }
5    
6语法二:
7    f_name()  {
8        ...函数体...
9    }

例子:

给定一个用户名,取得用户的id号和默认shell;

 1userinfo() {
 2    if id "$username" &> /dev/null; then
 3        grep "^$username\>" /etc/passwd | cut -d: -f3,7
 4    else
 5        echo "No such user."
 6    fi
 7}
 8username=$1
 9userinfo
10username=$2
11userinfo	

函数返回值

(1) 使用echo或printf命令进行输出;

(2) 函数体中调用的命令的执行结果;

 函数的退出状态码:

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

(2) 自定义:return

函数可以接受参数:

传递参数给函数:

在函数体中当中,可以使用$1,$2, ...引用传递给函数的参数;还可以函数中使用$*或$@引用所有参数,$#引用传递的参数的个数;
		
在调用函数时,在函数名后面以空白符分隔给定参数列表即可,例如,testfunc  arg1 arg2 arg3 ...

函数递归

函数直接或间接调用自身;
			
10!=10*9!=10*9*8!=10*9*8*7!=...

n*(n-1)!=n*(n-1)*(n-2)!=
1fact() {
2    if [ $1 -eq 0 -o $1 -eq 1 ]; then
3        echo 1
4    else
5        echo $[$1*$(fact $[$1-1])]
6    fi
7}
8fact $1					

select 语句

1select variable in list; do
2      循环体命令
3done

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

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

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

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

select经常和case联合使用

实验:实现一个菜单,输入菜单编号显示价格,不存在的菜单号显示"wrong menu"并退出

1序号	菜名	        价格
21	lamian	    10
32	huimian	    10
43	yangroutang	20
54	gaifan	    15
65	jiaozi	    20
 1#! /bin/bash
 2PS3="please type the menu num: "          # 环境变量PS3指定提示符格式
 3select menu in lamian huimian rangroutang gaifan jiaozi; do
 4        case $REPLY in          # 用户的输入自动存入REPLY变量中
 5        1)  
 6                echo "the price of lamian is \$10"
 7                ;;  
 8        2)  
 9                echo "the price of huimian is \$10"
10                ;;  
11        3)  
12                echo "the price of rangroutang is \$20"
13                ;;  
14        4)  
15                echo "the price of gaifan is \$15"
16                ;;  
17        5)  
18                echo "the price of jiaozi is \$20"
19                ;;  
20        *)  
21                echo "wrong menu"
22                break          # select语句默认无限循环,需要有break命令退出循环
23                ;;  
24        esac
25done

数组

1数组名[索引]
2
3${ARRAY_NAME[INDEX]}

注意:bash-4及之后的版本,支持自定义索引格式,而不仅仅是0,1,2,…数字格式;此类数组称之为“关联数组”

声明数组

1declare  -a  NAME:声明索引数组;
2declare  -A  NAME:声明关联数组;

数组中元素的赋值方式

1(1) 一次只赋值一个元素;
2    ARRAY_NAME[INDEX]=value
3(2) 一次赋值全部元素;
4    ARRAY_NAME=("VAL1"  "VAL2"  "VAL3"  ...)
5(3) 只赋值特定元素;
6    ARRAY_NAME=([0]="VAL1"  [3]="VAL4" ...)
7    注意:bash支持稀疏格式的数组;
8(4) read  -a  ARRAY_NAME

引用数组中的元素

1${ARRAY_NAME[INDEX]}    注意:引用时,只给数组名,表示引用下标为0的元素;
2数组的长度(数组中元素的个数):
3    ${#ARRAY_NAME[*]}
4	${#ARRAY_NAME[@]}

引用数组中的所有元素

1${ARRAY_NAME[*]}
2${ARRAY_NAME[@]}

数组元素切片

1${ARRAY_NAME[@]:offset:number}
2
3offset:要路过的元素个数;
4
5number:要取出的元素个数;省略number时,表示取偏移量之后的所有元素;

数组添加,删除元素

向非稀疏格式数组中追加元素:

ARRAY_NAME[${#ARRAY_NAME[*]}]=

删除数组中的某元素:

unset ARRAY[INDEX]

关联数组

1declare  -A  ARRAY_NAME
2ARRAY_NAME=([index_name1]="value1"  [index_name2]="value2" ...)

数组练习

生成10个随机数,并找出其中的最大值

1declare -a  rand
2declare -i max=0
3
4for i in {0..9}; do
5    rand[$i]=$RANDOM
6    echo ${rand[$i]}
7    [ ${rand[$i]} -gt $max ] && max=${rand[$i]}
8done 
9echo "MAX: $max"	

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

 1#!/bin/bash
 2declare -a files
 3files=(/var/log/*.log)
 4declare -i lines=0
 5for i in $(seq 0 $[${#files[*]}-1]); do
 6    if [ $[$i%2] -eq 0 ]; then
 7        let lines+=$(wc -l ${files[$i]} | cut -d' ' -f1)
 8    fi
 9done
10echo "Lines: $lines.

在bash中使用ACSII颜色

 1\033[31m hello \033[0m
 2##m:
 3    左侧#:
 4        3:前景色
 5        4:背景色
 6    右侧#:颜色种类
 7        1, 2, 3, 4, 5, 6, 7
 8
 9#m:
10    加粗、闪烁等功能;
11    
12多种控制符,可组合使用,彼此间用分号隔开;

信号捕捉

列出信号:

  • trap -l
  • kill -l
  • man 7 signal

trap ‘COMMAND’ SIGNALS

常可以进行捕捉的信号: HUP, INT

示例:

 1#!/bin/bash
 2    #
 3    declare -a hosttmpfiles
 4    trap  'mytrap'  INT
 5
 6    mytrap()  {
 7        echo "Quit"
 8        rm -f ${hosttmpfiles[@]}
 9        exit 1
10    }
11
12
13    for i in {1..50}; do
14        tmpfile=$(mktemp /tmp/ping.XXXXXX)
15        if ping -W 1 -c 1 172.16.$i.1 &> /dev/null; then
16            echo "172.16.$i.1 is up" | tee $tmpfile
17        else
18            echo "172.16.$i.1 is down" | tee $tmpfile
19        fi
20        hosttmpfiles[${#hosttmpfiles[*]}]=$tmpfile
21    done
22
23    rm -f ${hosttmpfiles[@]}

练习

ping主机

ping命令去查看172.16.1.1-172.16.67.1范围内的所有主机是否在线;在线的显示为up, 不在线的显示down,分别统计在线主机,及不在线主机数;

分别使用for, while和until循环实现。

 1#!/bin/bash
 2
 3declare -i uphosts=0
 4declare -i downhosts=0
 5
 6for i in {1..17}; do
 7    if ping -W 1 -c 1 172.16.$i.1 &> /dev/null; then
 8        echo "172.16.$i.1 is up."
 9        let uphosts+=1
10    else
11        echo "172.16.$i.1 is down."
12        let downhosts+=1
13    fi
14done
15
16echo "Up hosts: $uphosts, Down hosts: $downhosts."		
 1#!/bin/bash
 2    #
 3declare -i uphosts=0
 4declare -i downhosts=0
 5declare -i i=1
 6
 7hostping() {
 8    if ping -W 1 -c 1 $1 &> /dev/null; then
 9        echo "$1 is up."
10        return 0
11    else
12        echo "$1 is down."
13        return 1
14    fi
15}
16
17while [ $i -le 67 ]; do
18    hostping 172.16.$i.1
19    [ $? -eq 0 ] && let uphosts++ || let downhosts++
20    let i++
21done
22
23echo "Up hosts: $uphosts, Down hosts: $downhosts."	

写一个服务框架脚本

$lockfile, 值/var/lock/subsys/SCRIPT_NAME

(1) 此脚本可接受start, stop, restart, status四个参数之一;

(2) 如果参数非此四者,则提示使用帮助后退出;

(3) start,则创建lockfile,并显示启动;stop,则删除lockfile,并显示停止;restart,则先删除此文件再创建此文件,而后显示重启完成;status,如果lockfile存在,则显示running,否则,则显示为stopped.

 1#!/bin/bash
 2#
 3# chkconfig: - 50 50    //开机启动顺序50 关机启动顺序50  - 默认运行级别2,3,4,5
 4# description: test service script
 5#
 6prog=$(basename $0)
 7lockfile=/var/lock/subsys/$prog
 8
 9case $1  in
10start)
11    if [ -f $lockfile ]; then
12        echo "$prog is running yet."
13    else
14        touch $lockfile
15        [ $? -eq 0 ] && echo "start $prog finshed."
16    fi
17    ;;
18stop)
19    if [ -f $lockfile ]; then
20        rm -f $lockfile
21        [ $? -eq 0 ] && echo "stop $prog finished."
22    else
23        echo "$prog is not running."
24    fi
25    ;;
26restart)
27    if [ -f $lockfile ]; then
28        rm -f $lockfile
29        touch $lockfile
30        echo "restart $prog finished."
31    else
32        touch -f $lockfile
33        echo "start $prog finished."
34    fi
35    ;;
36status)
37    if [ -f $lockfile ]; then
38        echo "$prog is running"
39    else
40        echo "$prog is stopped."
41    fi
42    ;;
43*)
44    echo "Usage: $prog {start|stop|restart|status}"
45    exit 1
46esac

set

1set -e # 遇到错误停止执行   
2set +e #继续执行

declare

1declare -i 指定整形 -r 只读变量  -a 指定数组 -f 指定函数名
2
3declare # 输出系统中定义读变量

文件描述符

 1echo  "你好" > /tmp/file1.txt
 2# 0  标准输入
 3# 1 标准输出
 4# 2 错误输出
 5
 6# 定义4 输入, 输入给/tmp/file1.txt
 7#exec 4</tmp/file1.txt
 8#exec 5>/tmp/file2.out
 9#
10#while read line
11#do 
12#  echo "文件内容: $line"
13#done <&4
14#
15## exec M >&N     M,N都是文件描述符
16#exec >&5    # > 等价于 1>    定义标准输出到 5的文件描述符
17#echo "hello"  #输出到标准输出 , 会输出到 /tmp/file1.txt
18#
19#exec 2>&5   # 错误输出到5的文件描述符
20#
21#
22#exec 6<>/tmp/file.in.out  # 既可以是输入也可以是输出
23#
24#exec 4<&- # 关闭文件描述符
25#exec 5>&- # 关闭文件描述符
26#exec 6<>&- # 关闭文件描述符
27#
28
29# 建立一个管道
30pipe_path=/tmp/pipe1
31[ -e ${pipe_path} ] || mkfifo $pipe_path
32exec 7<>${pipe_path}
33fo i in `seq 1 5`
34do 
35  echo "向管道写入hello$i"
36  echo "hello$i" >&7
37  read -u7 name 
38  echo "从管道中读取内容: $name"
39done

Bash的命令行展开与Bash环境的配置