Shell常用脚本

  • A+
所属分类:技术教程

作为一个运维工程师(数据库工程师) ,管理和维护成百上千台服务器是很正常的事情,脚本就成为了
不可或缺的工具,shell是最基本的了,工作中经常用到,自己讲偶尔犯错的部分记录一下,警示自己。
另外,shell 是Linux原生的交互工具,使用方便,简单,可以在运维工作中 ,大大的提高工作效率
,对日常简单的任务都可以由shell处理 , 哈哈 。 当然也有不合适的地方。比如说:

需要比数组更复杂的数据结构
出现了复杂的转义问题
有太多的字符串操作 
性能问题
任务代码比较多 ... 
注意 : 这个时候 选择 Python  会是一个明智的选择

shell 工作中易错总结

1.字符串单引号和双引号 ‘’ “”

a. 单引号内部的 变量 是完全按照字符展示的,不会替换成为变量的值的。
b. 双引号中的变量会进行变量值替换。

#!/bin/bash
for x in $( seq 1 10 )
do
echo '$x : ' ," $x !"
done

输出:

$x :  , 1
$x :  , 2
$x :  , 3
$x :  , 4
$x :  , 5
$x :  , 6
$x :  , 7
$x :  , 8
$x :  , 9
$x :  , 10

2. 反引号和$()的使用

和 $() 是对等的,但是后者更清晰,特别是多层次嵌套的时候。 反单引号在有些字体里
跟正单引号很相似。 $()能够内嵌使用,而且避免了转义符的麻烦。

for x in $( seq 1 10 ) 和 for x in `seq 1 10 ` 是对等的

比如要是 输出 : 1-2-3-4 。 明显第二个更加简单方便,并且不需要转义

echo "1-`echo 2-\`echo 3-\\\`echo 4\\\`\``"
echo "1-$(echo 2-$(echo 3-$(echo 4)))"

3. 从文件中按照行级别读取

以下三个方式都可以的
3.1 while

while read line
do
    echo $line
done < test

3.2 cat | while

cat filename | while read line
do
    echo $line
done

3.3 for
注意 此方式在行中有空格的时候 会变为多行

for line in `cat filename `
do
    echo $line
done

4. 变量 variable

整数变量加减乘除运算 , 这个我经常记混,所以特别记录以下

for x in ` seq 1 10 ` 
do
    echo $x "   "  $((${x} + 10)) " " $(($x * 10))
done

输出 :
1 11 10
2 12 20
3 13 30
4 14 40
5 15 50
6 16 60
7 17 70
8 18 80
9 19 90
10 20 100

5. function ,shell 定义函数非常方便

把相同的功能模块定义为function , 可以提高代码的重用性和可读性,如下读取文件代码 :

GetFileLines() {     
    local sum=0
    local line=””
    while read line ; do
        sum=$((${sum} + ${line}))
    done
    echo ${sum}
} 

6. 用[]替代[]

使用[[]]能避免像异常的文件扩展名之类的问题,而且能带来很多语法上的改进
,而且还增加了很多新功能:

操作符 功能说明
||          逻辑or(仅双中括号里使用)
&&          逻辑and(仅双中括号里使用)
<           字符串比较(双中括号里不需要转移)
-lt         数字比较
=           字符串相等
==          以Globbing方式进行字符串比较(仅双中括号里使用,参考下文)
=~          用正则表达式进行字符串比较(仅双中括号里使用,参考下文)
-n          非空字符串
-z          空字符串
-eq         数字相等
-ne         数字不等

单中括号:

[ $x -gt  3 ] 

双中括号

[[ $x >  3 ]] #右侧如果是变量比较,一定要加上双引号
正则表达式

使用双中括号带来的好处用下面几个例子最能表现:

t="abc123"
[[ "$t" == abc* ]]         # true (globbing比较)
[[ "$t" == "abc*" ]]       # false (字面比较)
[[ "$t" =~ [abc]+[123]+ ]] # true (正则表达式比较)
[[ "$t" =~ "abc*" ]]       # false (字面比较)

注意,从bash 3.2版开始,正则表达式和globbing表达式都不能用引号包裹。如果你的表达式里有空格
,你可以把它存储到一个变量里:

r="a b+"
[[ "a bbb" =~ $r ]]        # true

按Globbing方式的字符串比较也可以用到case语句中:

case $t in
abc*)  <action> ;;
esac

7. 字符串操作

${#string}                              $string的长度 
${string:position}                      从位置$position开始提取子串
${string:position:length}               从位置$position开始,长度为$length的子串
${string#substring}                     从开头, 删除最短匹配$substring的子串
${string##substring}                    从开头, 删除最长匹配$substring的子串
${string%substring}                     从结尾, 删除最短匹配$substring的子串
${string%%substring}                    从结尾, 删除最长匹配$substring的子串
${string/substring/replacement}        $replacement,替换第一个匹配的$substring
${string//substring/replacement}       $replacement, 代替所有匹配的$substring
${string/#substring/replacement}       $string的前缀匹配$substring ,替换
${string/%substring/replacement}       $string的后缀匹配$substring, 替换

Bash里有各种各样操作字符串的方式
基本用户

f="path1/path2/file.ext" 
len="${#f}" # = 20 (字符串长度) 
# 切片操作: ${<var>:<start>} or ${<var>:<start>:<length>}
slice1="${f:6}" # = "path2/file.ext"
slice2="${f:6:5}" # = "path2"
slice3="${f: -8}" # = "file.ext"(注意:"-"前有空格)
pos=6
len=5
slice4="${f:${pos}:${len}}" # = "path2"

替换操作

f="path1/path2/file.ext" 
single_subst="${f/path?/x}"   # = "x/path2/file.ext"
global_subst="${f//path?/x}"  # = "x/x/file.ext" 

删除头部或尾部

f="path1/path2/file.ext"
# 删除字符串头部
extension="${f#*.}"  # = "ext"
# 以贪婪匹配方式删除字符串头部
filename="${f##*/}"  # = "file.ext"
# 删除字符串尾部
dirname="${f%/*}"    # = "path1/path2" 
# 以贪婪匹配方式删除字符串尾部
root="${f%%/*}"      # = "path1"

8.分割字符串

假设变量$str的值为foo-bar-baz,如果你想按-分割成多个段并遍历它,可以使用read命令,
并且设置 IFS 的值为-:

$ IFS=- read -r x y z <<< "$str"

这里我们使用read x命令从标准输入读取内容,分割后并依次保存到x y z变量中。其中,$x 为foo,
$y 为 bar, $z 为 baz。 另外要留意的一处是here-string操作符<<<,可以很方便地将字符串传递
给命令的标准输入。在这个例子中,$str的内容传给 read 命令的标准输入。

你也可以将分割后的几个字段保存到数组类型的变量中:
$ IFS=- read -ra parts <<< "foo-bar-baz"

在这里,-a 选项告诉read命令将分割后的元素保存到数组parts中。随后,你可以通过${parts[0]}
${parts[1]}和${parts[0]}来访问数组的各个元素,或者通过${parts[@]}来访问所有元素。

9. 转换大小写

使用tr 命令替换

10.去除行尾的最后一个字符

awk -F "" -v OFS=""  '$NF="" {print}1'

总结

shell 使用比较方便,系统原生,属于轻量级脚本,但是不得不承认,语法上面不如Python 简洁,另外shell 用起来很多地方容易出错。