Class2(2020):Shell基础(二)——Shell脚本设计基础
本系列博客为MIT的《Missing in CS Class》的课程笔记。
Class2(2020):Shell基础(二)——Shell脚本设计基础
注:若无特殊说明,本文中带有[]
的部分均为可选参数。
脚本文件
- 脚本语言为解释执行,其运行需有解释器,如Python。
- Shell是一种脚本语言,其文件扩展名为
.sh
。 - 脚本文件的首行一般为
#! <shell_name>
,符号#!
被称为shebang
,其规定该脚本以何种Shell解释器执行。一般采用env
(/usr/bin/env
)程序,到$PATH
中查找某种解释器在何位置,如#!/usr/bin/env shell
- 若同一行有多条命令,用
;
分隔 - 执行脚本:分为子Shell执行与当前Shell执行。若为子Shell执行,在不使用
export
的情形下,脚本中定义的各种变量、函数、环境都不会被带回当前Shell;若为当前Shell执行则反之,可在后续命令中调用脚本中定义的变量、函数等内容。./<script_name>.sh
:仅当该脚本有可执行权限(x
)时,根据shebang
中规定的解释器,创建一个独立子Shell以执行该脚本<shell_name> ./<script_name>.sh
:无论该脚本是否有可执行权限,忽略shebang
,以<shell_name>
创建一个独立子Shell以执行该脚本source ./<script_name>.sh
或. ./<script_name>.sh
:无论该脚本是否有可执行权限,忽略shebang
,直接在当前Shell中执行该脚本,相当于将脚本中进行的修改注入到当前Shell。
变量
-
变量赋值:Shell与其他多数脚本语言一致,变量无需定义,直接赋值即可。右值可为数字、字符串、其他变量、数组、命令替换等。
<name>=<value>
注意赋值中各符号间不能有任何空格。 -
变量可被重新赋值使用。
-
变量使用:
$<name>
-
删除变量:
unset <name>
-
输出:
-
echo
:如echo $a
-
格式化输出:
printf <format_string> [arg_list]
,与C中相同
-
-
Shell定义了一些保留变量:
$0
:脚本名$1
-$9
:脚本执行的第 i i i 项参数。类似于C中main
函数的形参argv[]
$@
:所有参数$#
:参数数量。类似于C中main
函数的形参argc
$$
:当前脚本的PID
(进程识别码)$?
:上条程序的返回值。!!
:上条包含所有参数的完整命令。如执行命令后被返回Permission Denied
,使用sudo !!
即可重新提升执行$_
:上条命令的最后一项参数。如:mkdir 1
,cd $_
数组、字符串
-
Shell只支持一维数组,所有
value
都会被视为字符串处理-
初始化:
<array_name>=(<value1>,<value2>,...)
。 -
赋值:
<array_name>[<index>]=<value>
-
随机访问:
${<name>[<index>]}
。特别的,<idx>
为@
时,代表取数组中所有元素。
-
-
关系数组:相当于Python中的
dic
(字典)、C++中的map
。与普通数组不同,关系数组必须声明后才能使用。-
声明:
declare -A <array_name>
-
初始化:
declare -A <array_name>=(["<key1>"]="<value1>" ["<key2>"]="<value2>" ...)
,其中<key>
必须唯一。 -
赋值:
<array_name>["<key>"]="<value>"
-
-
字符串:
- 单引号
‘
包围的字符串:被视为字符串字面常量,所有内容将被原样存储,变量也不会被替换。如echo '$a'
:输出$a
- 双引号
“
包围的字符串:正常替换变量。如echo "$a"
:输出:b
- 字符串长度:
${#<name>}
- 单引号
命令替换、进程替换
-
命令替换:
$(<command>)
:<command>
命令的stdout
替换$(<command>)
本身。如:today=$(date) #将输出赋值给变量 echo "$today"
for i in $(ls) ; do #遍历文件 echo "$i" done
cd $(ls)
-
进程替换:
-
输出重定向:
<(<command>)
,将<command>
的stdout
写入临时文件中,可作为其他命令的文件读入。例:diff <(ls <dir1>) <(ls <dir2>)
-
输入重定向:
>(<command>)
,将其他命令的stdout
写入临时文件中,由<command>
读取。效果等价于管道符|
-
流程控制
选择结构
if…fi
注意Shell中的分支不可为空,若不需要就不要写对应的分支。注意在每个<condition>
后都需加then
-
单分支结构:
if then ... fi
if <condition> ; then <command> fi
-
双分支结构:
if then ... else ... fi
if <condition> ; then <command> else <command> fi
-
多分支结构:
if then ... elif then ... else ... fi
if <condition> ; then <command> elif <condition> ; then <command> else <command> fi
<condition>
为布尔表达式时的两种表示形式:
[<bool_expression>]
:-eq
:判断两个数字是否相等;-ne
:判断两个数字是否不相等;-lt
:判断数字是否小于;-le
:判断数字是否小于等于;-gt
:判断数字是否大于;-ge
:判断数字是否大于等于。((<bool_expression>))
:算术专用的布尔表达式,直接使用关系运算符即可。
case...esac
case <variable> in
<pattern>)
<command>
;;
[
*) #相当于default
<command>
;;
]
esac
类似于C中的switch
,顺次进行匹配。case...esac
并没有原生的default
,因此在结尾使用通配符*
对未匹配内容进行匹配。
<pattern>
中若有多个匹配规则,用|
(或)连接,如1|2|3
。可使用通配符、字符集([]
)。
循环结构
for
for <variable> in <object> ; do
<command>
done
-
传入列表:
for i in 1 2 3 4 5 ; do echo "$i" done
-
传入范围序列
$(seq)
(相当于Python的range(),注意Shell的seq的左闭右闭区间):for <variable> in $(seq <start> <end> [<foot>]) ; do <command> done
-
传入花括号
{<start>..<end>}
:等价于$(seq <start> <end>)$
for <variable> in {<start>..<end>} ; do <command> done
while
while <condition> ; do
<command>
done
当<condition>
为真时,循环执行循环体
until
until <condition> ; do
<command>
done
<condition>
为假时,循环执行循环体,直到<condition>
为真
跳转语句
continue
:跳出循环的当前轮break
:跳出整个循环
函数
- 函数定义
function_name() {
<command>
[return ...]
}
function function_name {
<command>
[return ...]
}
- 函数调用
function_name [argv,...]