本文主要讲bash脚本中容易出错和很少用但是用起来有意想不到效果的部分。
循环:
正常的for循环:
for i in a b c 1 2 3; do echo "$i" done
数字序列循环:
for i in `seq 100`; do echo "$i" done
其中的 `seq 100` 也可以换成 {1..100}
{开始..结尾}的这种形式可以表示连续的数列,可以从小到大也可以从大到小,但是可惜不像python的range()那样可以指定间隔量
C语言风格的for循环:
for (( i=1; i<100; i++ )); do echo "$i" done
for循环迭代文本
for循环会使用空格、换行符、制表符分割文本,依次迭代,如:
a="ab cd ef" for i in $a; do echo "$i" done
输出结果:
ab
cd
ef
shell分割文本其实是以一个全局变量$IFS为依据的,默认情况下IFS=$'\n\t '(即换行符、制表符、空格),可以通过修改IFS的值来改变迭代文本的分割方式。
请注意上面的$a如果加引号括起来的话,会把它当成一个整体,不会切割迭代。
数学计算
双括号“(())”还可以用于数学计算:
(( a=123+456 )) echo $a # 579
在双括号中,变量并不需要用$符号来引用,英文单词天然是变量。
变量:
间接引用:
a=b b=100 echo ${!a} # 100
效果等同于 eval echo \$$a
特殊字符转义
包含特殊字符的字符串:
echo "ab\ncd"
上列命令会输出字符ab+换行+cd,但是实际上字符串"ab\ncd"中并不包含“换行”这个字符,输出的换行只是echo命令解析的结果。
# a="ab\ncd" # echo "length of var a is ${#a}" length of var a is 6
通过${#var}的形式可以得到一个字符串的长度,上面示例可以看出来,字符串"ab\ncd"的长度是6而不是预期的5。
那么能否将不可打印的字符赋值给变量呢?答案是肯定的:
# a=$'\x61\n\x62' # echo "$a" a b
请注意一定用的是单引号“'”,用双引号括起来是不起转义作用的。
字符串操作
变量值字符截取:
a="abcdefg" echo ${a:2} # cdefg echo ${a:2:3} # cde
变量值字符删除:(#为从头部开始匹配,%为从尾部匹配,支持通配符“?*”)
a="abdcdefgh" echo ${a#*d} # 删除字符串头部匹配“*d”的部分,通配符*代表匹配任意多个字符,一个#号代表通配符“*”保守匹配 # cdefgh echo ${a##*d} # 两个#号表示贪婪匹配 # efgh echo ${a%d*} # 同上,两个%时代表贪婪匹配 # abdc
a=b b="abcdefg" echo "${!a#abc}" # defg
可以同时支持间接引用和字符删除
变量赋值安全
用于赋值前检查是否已经定义或赋值。
unset a unset b b="" # a没有定义,b定义了但是空值 echo ${a-789} # 如果没定义,则表达式采用默认值789 # 789 echo ${a:-789} # 如果没定义或是空值,则表达式采用默认值789 # 789 echo ${b-789} # 如果没定义,则表达式采用默认值789,b定义了 # echo ${b:-789} # 如果没定义或是空值,则表达式采用默认值789 # 789 echo $a # 原变量并没有被赋值 # echo $b # 原变量并没有被赋值 # b=123 echo ${b-789} # 如果变量已经被赋值,则不改变原有值 # 123
unset a echo ${a=789} # 如果a没定义,则表达式采用默认值,并给a赋值 # 789 echo $a # 789 # a已经被赋值了 a="" echo ${a=789} # echo ${a:=789} # 加冒号:的作用和之前一样 # 789 echo $a # 789
前面讲了用“-”和“=”的,还有一种用“+”的,用于当变量已经被赋值的情况下采用默认值,举一反三,不赘述了。
位置变量
$0是文件名,$#代表参数个数,$1,$2等等表示执行该脚本或函数后面跟的位置参数,$1代表第一个参数,依次类推,需要注意的是,第9个以后的参数应该这么表示:
${10}
这表示第10个参数,必须用花括号括起来,否则就变成第1个变量后面加个字符“0”了。因为这个有的人以为第9个以后的位置变量就不可用了,是错的。
$@和$*都表示所有的变量,但是区别是前者将每个作为一个字符串,是多个字符串的结合,而后者将所有变量组合成一个字符串,用空格分隔。
这样不同会在for循环里面造成使用的区别,在for循环里面,如果加上引号 for i in "$@" 和 for i in "$*" 是不同的,前者还是会依次把参数代入循环,而后者会吧所有参数作为字符串一次性代入:
echo ' for i in "$@"; do echo "$i" done for i in "$*"; do echo "$i" done ' > myscript.sh chmod 755 myscript.sh ./myscript.sh abc 123 def
结果是:
abc 123 def abc 123 def
那么$@加引号和不加引号有区别么?
大部分情况下结果看起来是一样的,但会在某些情况下造成麻烦,因为for循环会用换行符“\n”和空格来拆分变量,所以如果调用脚本所带的参数中带有空格或者换行符,不加引号的时候会把这个参数拆开了依次代入,而加了引号就可以避免这种情况。
echo ' for i in "$@"; do echo "$i" done for i in $@ ; do echo "$i" done ' > myscript.sh chmod 755 myscript.sh ./myscript.sh abc "123 def"
结果是:
abc 123 def abc 123 def
for循环使用数组
将数组的值依次代入循环,可以用${arr[@}和${arr[*]}这两种方法,区别和加引号的区别同上面位置参数的使用相同。
字符串匹配
shell的字符串匹配通常是这样的:
[ "abc" = "abc" ] echo $? # 0
若字符串相同则返回0,否则返回1。除了全匹配外,通过双方括号“[[]]”还可以用正则表达式匹配
[[ "abc234def" =~ ^abc[0-9]{3}[a-z]+$ ]] echo $? # 0
注意这里正则表达式不能用引号括起来,而且只支持基本的正则,不支持如“\b \S”之类的扩展。
原文地址:http://www.cnblogs.com/foxgab/p/6901782.html
如果觉得本文对您有帮助,请扫描后面的二维码给予捐赠,您的支持是作者继续写出更好文章的动力!