MIT-Missing-Semester
这门课程包含许多大学的课堂上不会涉及但却对每个 CSer 无比重要的工具或者知识点。例如 Shell 编程、命令行配置、Git、Vim、tmux、ssh 等等。其中的一些内容对于我们来说可能略有耳闻,或者只知道如何使用而不知道其原理,那么这堂课无疑是了解这些工具的good way。通过这门课程的学习,我了解到了linux的一些高端操作,以及为什么用linux的电脑才是真正的掌握自己的电脑。
Part 1 shell入门
一些简要的文件系统操作这里不再记录。
重定向输入输出 ,<
,>
missing:~$ echo hello > hello.txt
missing:~$ cat hello.txt
hello
missing:~$ cat < hello.txt
hello
missing:~$ cat < hello.txt > hello2.txt
missing:~$ cat hello2.txt
hello
重定向输出,(行末追加)>>
管道符|
允许我们将一个程序的输出和另外一个程序的输入连接起来:
missing:~$ ls -l / | tail -n1
drwxr-xr-x 1 root root 4096 Jun 20 2019 var
missing:~$ curl --head --silent google.com | grep --ignore-case content-length | cut --delimiter=' ' -f2
219
上图第二条为对google.com的信息进行数据整理操作,在part4会有更多操作实例。
顺带一提,利用管道符和xclip
软件可以非常方便的将命令行中你想要的输出进行拷贝,具体用法:
ls | xclip -sel c
- 一个很基础实用的软件
tee
,作用是一个三通管道,通过输入流进入后重定向输出到一个文件中,同时在命令行输出输入流。
下面是一个有趣的操作例子
在linux中,系统被挂在在/sys
文件中,sysfs
文件则暴露了一些内核(kernel)参数。 因此,不需要借助任何专用的工具,就可以轻松地在运行期间配置系统内核。注意 Windows 和 macOS 没有这个文件, wsl也不行()
我们可以对电脑屏幕亮度进行更改。 首先我们来到/sys/class/backlight
中,以我的电脑为例,其中有
yoo_koishi@YOOkoishi /s/c/b/intel_backlight> ls
actual_brightness brightness max_brightness scale type
bl_power device@ power/ subsystem@ uevent
等文件,这个时候我们就可以通过修改brightness文件来更改屏幕亮度了(需要root权限),一般来说,我们可能希望使用
~$ sudo echo 3 > brightness
来修改文件,不过此时命令行提示我们权限不够,这是为什么? 关于 shell,有件事我们必须要知道。|、>、和 < 是通过 shell 执行的,而不是被各个程序单独执行。 echo 等程序并不知道 | 的存在,它们只知道从自己的输入输出流中进行读写。 所以,根据这点,使用刚才提到的tee
软件
~$ echo 3 | sudo tee brightness
就可以实现对屏幕亮度的更改了。
此外还有我们还可以对键盘上的灯光进行操控。
在/sys/class/leds
目录下可以对电脑的灯光进行控制。通过相同的操作,我们可以利用大写锁定键的灯光来实现邮件提醒的操作。
Part 2 shell工具与脚本
shell脚本
大多数 shell 都有自己的一套脚本语言,包括变量、控制流和自己的语法。shell 脚本与其他脚本语言不同之处在于,shell 脚本针对 shell 所从事的相关工作进行了优化。因此,创建命令流程(pipelines)、将结果保存到文件、从标准输入中读取输入,这些都是 shell 脚本中的原生操作,这让它比通用的脚本语言更易用。
在 bash 中为变量赋值的语法是 foo=bar
,访问变量中存储的数值,其语法为 $foo
。 需要注意的是,foo
=
bar
(使用空格隔开)是不能正确工作的,因为解释器会调用程序 foo
并将 =
和 bar
作为参数。 总的来说,在 shell 脚本中使用空格会起到分割参数的作用,有时候可能会造成混淆。
Bash 中的字符串通过 '
和 "
分隔符来定义,但是它们的含义并不相同。以 '
定义的字符串为原义字符串,其中的变量不会被转义,而 "
定义的字符串会将变量值进行替换。
foo=bar
echo "$foo"
# 打印 bar
echo '$foo'
# 打印 $foo
bash
支持if
,case
,while
,for
这些控制流关键字。 bash
也支持函数,它可以接受参数并基于参数进行操作。
特殊变量 bash中有一些特殊的变量,具有特定的含义,下面是一些常用的特殊变量。
$0
- 脚本名$1
到$9
- 脚本的参数。 $1 是第一个参数,依此类推。$@
- 输入的参数的具体内容(将输入的参数作为一个多个对象,即是所有参数的一个列表)。$*
- 输入的参数的具体内容(将输入的参数作为一个单词)。$#
- 参数个数$?
- 前一个命令的返回值$$
- 当前脚本的进程识别码!!
- 完整的上一条命令,包括参数。常见应用:当你因为权限不足执行命令失败时,可以使用 sudo !! 再尝试一次。$_
- 上一条命令的最后一个参数。如果你正在使用的是交互式 shell,你可以通过按下 Esc 之后键入 . 来获取这个值。
示例代码:
#!/bin/bash
echo "脚本的名字是:$0"
echo "输入的第一个参数:$1"
echo "输入的第二个参数:$2"
echo "输入的参数个数:$#"
echo "所有参数(使用\$@):$@"
echo "所有参数(使用\$*):$*"
echo "上一个命令的返回值:$?"
echo "当前 Shell 环境的进程ID号:$$"
n=1
echo "使用\$@的参数列表为:"
for temstr in "$@"
do
echo "第$n个参数是:$temstr"
let n+=1
done
n=1
echo "使用\$*的参数列表为:"
for temstr in "$*"
do
echo "第$n个参数是:$temstr"
let n+=1
done
假设脚本名为 test.sh
,执行命令 ./test.sh 1 2 3
,输出结果如下: 脚本的名字是:./test.sh
输入的第一个参数:1
输入的第二个参数:2
输入的参数个数:4
所有参数(使用$@):1 2 3 4
所有参数(使用$*):1 2 3 4
上一个命令的返回值:0
当前 Shell 环境的进程ID号:12345
使用$@的参数列表为:
第1个参数是:1
第2个参数是:2
第3个参数是:3
第4个参数是:4
使用$*的参数列表为:
第1个参数是:1 2 3 4
返回值妙用 命令通常使用 STDOUT
来返回输出值,使用 STDERR
来返回错误及错误码,便于脚本以更加友好的方式报告错误。 返回码或退出状态是脚本/命令之间交流执行状态的方式。返回值 0 表示正常执行,其他所有非 0 的返回值都表示有错误发生。
退出码可以搭配 &&
(与操作符)和||
(或操作符)使用,用来进行条件判断,决定是否执行其他程序。它们都属于短路 运算符(short-circuiting) 同一行的多个命令可以用;
分隔。程序true
的返回码永远是 0,false
的返回码永远是 1。
所以,我们可以写出类似
false || echo "Oops, fail"
来进行一些error handling。
命令替换(command substitution) 可以通过$( CMD )
这样的方式来执行CMD
命令,例如,如果执行 for file in $(ls)
,shell
首先将调用 ls
,然后遍历得到的这些返回值。还有一个冷门的类似特性是 进程替换(process substitution), <( CMD )
会执行 CMD
并将结果输出到一个临时文件中,并将 <( CMD )
替换成临时文件名。这在我们希望返回值通过文件而不是 STDIN
传递时很有用。例如, diff <(ls foo) <(ls bar)
会显示文件夹 foo
和 bar
中文件的区别。
统配(globbing)
通配符 - 当你想要利用通配符进行匹配时,你可以分别使用
?
和*
来匹配一个或任意个字符。例如,对于文件foo
,foo1
,foo2
,foo10
和bar
,rm foo?
这条命令会删除foo1
和foo2
,而rm foo*
则会删除除了bar
之外的所有文件。花括号
{}
- 当你有一系列的指令,其中包含一段公共子串时,可以用花括号来自动展开这些命令。这在批量移动或转换文件时非常方便。如
convert image.{png,jpg}
# 会展开为
convert image.png image.jpg
编写 bash 脚本有时候会很别扭和反直觉。例如 shellcheck↗ 这样的工具可以帮助你定位 sh/bash 脚本中的错误。
在 shebang
(#!
)行中使用 env
命令是一种好的实践,它会利用环境变量中的程序来解析该脚本,这样就提高了您的脚本的可移植性。env
会利用我们第一节讲座中介绍过的 PATH
环境变量来进行定位。 例如,使用了 env
的 shebang
看上去是这样的 #!/usr/bin/env python
。
一些工具
- 觉得
man
太显示内容长了?不妨试试TLDR pages↗ fd
,find的替代品,更友好一些。fzf
模糊查找shell的历史记录。rg
,ack
,ag
,grep
的替代品,更符合人的直觉。tree
,broot
,等软件可以很好的展示文件结构。- 更好的文件管理器:
nnn
,ranger
。 xrags
它可以使用标准输入中的内容作为参数。 例如ls | xargs rm
会删除当前目录中的所有文件。
一些终端推荐
个人目前使用较多的终端为tmux
和fish
,另外也推荐zsh
等其他终端。
Part 3 Vim
vim作为一个老牌的代码编辑器,功能十分强大,在程序设计基础实验这门课里,我们或多或少接触了vim。当按下:wq
的时候,vim神秘的面孔,逐渐显现。
基本操作模式
- 正常模式:用于导航和执行命令。
- 插入模式:插入文本。
- 可视模式:选择文本块。
- 命令模式:执行特定命令。
切换模式:
- 按
i
进入插入模式。 - 按
v
进入可视模式。 - 按
:
进入命令模式。 - 按
<Esc>
返回正常模式。
提示:由于频繁使用 <Esc>
,可以考虑将其映射到更方便的键位。
基本操作
插入文本
- 在正常模式下,按
i
进入插入模式。 - 输入文本。
- 按
<Esc>
返回正常模式。
缓冲区、标签页和窗口
- 缓冲区:Vim 中打开的文件。
- 标签页:可以包含多个窗口。
- 窗口:显示缓冲区的视图,一个缓冲区可以在多个窗口中打开。
命令行模式
:q
:退出。:w
:保存。:wq
:保存并退出。:e {文件名}
:打开文件。:ls
:列出缓冲区。:help {命令}
:查看