Shell
[toc]
概述
Shell 是提供给用户的用以访问操作系统服务的程序.
sh(Bourne Shell)是一个早期的重要shell, 1978年由史蒂夫·伯恩编写, 并同Version 7 Unix一起发布.
bash(Bourne-Again Shell)是一个为GNU计划编写的Unix shell.1987年由布莱恩·福克斯创造. 主要目标是与POSIX标准保持一致, 同时兼顾对sh的兼容, 是各种Linux发行版标准配置的Shell, 在Linux系统上/bin/sh往往是指向/bin/bash的符号链接.
简单说, sh是bash的一种特殊的模式, sh就是开启了POSIX标准的bash, /bin/sh
相当于 /bin/bash --posix
Shell 脚本
执行程序
Shell 脚本的第一行常常写为
1
#!/bin/sh
虽然 # 开头的是注释, 但是这个写在第一行就是指定使用哪个解释器来翻译.
变量
在交互模式下逐行输入下面命令
1
2
3
4
5
6
7
8
9
# 定义变量(不能加空格)
foo=bar
$foo
foo = bar # 会将 foo 视作可执行文件,从而报错
# 单引号不会转义,双引号会转义
echo "$foo" # bar
echo '$foo' # $foo
环境变量
shell 内置了一些常用的变量. 可以通过 printenv
来打印出所有的变量. 也可以通过c语言的stdio.h库的 environ变量进行打印.
1
2
3
4
5
6
7
8
9
#include<stdio.h>
extern char** environ;
int main(){
char ** var;
for(var = environ;*var!=NULL;++var){
printf("%s\n",*var);
}
return 0;
}
export
命令可以进行环境变量的增删改查, 但是仅限于当前登录活动.
环境变量相当于上文提到的本地变量的区别就是, 本地变量仅能在当前shell中使用, 而不能在当前shell执行的脚本或者子进程中使用. 但是环境变量可以.
$USER # 用户名
$HOME # 个人目录
$PATH # 环境变量的路径
$DISPLAY # X窗口服务器名称和显示器编号
函数
编写文件 mcd.sh
1
2
3
4
5
6
#!/bin/sh
mcd(){
# $1 就是传入的第一个参数,类似 argv[1]
mkdir -p "$1"
cd "$1"
}
然后执行 source mcd.sh
, 看似无事发生, 实际上已经将 mcd导入了 shell 中, 在之后就可以直接使用了.
mcd new_folder
会创建 new_folder 文件夹并 cd 进去
特殊变量
$0
- 脚本名$1
到$9
- 脚本的参数.$1
是第一个参数, 依此类推.$@
- 所有参数$#
- 参数个数$?
- 前一个命令的返回值(往往0表示正常退出, 如果用c语言的话, 就是main函数或者exit的返回值)$$
- 当前脚本的进程识别码!!
- 完整的上一条命令, 包括参数. 常见应用: 当你因为权限不足执行命令失败时, 可以使用 sudo !! 再尝试一次.$_
- 上一条命令的最后一个参数. 如果你正在使用的是交互式shell, 你可以通过按下 Esc 之后键入 . 来获取这个值.
逻辑操作
有趣的是, 根据上文的 $?
, true 的返回值恒为0, false 返回值恒为1.
&&
链接两个命令, 仅当第一个返回值为0时才执行第二个. 为方便记忆, 即前面成功才执行后面.
||
链接两个命令, 仅当第一个返回值为1时才执行第二个. 为方便记忆, 即前面失败才执行后面.
;
也是如此, 不过是无论第一个的返回值如何, 都会执行第二个
1
2
3
4
5
true && echo "success"
false && echo "This is not print"
false || echo "success"
true || echo "This is not print"
false ; echo "This will always run"
通配符
*
: 匹配任意长度的任意字符.如 foo*
可以匹配 foo foo1 foobar
+
: 匹配单个任意字符.如 foo*
可以匹配 foo1 foo2
{}
: 花括号用于自动填充字符串, 如 ls image.{png,jpg}
,会自动扩展为 ls image.png image.jpg
综合运用
创建 example.sh
, 然后赋予执行权限
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/bin/bash
# 日期 以 $(cmd) 的形式就是将命令执行完的输出替换占位符
echo "Starting program at $(date)" # Date will be substituted
# 文件名 , 参数数量 , 进程号
echo "Running program $0 with $# arguments with pid $$"
# 遍历参数
for file in "$@"; do
# 让标准输出 和 错误输出都重定向到 null
grep foobar "$file" > /dev/null 2> /dev/null
# When pattern is not found, grep has exit status 1
# We redirect STDOUT and STDERR to a null register since we do not care about them
# ne 即 not equal ,判断 grep 的返回值,如果没有 foobar,那么在文件末尾加上 # foobar
if [[ $? -ne 0 ]]; then
echo "File $file does not have any foobar, adding one"
echo "# foobar" >> "$file"
fi
done
运行结果如下
1
2
3
4
$ ./example.sh mcd.sh example.sh
Starting program at 2021年 02月 08日 星期一 12:15:07 CST
Running program ./example.sh with 2 arguments with pid 8972
File mcd.sh does not have any foobar, adding one
常用工具
fd
fd 是用于替换掉 find的工具. 此工具更快而且更易使用
1
2
# 在 ~ 下递归搜索后缀为 sh 的, 文件名中含 mcd 的文件
fd mcd -p ~ -e sh
参考资料
MIT 6. NULL B站视频 MIT 6. NULL 英文讲义 MIT 6. NULL 中文讲义 Advanced Linux Programming