【Missing Semester】02 Shell工具和脚本
本文为计算机教育中缺失的一课 The Missing Semester of Your CS Education 笔记
Shell 脚本
第一个 Shell 脚本:
-
新建一个文件
touch a.sh
。 -
文件内容:
#!/bin/bash echo "Hello World !"
-
更改文件权限
chmod +x a.sh
。 -
执行
./a.sh
或source a.sh
:source
命令:执行当前文件中的命令。
变量
- 为变量赋值:
foo=bar
。- 变量名和等号之间不能有空格,在 Shell 脚本中使用空格会起到分割参数的作用。
- 访问变量中的值:
$foo
或${foo}
。- 花括号是可选的,帮助解释器识别变量的边界。
字符串
- 字符串可以用单引号,也可以用双引号,也可以不用引号:
- 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的。
- 双引号里可以有变量,双引号里可以出现转义字符。
- 获取字符串长度:
${#string}
。
数组
-
用括号来表示数组,数组元素用"空格"符号分割开:
array_name=(value0 value1 value2 value3)
。 -
读取数组元素值:
${数组名[下标]}
。- 使用
@
符号可以获取数组中的所有元素:${数组名[@]}
。
-
获取数组长度:
length=${#array_name[@]}
或length=${#array_name[*]}
例——变量、字符串、数组:
#!/bin/bash
var1="Hello"
echo $var1
echo "double quote" "$var1"
echo "single quote" '$var1'
var2=("Monday" "Tuesday" "Wednesday")
echo "lenth of array" ${#var2[@]}
echo "lenth of first element" ${#var2[0]}
# 输出
# Hello
# double quote Hello
# single quote $var1
# lenth of array 3
# lenth of first element 6
传递参数
$0
:脚本名。$1
到$9
:脚本的参数。$1
是第一个参数,依此类推。$@
或$*
:所有参数。- 以
"$1" "$2" … "$n"
的形式输出所有参数。 $*
以一个单字符串显示所有向脚本传递的参数。
- 以
$#
:参数个数。$?
:前一个命令的返回值。0 表示没有错误,其他任何值表明有错误。$$
:当前脚本的进程识别码!!
:完整的上一条命令,包括参数。常见应用:当你因为权限不足执行命令失败时,可以使用sudo !!
再尝试一次。$_
:上一条命令的最后一个参数。
例——传递参数:
echo "-- \$* 演示 ---"
for i in "$*"; do
echo $i
done
echo "-- \$@ 演示 ---"
for i in "$@"; do
echo $i
done
echo "$0"
echo "$#"
执行结果:
bogon:tmp wangxinyuan$ ./a.sh hhh w x y
-- $* 演示 ---
hhh w x y
-- $@ 演示 ---
hhh
w
x
y
./a.sh
4
bogon:tmp wangxinyuan$ echo "$?"
0
运算符
算数运算符:
+
-
*
/
%
=
==
!=
。- 例:
val=`expr $a + $b`
。 - 表达式和运算符之间要有空格。
- 乘号
*
前边必须加反斜杠\
才能实现乘法运算。 - 条件表达式要放在方括号之间,并且要有空格,例
[$a == $b]
。 - 使用运算符赋值的几种方法:
c=$[$a+$b]
。let "c=a+b"
。((c=a+b))
。
关系运算符:
-eq
-ne
-gt
-lt
-ge
-le
。- 例:
if [ $a -eq $b ]
,注意方括号前后两边都要有空格。 - 只支持数字,不支持字符串,除非字符串的值是数字。
- 在 bash 中进行比较时,尽量使用双方括号
[[ ]]
而不是单方括号[ ]
,这样会降低犯错的几率,尽管这样并不能兼容 sh。
布尔运算符:
- 非
!
、或-o
、与-a
。 - 例:
if [ $a -lt 100 -a $b gt 15]
逻辑运算符:
&&
||
- 例:
if [[ $a -lt 100 && $b -gt 100 ]]
字符串运算符:
=
、!=
、检查字符串长度是否为 0-z
、检查字符串长度是否不为 0-n
、检查字符串是否为空$
。
文件测试运算符:
- 检测文件是否是目录:
-d file
。 - 检测文件是否是普通文件:
-f file
。 - 检测文件是否可读:
-r file
。 - 检测文件是否可写:
-w file
。 - 检测文件是否可执行:
-x file
。 - 检测文件是否为空 / 文件大小是否大于 0:
-s file
。 - 检测文件(包括目录)是否存在:
-e file
。
流程控制
if else:
- bash 的流程控制不可为空,如果
else
分支没有语句执行,就不要写这个else
。 condition
处可以是[ ]
也可以是命令。
if condition1
then
command1
elif condition2
then
command2
else
commandN
fi
# 也可写到同一行
if condition; then command1; command2; ... fi
例——elif diff a.txt b.txt:
if [ 1 == 2 ]
then
echo hi
elif diff a.txt b.txt
then
echo hello
fi
for:
for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done
while:
while condition
do
command
done
until、case esac、break、continue:咕咕咕~
函数
- 可以带
function fun()
定义,也可以直接fun()
定义,不带任何参数。 - 参数返回:可以显示加
return
返回;如果不加,将以最后一条命令运行结果作为返回值。return
后跟数值\(n(0~255)\)。 - 函数返回值在调用该函数后通过
$?
来获得。 - 当 \(n>=10\) 时,需要使用
${n}
来获取参数。 - 所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至 Shell 解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。
[ function ] funname [()]
{
action;
[return int;]
}
其它
- 命令替换:
$( CMD )
。- 如执行
for file in $(ls)
,Shell 首先将调用ls
,然后遍历得到的这些返回值。
- 如执行
- 进程替换:
<( CMD )
。 - 通配:
- 通配符
?
:匹配一个任意字符。 - 通配符
*
: 匹配0个或任意多个字符,也就是可以匹配任何内容。- 例:对于文件 foo, foo1, foo2, foo10, bar:
rm foo?
删除 foo1 和 foo2。rm foo*
删除除bar
外的所有文件。
- 通配符
[ ]
:匹配中括号中任意一个字符。[-]
代表范围,[^]
代表逻辑非。- 例:
[abc]
匹配a
或b
或c
;[a-z]
匹配所有小写字母;[^0-9]
匹配一个不是数字的字符。
- 例:
- 花括号
{ }
:当你有一系列的指令,其中包含一段公共子串时,可以用花括号来自动展开这些命令。- 例:
convert image.{png,jpg}
会展开为convert image.png image.jpg
。
- 例:
- 通配符
- shebang
#!
:#!/usr/local/bin/python
让内核知道去用 python 解释器而不是 Shell 命令来运行这段脚本。
例——命令替换、通配:
#!/bin/bash
echo "Starting program at $(date)" # 命令替换
echo "Running program $0 with $# arguments with pid $$"
for file in "$@"
do
# 如果模式没有找到,则grep退出状态为 1
# 我们将标准输出流和标准错误流重定向到Null,因为我们并不关心这些信息
grep foobar "$file" > /dev/null 2> /dev/null
if [[ $? -ne 0 ]]
then
echo "File $file does not have any foobar, adding one"
echo "# foobar" >> "$file"
fi
done
执行结果:
bogon:tmp wangxinyuan$ touch text{1,2,3,4,5}.txt # 通配
bogon:tmp wangxinyuan$ echo "# foobar" > text3.txt
bogon:tmp wangxinyuan$ ./a.sh text{1,2,3,4,5}.txt
Starting program at 2022年 2月19日 星期六 15时20分19秒 CST
Running program ./a.sh with 5 arguments with pid 19714
File text1.txt does not have any foobar, adding one
File text2.txt does not have any foobar, adding one
File text4.txt does not have any foobar, adding one
File text5.txt does not have any foobar, adding one
Shell 工具
文件管理
cat
:连接文件并打印到标准输出设备上。chmod
:控制用户对文件的权限的命令。- 菜鸟教程:chmod
chmod [ugoa...][[+-=][rwxX]...][,...] filename
。u
user,g
group,o
others,a
all。+
增加权限,-
取消权限,=
唯一设定权限。r
read,w
write,x
excute,X
只有当该文件是个子目录或者该文件已经被设定过为可执行。
- 可使用 8 进制来设定权限。
diff
:比较文件的差异。-b
不检查空格字符的不同。-B
不检查空行。-q
仅显示有无差异,不显示详细信息。
find
:在指定目录下查找文件。-name
在当前目录下递归查找符合参数所示文件名的文件。
locate
:在保存文档和目录名称的数据库内,查找符合参数所示文件名的文件。mv
:为文件或目录改名、或将文件或目录移入其它位置。rm
:删除一个文件或者目录。-r
递归删除。-f
强制删除。-i
逐一确认是否要删除。
tee
:读取标准输入的数据,并将其内容输出成文件。touch
:修改文件或者目录的时间属性,包括存取时间和更改时间。若文件不存在,系统会建立一个新的文件。cp
:复制文件或目录。-r
递归赋值。
awk
:文本分析工具。read
:用于从标准输入读取数值。
文档编辑
grep
:查找文件里符合条件的字符串。sed
:依照脚本的指令来处理、编辑文本文件。let
:计算表达式,let 表达式
。
磁盘管理
cd
:切换当前工作目录。mkdir
:创建目录。pwd
:显示工作目录。tree
:列出指定目录下的所有文件,包括子目录里的文件。-a
显示隐藏文件。-d
只列出目录。
ls
:显示指定工作目录下之内容。-a
显示隐藏文件。-l
在一行显示文件详细信息。