前言
Shell是一种让使用者可以和作业系统kernal(用来控制CPU、记忆体、硬碟等硬体)互动沟通的桥梁。Shell Script主要是使用在Linux和MacOS等Unix-like作业系统的自动化操作指令的程式语言。其透过Unix shell命令列直译器来执行(我们这边主要使用bash shell,其他的Unix shell观念大致类似),使用方式有点类似直译式程式语言(不用编译直接执行)。在Windows系列家族也有类似的使用方式:Batch file。
一般情况Shell Script常用于系统管理、自动化操作档案、自动化重复的指令码、分析log等文件档案、列印呈现我们想要的资料等,透过程式语言的使用来减少重复琐碎的工作,所以若能妥善使用将提升不少开发者和软体工程师的日常工作效率。接着我们将透过日常生活常用的使用情境,带领读者们进入入门Shell Script(读者需要具备基本Linux指令码的基本观念,若你需要复习常见Linux指令可以参考Linux Command命令列指令与基本操作入门教学) 。
Shell Script 初体验
在撰写Shell Script之前我们先来了解Shell Script撰写的流程和架构。一般我们会使用.sh副档名来命名Shell Script档案。然后将该档案设定为可执行:
1
chmod +x demo.sh
可以透过检视档案详细资料观看是否已有+x的执行权限:
1
2
3
ls -l demo.sh
# -rwxr-xr-x 1 user staff 106 Nov 16 10:41 demo.sh
执行Shell Script 档案:
1
./demo.sh
接着,我们先利用一个简单的范例:将目前执行process 的PID 依照数字大小排序,取出前10 名,来了解撰写Shell Script 的基本架构。
1
2
3
4
5
6
7
#宣告使用/bin/bash
#!/bin/bash
echo "===将目前执行process的PID依照数字大小排序,取出前10名=== "
# ps为列出process相关资讯,透过| pipe管线传递资料。awk可以根据pattern进行资料处理(这边印出第一栏PID)而sort是进行排序,其排序时,预设都是把资料当作字串来排序,若想让资料根据实际数值的大小来排序,可以加上-n参数。-r则是由大到小排序,预设是由小到大
ps | awk '{print $1}' | sort -rn | head -10
执行结果:
1
2
3
4
5
6
7
8
9
10
11
12
$ ./demo.sh
===将目前执行process的PID依照数字大小排序,取出前10名===
83784
83783
75956
75955
75954
75952
74069
74068
73543
37621
恭喜你,你已经完成了第一个Shell Script 程式了!
变数
一般来说程式语言中变数是用来暂存接下来会使用到的资料或是储存指到物件的参考位置。在Shell Script 可以使用以下三种方式来宣告变数并给定值:
1
2
3
4
5
6
#!/bin/bash
variable1=value
#若是值内有空白则需要使用''或""包裹起来
variable2='value 2'
variable3="value 3"
注解使用#,因为没有多行注解,所以需要使用多单行注解达到
使用变数方式为${變數名稱},花括号主要是辅助了解变数的范围:
1
2
3
4
5
#!/bin/bash
pathName=demo.sh
# echo是列印值,印出变数pathName内容demo.sh
echo ${pathName}
更新变数直接重新assign 值即可:
1
2
3
4
5
6
7
8
9
#!/bin/bash
pathName=demo1.sh
#印出demo1.sh
echo ${pathName}
pathName=demo2.sh
#印出demo2.sh
echo ${pathName}
删除变数使用unset:
1
2
3
4
5
6
7
8
#!/bin/bash
pathName=demo.sh
#印出demo.sh
echo ${pathName}
unset pathName
#空值
echo ${pathName}
注意系统环境变数为全域变数、区域变数则为Shell Script 内部程式使用,不能跨档案使用。
运算式
运算式是当运算子和运算元计算结果回传后赋值给变数。在Bash Shell 中内建原生不支援运算式,但我们可以使用expr、awk 等指令来支援实现运算式。
算式
我们可以使用四则运算来赋值:
1
2
3
4
5
6
#!/bin/bash
result=`expr 10 + 2`
# 12
echo "Result: $result"
条件判断
在Shell Script中同样可以使用if..else条件判断,特别注意的是在Shell Script中使用fi为结尾(为if的倒写法,同样的接下来讨论的case也有类似用法),代表条件判断结束。==为等于,!=为不等于运算子。
if
1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/sh
x=20
y=30
if [ $x == $y ]; then
echo "value x is equal to value y"
fi
if [ $x != $y ]; then
echo "value x is not equal to value y"
fi
if else
在Shell Script可以使用-gt(greater than缩写)和-lt(less than缩写)代表大於和小於,而-ge(greater equal缩写)和-le(less equal缩写)则是大於等於和小於等於的运算子符号。
记得比较条件需要放在[] 中,前后要留空白
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
#!/bin/bash
if [ $x -gt $y ]; then
echo "value x is greater than value y"
else
echo "value x is not greater than value y"
fi
if [ $x -lt $y ]; then
echo "value x is not less than value y"
else
echo "value x is not less than value y"
fi
if [ $x -ge $y ]; then
echo "value x is greater or equal than value y"
else
echo "value x is not greater than value y"
fi
if [ $x -le $y ]; then
echo "value x is not less or equal than value y"
else
echo "value x is not less or equal than value y"
fi
if elif else
若有多个条件需要判断,可以使用if elif else:
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
value1=20
value2=30
value3=30
if [ $value1 -gt $value2 ]; then
echo "value1 is greater than value2"
elif [ $value1 == $value3 ]; then
echo "value1 is equal to value3"
else
echo "other result"
fi
case … esac
若要使用类似一般程式语言的switch来处理多种条件判断时,可以使用case来进行判断:
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
language='Java'
case $language in
Java*) echo "是Java!"
;;
Python*) echo "是Python!"
;;
C*) echo "是C!"
;;
*) echo "没match到!"
esac
回圈
当我们需要重复某些琐碎的任务或是迭代取得资料就需要回圈来支援。此时可以使用for、while和until回圈进行迭代。
for
Shell Script的for使用方法和一般程式语言类似,同样可以针对条件使用break、continue来跳出或是跳过回圈。
1
2
3
4
5
#!/bin/bash
for loop in 1 2 3; do
echo "number: $loop"
done
while
若是需要设定一个条件直到该条件为止,可以使用while,但要注意避免无限回圈状况:
1
2
3
4
5
6
7
#!/bin/bash
counter=0
while [ $counter -le 5 ]; do
counter='expr $counter+1'
echo $counter
done
until
直到某个条件结束可以使用until来进行:
1
2
3
4
5
6
7
8
#!/bin/bash
#从0印出数字直到10
counter=0
until [ $counter -gt 10 ]; do
echo $counter
counter=`expr $counter + 1`
done
函式
随着我们的程式越来越大,我们需要透过模组化或是将重复使用的程式码改成函式。函式基本架构如下:
函式名称(function关键字为选择性)
是否有传入参数
函式内操作
是否有回传值
1
2
3
4
function function_name () {
#做一些事情
[回传值]
}
函式范例:
1
2
3
4
5
6
7
8
#!/bin/bash
function echoHello() {
# hello world, rock!!
echo "${0} hello ${1}, ${2}!!"
}
echoHello 'world' 'rock'
以上我们可以看到使用""双引号把字串和变数取出来印出(你可以试试看使用单引号会发生什么事情),与一般程式语言比较不同的是其函式呼叫不需要有小括号传入参数,直接以空白当作参数传递的格式。注意参数从1开始,${0}为档名。
特殊变数
在Shell Script 档案和函式往往需要透过传入参数来设定执行程式的内容。在Shell Script 支援许多好用的特殊变数,可以方便我们透过使用变数方式来设置程式执行的流程。
指令
描述
注解
$0
目前的档案档名
$n
n 从1 开始,代表第几个参数
$#
传递到程式或函式目前有几个参数
$*
传递到程式或函式所有参数
$@
类似$* 但是在被双引号包含时有些许不同
$?
上一个指令退出状态或是函式的返回值
$$
目前process PID
透过执行./demo_special_var.sh var1 var2:
1
2
3
4
5
6
7
8
9
#!/bin/bash
echo "$0"
echo "$1"
echo "$#"
echo "$*"
echo "$@"
echo "$?"
echo "$$"
印出结果:
1
2
3
4
5
6
7
./demo.sh
var1
2
var1 var2
var1 var2
0
80417
总结
以上我们透过循序渐进入门了Shell Script并撰写了我们第一个Shell Script程式,并了解如何在Shell Script中使用变数、条件判断、回圈、函式以及特殊变数。Shell Script常用于系统管理、自动化操作档案、自动化重复的指令码、分析log等文件档案、列印呈现我们想要的资料等,透过程式语言的使用来减少重复琐碎的工作,快把Shell Script放入你的工具箱中吧!若能妥善使用将提升不少开发者和软体工程师的日常工作效率。