无论是前端,后端还是移动端,大数据,AI还是运维,等等。作为一个高效的 Coder 都必须掌握 shell 编程。So,本文将告诉,入门 shell 其实很简单,抽点时间就能掌握的高回报率的小技能。
包行命令和参数的行称为命令行。语法格式如下:
command [arg1] [arg2] ... [argn] RETURN
其中 command
为命令的名称,arg1 ~ argn
为参数,RETURN
是终止命令行的按键。命令行语法中的方括号表明被括起来的参数为可选项。并不是所有命令都需要参数。选项是一种特殊类型的参数,其前面通常是一个或两个连字符(或称短线,负号:“-”)。多数实用程序的选项前面需要带一个连字符,而 GNU 程序的选项前面通常带有两个连字符。
本质上,shell 只是执行命令的宏处理器(术语宏处理器是指扩展文本和符号以创建更大表达式的功能)。它既是命令解释器又是编程语言。作为命令解释器,shell 为丰富的 gnu 实用程序集提供用户接口;作为编程语言,允许组合这些实用程序,放在一个脚本文件中(通常后缀为 .sh 也可以不带后缀),构成新的命令,这些新命令具有与 /bin
等目录中的系统命令的具有相同可执行特性,允许用户或搭建立自定义环境以自动化其常见任务,来提高日常开发和运维效率。
shell 的实现有很多种,最常用的是 Bash (Bourne-Again Shell 的缩写),它是 GUN 操作系统的 shell 的解释器,也是本文使用的 shell。
下文需要介绍的条件结构构循环结构,都需要用到 test 内置命令。正确掌握 test 的使用,是应用下文知识的前提。因此,放到前面先介绍。
test 作为 shell 内置命令,它比较特殊,有两种等效写法:
test -<opt> args
反逻辑:
test ! -<opt> args
比较推荐的使用方式,下文条件和循环结构都采用这种方式。
!!!注意: 中括号两边(与选项和参数之间)要有空格。
[ -<opt> args ]
反逻辑:
[ ! -<opt> args ]
test 命令方式:
# 先在当前目录创建一个文件,用于测试
echo "" > file.txt
# 存在则执行后面 echo 命令
test -f file.txt && echo 'file.txt exist!'
# 不存在则执行后面 echo 命令
test ! -f file.txt && echo 'file.txt exist!'
中括号方式:
[ -f file.txt ] && echo 'file.txt exist!'
[ ! -f file.txt ] && echo 'file.txt exist!'
&&
用于连接多个 shell 命令,只有当前面的命令执行成功,才会执行后面的命令。作用类似 if 。
-e
: 检查文件或目录是否存在-d
: 检查目录是否存在-f
: 检查文件是否存在-s
: 检查文件是否存在,以及该文件是否大于0字节-r
: 检查文件是否存在,以及该文件是否可读-w
: 检查文件是否存在,以及该文件是否可写-x
: 检查文件是否存在,以及该文件是否可执行上一小节列出的选项是检查文件是否存在,只能接受 1 个输入参数。当比较两个数值的关系时,需要用到数值比较选项。
-eq
: 等于-ne
: 不等于-gt
: 大于-ge
: 大于等于-lt
: 小于-le
: 小于等于示例:
a=1; [ $a -eq 1 ] && echo 'a is equal to 1'
a=2; [ $a -gt 1 ] && echo 'a is greater than 1'
a=1; [ $a -lt 2 ] && echo 'a is less than 2'
字符串比较使用和数值比较不一样的选项,并且只有两种关系:等于和不等于。
=
: 等于!=
: 不等于示例:
a=hello; [ $a = hello ] && echo 'a is equal to hello'
a='hello'; [ $a != 'world' ] && echo 'a is not equal to world'
需要注意的是当将包含空格的字符串赋值给变量使用时,应该使用如下比较方式。
使用双引号将变量包裹起来:
a='hello world'; [ "$a" = 'hello world' ] && echo 'a is equal to "hello world"'
或者使用双中括号 [[ … ]] :
a='hello world'; [[ $a = 'hello world' ]] && echo 'a is equal to "hello world"'
if test-command
then
commands
fi
或(更常用的写法):
if test-command; then
commands
fi
单行写法(将换行符号换成分号):
if test-command; then commands; fi
保存到 if_test.sh
文件中。
a=1
if [ $a -eq 1 ]; then
echo 'a is equal to 1'
fi
或(可直接在命令行输入):
a=1; if [ $a -eq 1 ]; then echo 'a is equal to 1'; fi
if test-command; then
commands
else
commands
fi
将脚本保存到 if_else_test.sh
文件中。
if [[ $1 = 'hello world' ]]; then
echo "parameter 1 is equal to hello world"
else
echo "parameter 1 is not equal to hello world"
fi
命令行运行:
$ sh if_else_test.sh 'hello world'
parameter 1 is equal to hello world
$ sh if_else_test.sh 'hello world2'
parameter 1 is not equal to hello world
if test-command; then
commands
elif test-command; then
commands
...
else
commands
fi
将脚本保存到 if_elif_test.sh
文件中。
if [ $# -eq 0 ]; then
echo 'no parametes'
elif [ $# -eq 1 ]; then
echo 'only one parameter'
else
echo 'many parameters'
fi
命令行运行:
$ sh if_elif_test.sh
no parametes
$ sh if_elif_test.sh 1
only one parameter
$ sh if_elif_test.sh 1 2
many parameters
for loop-index in argument-list; do
commands
done
将脚本保存到 for_in_test.sh
文件中。
for i in 1 2 3; do
echo $i
done
for f in *.sh; do
echo $f
done
命令行运行:
$ sh for_in_test.sh
1
2
3
for_in_test.sh
if_elif_test.sh
if_else_test.sh
if_test.sh
while test-command; do
commands
done
将脚本保存到 while_test.sh
文件中。
i=0
while [ $i -lt 3 ]; do
echo $i
((i += 1))
done
命令行运行:
$ sh while_test.sh
0
1
2
shell 中使用变量比较简单,前文也已经多处使用到变量。
使用 ‘=’ 通过给标识符赋值即可创建变量。需要注意的是等号 ‘=’ 两边不能有空格。
a=1
b='hello'
访问或读取变量,需要在变量标识符前加 ‘$’。
echo $a
echo $b
函数是稍后执行(区别于马上执行)的一系列命令的集合。
其中关键字 function
可要可不要。当加上 function
时,函数名后面的圆括号可以省略。和其他语言不同的是,shell 函数不能接受参数,调用时也不能使用圆括号。
function-name() {
commands
}
function function-name {
commands
}
将以下脚本保存到文件 func_test.sh
中。
f=$1
sec_rm_file() {
if [ -f $f ]; then
echo "remove file $f ..."
rm -f $f
else
echo "$f not existed."
fi
}
function tips {
echo 'need 1 argument at least'
}
if [ $# -ne 0 ]; then
sec_rm_file
else
tips
fi
命令行运行:
$ sh func_test.sh file.txt
remove file file.txt ...
$ sh func_test.sh file.txt
file.txt not existed.