首页 > 代码库 > bash脚本(一)

bash脚本(一)

什么是脚本,其实脚本是有若干个命令串起来执行的命令行,如下图。命令行一行有255个字符数的限制,所以一般长些的脚本都写在文件中。
  1. [root@localhost ~]# date;uname -a;pwd
  2. Thu Dec 4 17:29:52 CST 2014    date命令显示
  3. Linux localhost.localdomain 2.6.32-504.el6.x86_64 #1 SMP Wed Oct 15 04:27:16 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux    uname -a命令显示
  4. /root    pwd命令显示
  1. [root@localhost ~]# cat test.sh
  2. #!/bin/bash      Shebang这个符号通常在Unix系统的脚本中第一行开头中写到,它指明了执行这个脚本文件的解释程序
  3. A=test test赋值给A
  4. echo "$A"        双引号是弱引用,显示A变量
  5. echo ‘$A‘      单引号是强引用,所以显示的是$A
  6. [root@localhost ~]# bash test.sh bash是命令,test.sh相当于参数
  7. test
  8. $A
  9. [root@localhost ~]# A=`pwd`         命令引用,把命令结果输出
  10. [root@localhost ~]# echo $A
  11. /root
  1. [root@localhost ~]# ./test.sh    shell能够提供一些内部命令,并且能通过PATH环境变量找到外部命令;把命令提交给内核启动为进程,由于被当成命令来执行没有执行权限,所有被拒绝
  2. -bash: ./test.sh: Permission denied
  3. [root@localhost ~]# ll test.sh
  4. -rw-r--r-- 1 root root 39 Dec 5 10:49 test.sh
  5. [root@localhost ~]# chmod a+x test.sh
  6. [root@localhost ~]# ll test.sh
  7. -rwxr-xr-x 1 root root 39 Dec 5 10:49 test.sh
  8. [root@localhost ~]# ./test.sh    bash读取到了第一行的shebang,就调用bash
  9. test
  10. $A

变量
局部变量:函数调用的整个生命周期
        如:local VAR=VALUE
本地变量:整个脚本,在脚本的函数中也可调用,也可修改 
            declare -a:定义数组
            declare -i:定义整数
            declare -r:定义只读变量,不能别unset
环境变量:定义工作目录,提示字符,执行命令搜索路径等
        1、全局环境变量(范围全局)
                export VAR_NAME=VALUE
                declare -x VAR_NAME=VALUE
                env,printenv
        2、局部环境变量(范围局部)
                set:
位置参数变量:$0(获取程序名),$1(获取第一个参数),$2,$3,$4,....$9,${10}
特殊变量:$@(取得所有参数),$*(取得所有参数),$#(取得参数个数)
$@和$*的区别
  1. #!/bin/bash
  2. count1=1
  3. for i in "$*"; do
  4. echo "A$count1 = $*"
  5. count1=$[ $count1 + 1 ]
  6. done
  7. count2=1
  8. for j in "$@"; do
  9. echo "B$count2 = $@"
  10. count2=$[ $count2 + 1 ]
  11. done
  12. [root@localhost ~]# bash test.sh 1 2
  13. A1 = 1 2
  14. B1 = 1 2
  15. B2 = 1 2
4种算术运算
  1. [root@localhost ~]# A=1
  2. [root@localhost ~]# echo $[ $A+1 ] 1、第一种算术运算
  3. 3
  4. [root@localhost ~]# echo $[$A+1]
  5. 3
  6. [root@localhost ~]# echo $(($A+1)) 2、第二种算术运算
  7. 3
  8. [root@localhost ~]# let A++      3、自增相当于let A=$A+1
  9. [root@localhost ~]# echo $(( $A+1 ))
  10. 3
  11. [root@localhost ~]# echo $( expr $A+1) 4、必须在+号两边那空格
  12. 2+1
  13. [root@localhost ~]# echo $( expr $A + 1)
  14. 3
输入输出重定向
0    STDIN        标准输入    <或<<   
1    STDOUT    标准输出     >或>>
2    STDERR     标准错误     2>或2>>
  1. [root@localhost ~]# ls /etc/fstab /1111 2> test2 1> test3 如不重定向默认是在显示器上输出
  2. [root@localhost ~]# cat test2 将STDERR错误重定向到test2
  3. ls: cannot access /1111: No such file or directory
  4. [root@localhost ~]# cat test3 将STDOUT重定向到test3
  5. /etc/fstab
  6. [root@localhost ~]# ls /etc/fstab /1111 &> test4 把正确的和错误的都重定向到test4
  7. [root@localhost ~]# cat test4
  8. ls: cannot access /1111: No such file or directory
  9. /etc/fstab 
永久重定向
  1. bash test.sh
  2. root@localhost ~]# cat test.sh 
  3. #!/bin/bash
  4. exec 2>testerror
  5. exec 1>testout
  6. echow "error"
  7. echo "aaa"
  8. lss /tmp
  9. ls /tmp
  10. [root@localhost ~]# cat testout 
  11. aaa
  12. test
  13. [root@localhost ~]# cat testerror 
  14. test.sh: line 5: echow: command not found
  15. test.sh: line 7: lss: command not found
创建自己的重定向
在shell中最多可以有9个打开的文件描述符。其他6个文件描述符会从3-8,并且可以当做输入或输出重定向都行。
  1. #!/bin/bash
  2. exec 3>test3out
  3. echo "11"
  4. echo "22"
  5. echo "33" >&3 >&重定向到 <&输入重定向
  6. [root@localhost test]# bash test.sh
  7. 11
  8. 22
  9. [root@localhost test]# cat test3out
  10. 33

退出状态码
当命令执行完毕会有一个退出状态码,可以通过$?查看,状态码的范围是0-255,如返回值是300,状态码会显示44(256的余数),在比较测试中命令状态码0表示true,非0为false
  1. [root@localhost ~]# pwdd;echo $? 命令执行失败后状态码为127
  2. -bash: pwdd: command not found
  3. 127
  4. [root@localhost ~]# pwd;echo $?   命令执行成功,后显示为0
  5. /root
  6. 0
  7. [root@localhost ~]# cat test.sh 可以通过exit来定义退出状态码
  8. #!/bin/bash
  9. exit 300
  10. [root@localhost ~]# echo $?
  11. 44
  1. [root@localhost ~]# cat > test6 << EOF 在屏幕输出后输入到test6
  2. > first
  3. > second
  4. > third
  5. > EOF
  6. [root@localhost ~]# cat test6
  7. first
  8. second
  9. third
测试相关
测试方式有3种 
1、test EXPRESSION
2、[ EXPRESSION ]
3、[[ EXPRESSION ]] 
数值测试,两边需要空格
n1 -eq n2 n1是否等于n2
n1 -ge n2 n1是否大于等于n2
n1 -gt n2         n1是否大于n2
n1 -le n2         n1是否小于等于n2
n1 -lt n2         n1是否小于n2
n1 -ne n2 n1是否不等于n2
  1. [root@localhost ~]# [ 1 -ne 0 ];echo $?     1不等于0为真
  2. 0
  3. [root@localhost ~]# [ 1 -eq 0 ];echo $?    1等于0为假
  4. 1
  5. [root@localhost ~]# [[ 10 -eq 9 ]];echo $?    10等于9为假
  6. 1
  7. [root@localhost ~]# [[ 10 -ne 9 ]];echo $?    10不等于9为真
  8. 0
  9. [root@localhost ~]# test 1 -ne 0;echo $?     1不等于0为真
  10. 0
  11. [root@localhost ~]# test 1 -eq 0;echo $?    1等于0为假
  12. 1
字符测试
str1 == str2     str1是否和str2相同
str1 != str2       str1是否和str2不同
str1 < str2        str1是否比str2小
str1 > str2        str1是否比str2大
-n str1              判断str1是否不空;不空则为真,空则为假;
-z str1               判断str1是否为空;空则为真,不空则假;
  1. [root@localhost ~]# [[ a > b ]];echo $? a大于b为假
  2. 1
  3. [root@localhost ~]# [[ a < b ]];echo $?    a小于b为真
  4. 0
  5. [root@localhost ~]# [ a < b ];echo $? 在字符测试中单中括号是不生效的
  6. 0
  7. [root@localhost ~]# [ a > b ];echo $?
  8. 0
  9. [root@localhost ~]# test a > b;echo $?
  10. 0
  11. [root@localhost ~]# test a < b;echo $?
  12. 0
  13. [root@localhost ~]# test a \< b;echo $? 字符比较大于等于号必须要加反斜杠转义
  14. 0
  15. [root@localhost ~]# test a \> b;echo $?
  16. 1
"$A" =~ PATTERN 如果变量A中保存的字符串能被PATTERN所匹配;即为真;否则为假
  1. [root@localhost ~]# echo $A
  2. blue
  3. [root@localhost ~]# [[ "$A" =~ b ]];echo $?
  4. 0
  5. [root@localhost ~]# [[ "$A" =~ a ]];echo $?
  6. 1
  7. [root@localhost ~]# [[ "$A" =~ a* ]];echo $?
  8. 0
  9. [root@localhost ~]# [[ "$A" =~ . ]];echo $?
  10. 0
  11. [root@localhost ~]# [[ "$A" =~ * ]];echo $?
  12. 2
文件测试
-d file:目录是否存在
-e file:是否存在,存在则为真
-a $file: 同上;弃用;
-f file:是否存在且为普通文件
-h file:是否存在且为符号链接文件
-L $file:同上
-b file:是否存在且为块设备文件
-c file:是否存在且为字符设备文件
-p file:是否存在且管道文件
-S file:是否存在且为套接字文件
-s file:是否存且是否为非空文件
  1. [root@localhost ~]# [[ -b /dev/sda ]];echo $?
  2. 0
  3. [root@localhost ~]# [[ -b /dev/sd ]];echo $?
  4. 1
-r file:是否存在且当前用户是否拥有读权限
-w file:是否存在且当前用户是否拥有写全权限
-x file:是否存在且当前用户是否拥有执行权限
-O file:是否存在且当前用户所有
-G file:是否存用户默认组相同
  1. [centos@localhost ~]$ [[ -w /etc/passwd ]];echo $? centos用户对/etc/passwd文件不能写
  2. 1
  3. [centos@localhost ~]$ [[ -w /etc/ ]];echo $? 也可以对目录测试
  4. 1
  5. [centos@localhost ~]$ [[ -w /tmp ]];echo $?
  6. 0
  7. [centos@localhost ~]$ touch /tmp/centos
  8. [centos@localhost ~]$ ll /tmp/centos
  9. -rw-rw-r-- 1 centos centos 0 Dec 5 14:37 /tmp/centos
  10. [centos@localhost ~]$ [[ -w /tmp/centos ]];echo $? 可以写显示为0
  11. 0
-N file:文件自从上一次被读取之后,是否被修改过
file1 -nt file2:文件file1是否比文件file2新
file1 -ot file2:文件file1是否比文件file2旧
file1 -ef file2:file1和file2是否为同一个文件的硬链接
  1. [centos@localhost ~]$ ll /etc/fstab
  2. -rw-r--r--. 1 root root 921 Nov 28 08:29 /etc/fstab
  3. [centos@localhost ~]$ ll /tmp/test.sh
  4. -rw-r--r-- 1 root root 339 Dec 5 10:49 /tmp/test.sh
  5. [centos@localhost ~]$ [[ /etc/fstab -nt /tmp/test.sh ]];echo $?
  6. 1
  7. [centos@localhost ~]$ [[ /etc/fstab -ot /tmp/test.sh ]];echo $? fstab比test旧
  8. 0
组合测试
或 -o:EXPRESSION -o EXPRESSION ]
与 -a:[ EXPRESSION -a EXPRESSION ]
非 !:[ ! EXPRESSION ]
  1. [root@localhost ~]# [ -z "" -a -f /etc/fstab ];echo $? 2者都满足则为真
  2. 0
  3. [root@localhost ~]# [ -n "" -a -f /etc/fstab ];echo $?
  4. 1
  1. [root@localhost ~]# [ 1 -gt 2 ];echo $? 没加!状态码非0
  2. 1
  3. [root@localhost ~]# [ ! 1 -gt 2 ];echo $? 加了!状态码为0
  4. 0
与:COMMAND1 && COMMAND2
或:COMMAND1 || COMMAND2
非:! COMMAND
  1. 在/tmp/下创建了4个文件,以下真为状态码为0
  2. 真 执行 && 真 执行 && 真 执行 && 真 执行
  3. [root@localhost ~]# ls /tmp/test1 && ls /tmp/test2 && ls /tmp/test3 && ls /tmp/test4
  4. /tmp/test1
  5. /tmp/test2
  6. /tmp/test3
  7. /tmp/test4
  8. 真 执行 && 真 执行 || 不执行 && 真执行
  9. [root@localhost ~]# ls /tmp/test1 && ls /tmp/test2 || ls /tmp/test3 && ls /tmp/test4
  10. /tmp/test1
  11. /tmp/test2
  12. /tmp/test4
  13. 真 执行 || 不执行 || 不执行 && 执行
  14. [root@localhost ~]# ls /tmp/test1 || ls /tmp/test2 || ls /tmp/test3 && ls /tmp/test4
  15. /tmp/test1
  16. /tmp/test4
  17. 假 返回错误 || 真 执行 || 不执行 && 执行
  18. [root@localhost ~]# lss /tmp/test1 || ls /tmp/test2 || ls /tmp/test3 && ls /tmp/test4
  19. -bash: lss: command not found
  20. /tmp/test2
  21. /tmp/test4
  1. [root@localhost ~]# ls /tmp/test1 &> /dev/null;echo $? 命令执行成功状态码为0,&> /dev/null 为把错误和正常输出全部丢弃
  2. 0
  3. [root@localhost ~]# ! ls /tmp/test1 &> /dev/null;echo $? 加了!状态码为非0
  4. 1
交互式相关
  1. [root@localhost ~]# read -t 3 -p "Enter your menu:" menu -t为3秒后不等待输入,直接进入下一步 -p为显示输入提示,
  2. Enter your menu:                                    menu为输入的参数赋值给menu
-s 选项是隐藏方式读取(如输入密码的时候)
可以和[ -z "$VAR" ] && VAR=VALUE结合使用,如3秒后为输入,赋给它默认的值
字符串操作
字符串切片${var:offset:lenth}
  1. [root@localhost ~]# A=test;echo ${A:2:2} 取3,4两个字符
  2. st
取字符串最后的几个字符:${var: -lenth},冒号后面有空格
  1. [root@localhost ~]# A=test;echo ${A: -2}
  2. st
取子串
${var#*word}:从左开始删除到第一个word字符的数据(包含word字符)
  1. [root@localhost ~]# A=www.sina.com.cn;echo ${A#*.}
  2. sina.com.cn
${var##*word}:从左开始删除到最后一个word之间的数据(包含word字符)
  1. [root@localhost ~]# A=www.sina.com.cn;echo ${A##*.}
  2. cn
${var%word*}:从右开始删除到第一次出现word的数据(包含word字符)
  1. [root@localhost ~]# A=www.sina.com.cn;echo ${A%.*}
  2. www.sina.com
${var%%word*}:从右开始删除到最后一次出现word的数据(包含word字符)
  1. [root@localhost ~]# A=www.sina.com.cn;echo ${A%%.*}
  2. www
基于模式的查找替换,删除,可使用?, *元字符
查找替换
${var/pattern/substi}:替换第一次pattern匹配到的模式
  1. [root@localhost ~]# A="aaa bb cc aa";echo ${A/a*/AA}
  2. AA
  3. [root@localhost ~]# A="aaa bb cc aa";echo ${A/a?/AA}
  4. AAa bb cc aa
${var//patten/subst}:替换所有pattern匹配到的字符
  1. [root@localhost ~]# A="aaa bb cc aa";echo ${A//a?/AA}
  2. AAAAbb cc AA
${var/#pattern/subst}:以行首锚定方式替换以pattern开头的行,如能匹配则替换subst
  1. [root@localhost ~]# A="aaa bb cc aa";echo ${A/#a?/AA}
  2. AAa bb cc aa
  3. [root@localhost ~]# A="aaa bb cc aa";echo ${A/#b*/AA}
  4. aaa bb cc aa
${var/%pattern/subst}:以行尾锚定方式将pattern匹配至var,如能匹配替换subst
  1. [root@localhost ~]# A="aaa bb cc aa";echo ${A/%cc/AA}
  2. aaa bb cc aa
  3. [root@localhost ~]# A="aaa bb cc aa";echo ${A/%aa/AA}
  4. aaa bb cc AA
查找并删除
${var/pattern}:删除pattern匹配到的第一次出现
  1. [root@localhost ~]# A="aaa bb cc aa";echo ${A/b*}
  2. aaa
  3. [root@localhost ~]# A="aaa bb cc aa";echo ${A/b?}
  4. aaa cc aa
${var//pattern}: 删除pattern匹配到的所有出现
  1. [root@localhost ~]# A="aaa bb cc aa";echo ${A//a?}
  2. bb cc
  3. [root@localhost ~]# A="aaa bb cc aa";echo ${A//a*}
${var/#pattern}:删除以pattern开头字符
  1. [root@localhost ~]# A="aaa bb cc aa";echo ${A/#a?}
  2. a bb cc aa
${var/%pattern}:删除以行尾锚定pattern的字符
  1. [root@localhost ~]# A="aaa bb cc aa";echo ${A/%a?}
  2. aaa bb cc
字符串大小写转换
${var^^}:小写-->大写
  1. [root@localhost ~]# A="aa";echo ${A^^}
  2. AA
${var,,}:大写-->小写
  1. [root@localhost ~]# A="AA";echo ${A,,}
  2. aa
变量赋值
${var:-word}: 如果var为空或未设置,那么返回word;否则,则返回var中的值
  1. [root@localhost ~]# echo $A
  2. [root@localhost ~]# echo ${A:-test}
  3. test
  4. [root@localhost ~]# echo $A

${var:=word}:如果var为空或未设置,那么返回word,并且将word赋值给var;否则,返回var中的值
  1. [root@localhost ~]# echo $A
  2. [root@localhost ~]# echo ${A:=test}
  3. test
  4. [root@localhost ~]# echo $A
  5. test
${var:?err_info}:如果var为空或未设置,那么返回错误信息;否则,则返回var自身的值
  1. [root@localhost ~]# echo $A

  2. [root@localhost ~]# echo ${A:?test}
  3. -bash: A: test
  4. [root@localhost ~]# echo $A
  5. [root@localhost ~]# A=tset
  6. [root@localhost ~]# echo ${A:?111}
  7. tset
${var:+word}:如果var自身有正常数据,则返回word
  1. [root@localhost ~]# echo $A
  2. [root@localhost ~]# echo ${A:+test}
  3. [root@localhost ~]# echo $A
  4. [root@localhost ~]# A=AAAAA
  5. [root@localhost ~]# echo ${A:+test}
  6. test
  7. [root@localhost ~]# echo $A
  8. AAAAA

bash脚本(一)