linux shell编程-bash的奇技淫巧

本文主要讲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”之类的扩展。

另单方括号‘[ ]’和双方括号‘[[ ]]’的区别,单括号等同于bash内置test命令,双方括号不是命令,是bash关键字。

 

原文地址:http://www.cnblogs.com/foxgab/p/6901782.html

如果觉得本文对您有帮助,请扫描后面的二维码给予捐赠,您的支持是作者继续写出更好文章的动力!

posted @ 2017-05-25 11:54  foxgab  阅读(1043)  评论(0编辑  收藏  举报