入门shell编程

入门shell编程

第一个shell脚本

1
2
3
4
5
6
7
#!/usr/bin/env bash
mkdir code
cd code
for ((i=0; i<3; i++)); do
touch test_${i}.txt
echo "shell很简单" >> test_${i}.txt
done

让我们看一下以上代码都做了什么:

  1. 从系统path中寻找指定脚本的解释程序
  2. 创建 名叫code文件夹
  3. 进入创建的文件夹
  4. for循环3次
  5. 创建文件
  6. 往创建的文件中写入信息
  7. 结束循环

mkdir, touch,cd,touch,echo都是系统命令,在命令行下可以直接执行 for, do, done 是shell脚本语言 for循环的语法.

编写shell

新建一个文件,扩展名为sh(sh代表shell),扩展名并不影响脚本执行,见名知意就好,如果你用php,扩展名为php,如果你用Python,扩展名为python.

第一行一般是这样:

1
2
3
4
#!/usr/bin/php
#!/usr/bin/env python3
#!/usr/bin/env bash
#! 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行 /env 是系统的PATH目录中查找

运行shell脚本有两种方式

  1. 作为可执行程序

    1
    2
    chmod +x op_base.sh  //设置op_base.sh可执行权限
    ./op_base.sh //执行op_base.sh文件
  2. 作为参数

    1
    /bin/sh op_base.sh

变量

定义变量时,变量名前不需要加符号和Python一样,但是在PHP语言中变量需要加$.如my_name="jack"

变量名和=之间不能有空格且变量后不能有;号.

使用变量

对于已经定义过的变量,使用的时候在前面添加$,如echo $my_name,或echo ${my_name} .花括号是可选的,但建议加上.因为花括号是为了帮助解释器识别变量的边界.

注释

单行注释使用#,解释器会忽略该行

sh 里没有多行注释,只能每一行加一个#号.

字符串

shell 不像其他语言如 php,python 有很多数据类型,在 shell 中常用的数据类型即字符串和数字.

shell中的引号和 php 类似,可以用单引号或双引号,双引号中可以有变量,可以出现转义字符,但单引号中的变量是无效的.python 中引号没有区别,且存在三个引号.三个引号不会被转义.

字符串操作
  • 拼接字符串

    1
    2
    3
    a="hello"
    b="world"
    echo $a $b
  • 获取字符串长度

    1
    echo ${#a}
  • 截取字符串

    1
    echo ${a:0:2}

shell 数组

定义数组

在 shell 中用括号表示数组,数组元素用空格分隔开.如name=(name1 name2 name3)还可以单独定义数组的各个分量如arr[0]=name1

读取数组

读取数组的一般格式是${数组[下标]},比如echo ${name[0]},使用@符号可以获取数组中所有元素.例如echo $name[@]

获取数组长度

获取数组长度与字符串类似,例如:

1
2
3
4
5
6
7
8
9
#获取数组元素个数
length=${#name[@]}
echo length
# 或者
length=${#name[*]}
echo length
# 取得数组单个元素的长度
length=${#name[n]}
echo length

shell 传递参数

我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$n。n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推……

1
2
3
./test.sh 1 2 3
$0 # ./test.sh
$1 # 1 ...

shell 运算符

原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 算术运算符
# + - \* / % = == !=
val=`expr 2 + 2`
echo "两数之和为 : $val"

#关系运算符
# -eq 相等 , -ne 不相等, -gt 大于, -lt 小于, -ge 大于等于, -le 小于等于
if [ $a -eq $b ]
then
echo "$a -eq $b : a 等于 b"

# 布尔运算
# ! 非. -o 或, -a与
if [ $a -lt 100 -a $b -gt 15 ]
then
echo "$a 小于 100 且 $b 大于 15 : 返回 true"
# 逻辑运算
# && ||

# 字符串运算
# =, !=, -z 长度是否为 0, -n 长度是否不为 0, $ 是否为空

# 文件测试运算符
# -e 是否存在
# -d 是否存在且为目录
# -f 是否存在且为常规文件
# -r 是否存在且可读
# -w 是否存在且可写
# -x 是否存在且可执行
# -s 文件大小是否大于 0

test 命令 和 [[]]

test命令用来测试某条件是否成立,可以用[]代替, 但[]内部不能使用||和&&以及!,[[]]支持这种写法,

1
2
3
4
5
6
7
read tel
if [[ $tel =~ ^1[0-9]{10}$ ]]
then
echo "你输入的是手机号码"
else
echo "你输入的不是手机号码"
fi

echo 和 printf

echo 和 printf是用于字符串的输出.

1
2
echo "$1" > xx.txt
printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg

shell 流程控制

和其他编程语言流程控制不同,sh的流程控制不能为空

如 js 流程控制

1
2
3
4
5
6
var a = true
if a{
console.log("a 为 true")
}else{
//什么也不做
}

在 sh/bash 里不能这么写,如果 else 分支没有执行语句就不要写 else. 如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/env sh
a=1
b=2
if [$a == $b]
then
echo 'a 等于 b'
elif [$a -gt $b]
then
echo 'a 大于 b'
elif [$a -lt $b]
then
echo 'a 小于 b'
else
echo "没有符合的条件"
fi

for 循环

shell 的 for 循环和 python 类似.

python 的 for 循环:

1
2
for item in 1,2,3,4,5:
print(item)

shell 的 for 循环写法一:

1
2
3
4
for item in 1 2 3 4 5;
do
echo 'i='$i
done

shell 的 for 循环写法二:

1
2
3
4
for ((i=0;i<5;i++)); 
do
echo "i="$i
done

while语句

while 循环用于不断执行一系列命令,也用于从输入文件中读取数据,命令通常为测试条件.

1
2
3
4
5
6
int=1
while (($int<=5))
do
echo $int
let "int++"
done

在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数

shell 函数

1
2
3
4
5
6
7
8
9
funWithReturn(){
echo "这个函数会对输入的两个数字进行相加运算..."
echo "输入第一个数字: "
read aNum
echo "输入第二个数字: "
read anotherNum
echo "两个数字分别为 $aNum 和 $anotherNum !"
return $(($aNum+$anotherNum))
}

输出/输入重定向

1
2
3
4
# > 输出重定向到 file
# < 输人重定向到 file
# >> 追加
# n>&m n 和m 合并到 m

shell 结合系统命令

shell脚本结合系统命令会更加强大,在字符串处理领域,有 grep,awk,sed 三剑客, grep 负责找出特定的行, awk 能将行拆分成多个字段, sed 则可以实现更新插入删除等写操作.例如定时检测 nginx,mysql 是否被关闭.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
path=/var/log
log=${path}/httpd-mysql.log

name=(apache mysql)

exs_init[0]="service httpd start"
exs_init[1]="/etc/init.d/mysqld restart"

for ((i=0; i<2; i++)); do
echo "检查${name[i]}进程是否存在"
ps -ef|grep ${name[i]} |grep -v grep
if [ $? -eq 0 ]; then
pid=$(pgrep -f ${name[i]})
echo "`date +"%Y-%m-%d %H:%M:%S"` ${name[$i]} is running with pid $pid" >> ${log}
else
$(${exs_init[i]})
echo "`date +"%Y-%m-%d %H:%M:%S"` ${name[$i]} start success" >> ${log}
fi
done
#检测 nginx、mysql进程是否存在,如果不存在了会自动重新启动。 脚本每次运行会写日志的,没事可以去看看该日志文件,如果进程是不是真的经常性不存在,恐怕就要排查一下深层原因了。