计算机教育缺失的一课

the Missing Semester of your CS education

The Missing Semester of your CS education,共11节课,每节课约1h。

确实不错,手敲命令+讲解。多使用Linux

学校应该在大一教这个,而不是TM的大学物理

课程网站

1
https://missing.csail.mit.edu/

lectrue1: overview & shell

主要是 Linux 基本命令

overview

  • 如何利用存在的工具使我们开发更加高效,以及如何更好的利用我们的计算机,这是这门课主要解决的问题。

shell

讲述了Linux Shell.主要是bash

1
shell是运行在终端中的文本互动程序,bash(GNU Bourne-Again Shell)是最常用的一种shell。是当前大多数Linux发行版的默认Shell。

日期

1
$ date

打印

1
$ echo "hello world"

路径列表

1
2
3
# 当我们执行一些命令时,会遍历PATH寻找;比如说使用 /bin/echo 时只需在终端输入 echo 就行
# `:` 分隔
$ echo $PATH

寻找命令的路径

1
2
3
4
5
6
$ which echo
/usr/bin/echo

# whereis可以寻找到 原始代码、二进制文件,或是帮助文件
$ whereis bash
bash: /usr/bin/bash /usr/share/man/man1/bash.1.gz

目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# change dir
$ cd /home/username

# `..` 代表上一级目录。`.` 代表当前目录
$ cd ..

# `-` 当前目录和之前目录下切换
$ cd /mnt
$ cd /home
$ cd -
/mnt
$ cd -
/home
$ cd -
/mnt

$ pwd
/home/

# 有趣的一点是,`..`使用过多的情况。`/`目录没有上一级,最高只能到 `/`
$ ../../../../../../bin/date
xxx

文件操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#  list: 列出指定目录下所有的文件,默认当前目录
$ ls
test.txt
$ ls /
lib root home ...

# 移动文件位置 move
$ mov test.txt /tmp
$ ls /tmp
test.txt
# 重命名
$ cd /tmp
$ mov test.txt tmp.txt
$ ls
tmp.txt

# 复制 copy
$ cp tmp.txt tmp1.txt

# 删除
$ rm tmp1.txt

# 创建一个文件
$ touch tmp2.txt

# 创建文件夹
$ mkdir tmpdir
$ rmdir # rm -r 也行

# 查看文件所有内容
$ cat test.txt
hello world

# 查看文件开头结尾,默认10行
$ head test.txt
$ tail test.txt

# cat 重定向
$ cat test.txt > tmp.txt # 如果没有就创建tmp.txt.
$ cat < text.txt > tmp.txt # 同上
$ cat test.txt >> tmp.txt # > 会清除原先内容。>> 代表append,追加而不清除

命令参数和帮助文档

1
2
3
4
5
6
7
8
9
# 一般命令也存在参数,一般为 -?
$ ls -l /home
# d: dir rwx: 权限Read, Write, eXecute(也代表是否能够访问此目录)。
# 从前到后: owner权限 group权限 其余用户权限 owner group size date dir_name
drwxr-x--- 27 user user 4096 Jun 21 14:42 user

# 查帮助文档,q退出。manual pages
$ man ls
-l use a long listing format

管道:左侧的输出作为右侧的输入

1
2
3
4
$ ls / | tail -n 1 # -n 1 最后一行

# 有时候给root权限文件写入时,比如sudo echo 123 > tmp.txt会报错,可用以下命令
$ echo 123 | sudo tee tmp.txt

root user: 超级管理员。尽量少用,在运行错误的程序时会破坏计算机。

1
2
3
4
5
6
# 用户使用root权限
$ sudo <commond>

$ sudo su
password: xxx
root# `#` 代表root 权限

/sys 文件夹:各种内核参数,显示设备的状态。linux系统将其视为文件暴漏给用户,意味着我们可以操作从而控制某些设备

有趣的命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 删库跑路,无需确认强制删除所有的文件
$ rm -rf /

# fork 炸弹, 会耗尽电脑资源
$ :(){ :|:& };: # 理解为一个函数递归调用 `:` 为函数名
:() {
: | :&
};
:


# 由于ls打错频率比较高,会出现一个动画(火车)
sl


# cowsay 打印一段话 cowthink 类似,但是think
cowsay "nb"
-l 查看动物,需要安装
-f 指定动物
____
< nb >
----
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||

# figlet 字符字 toilet类似
figlet love
""#
# mmm m m mmm
# #" "# "m m" #" #
# # # #m# #""""
"mm "#m#" # "#mm"


# 打印一堆信息和logo,装b使用
neofetch

现代化的命令:开发者使用rust写了很多更加现代的命令,可以替代一些老命令

1
2
exa  =>  ls
bat => cat

Q&A

shell, console, terminal?

1
xxx

lectrue2: shell script

Linux 脚本的使用

语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$0      运行文件名
$1-$9 参数
$? 获取上一个运行的错误 0代表正确,1代表出错
$_ 存储上次运行的结果
$# 参数个数
$$ 进程id
$@ 所有的参数组成,可迭代

!! 在执行时会替换为上一个执行的命令
# 变量
foo=bar
echo "$foo"

# 变量赋值为命令的结果
foo=$(pwd)

循环

1
for xxx; do... done

条件

1
if xxx; then...fi

编程脚本

1
2
3
# 加入magic line 指定解析器
# 一个 称之为 `shebang` 的东西
#!/usr/bin/pyton3

man 命令的其余选择 tldr ,更加简洁(too long; don’t read)

1
tldr tar

递归查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
find .     # . 代表当前路径,我们可以指定比如 `/` 
-name # 名称,可以使用通配符
-type # 类型 d(dir) f(file)
-mtime -1 # 修改时间modify time, 1指1天
-exec rm {} \; # 对于寻找的文件执行命令 \; 不可缺少

fd # 更加现代化的find

locate # 默认查找整个计算机

grep # 应用于查文件内容
-R # 递归

rg # ripgrep

查找使用过的命令

1
2
3
4
5
方向键的上

fzf

zsh 的一个插件,在输入时会显示曾经输入的类似命令

更清晰的目录结构

1
2
tree
broot

lecture3: editor VIM

vim

  • normal mode: 可以控制光标,执行命令…
  • insert mode: 就是文本编辑器。
  • command mode: 在insert mode 输入 : 在输入命令
  • visual mode: 有点像使用鼠标选中一块连续区域
  • replace mode

进入 insert mode

1
2
3
4
5
6
i   insert, 在光标前插入
a append,在光标后
I 行首
A 行尾
o 下一行 open a new line
O 上一行

normal mode

1
esc  也可以自行配置

保存,退出

1
2
3
4
-- write quit
normal mode 下 :wq

-- 加入 ! 代表强制执行

normal mode 光标操作。

  • 所有的都可以在前面加一个数字,代表count
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
h   左移
j
k
l

w 向前(后一个单词)移动一个 word, 也就是一个单词,标点或者空格分开。在单词第一个字母
b 向back移动 word back of word
e 移动到单词末尾 end of word
0 移动到行首
$ 行尾
^ 一行第一个非空字符

ctrl-u up类似鼠标向上滚动
ctrl-d down 向下

H highest 屏幕第一行
L low 屏幕最后一行
M mid 屏幕中间一行

删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
d  删除一个字符,配合移动光标使用
dd 删除一行,可以使用p
D 删除至末尾
d1G 删除到第一行
dG 删除到最后一行
n1, n2d 删除n1-n2行
-- 在这里 a around; i inside
di( 删除括号内的内容
da( 删除包括括号的东西
c change a word 删除(d 类似)并进入insert mode

x 删除后面一个字符
<num>x 删除后面num个字符
s 删除字符进入insert mode
S 删除一行进入insert mode

复制粘贴

1
2
3
4
5
y   yank 
yy 复制当前行
y1G ...
p paste在这一行后面
P 这一行前面

跳转到某一行

1
2
3
4
5
g
gg 第一行
G 最后一行
<num>G 跳转到num行
:<num> 跳到第num行

撤销之前的操作

1
2
u undo
ctrl + r redo 恢复撤销的内容

visual mode: 允许我们改变一列,整块复制等

1
2
3
v   字符可视,开始和结束两个字符中间所有内容,退出按v
V 行可视行,光标所在行,退出 V
ctrl+v 块可视,开始和结束光标的矩阵,退出ctrl+v

搜索

1
2
3
4
f<word>  本行查找
/<word> 查内容,全文
n 继续向下继续找 next
N 继续向上找

更改环境

1
:set nu   显示行号

~ 反转大小写

括号匹配

1
%   到匹配的另一个括号处

tab, window, buffer

1
2
3
4
5
tab: 我理解为 在系统中打开vim这个软件两次,就是两个tab

window: vim 屏幕,可以分屏

buffer: 打开文件,文件具有buffer, 同一个文件同一个buffer,实时。

配置,vim 在执行前会加载一个 ~/.vimrc 文件。

  • 我们可以DIY自己的喜欢的按键。
  • 安装插件实现更多的功能

neovim

本人使用(IDE不香吗?) neovim 配合 LazyVim。可DIY. 然后再 VsCode 安装neovim插件,同步使用。

  • vim常用的操作,neovim也能用。

keymap

Leader 为 space 键,挺好用, 使用这些功能也需要安装对应插件

打开终端

1
2
3
4
<leader>ft   当前
<leader>fT /

ctrl-/ 打开和关闭

快速注释

1
<leader>gc    但是需要指定的 LSP

不同文件间(buffer)切换

1
<leader>bb

文件搜索 telescope

1
2
3
4
5
-- 文件名
<leader><leader> <esc><esc> 退出

-- 文件内容
<leader>sb

文件内

1
2
<c-s> 保存文件
<leader>fn new file

分屏

1
2
3
4
5
6
7
8
<leader>w|  <leader>|  生动表示竖着
<leader>w- <leader>- 横着

-- 切换,和normal mode 下的移动联系
<C-h>
<C-j>
<C-k>
<C-l>

侧边栏 neo-tree

1
2
3
4
5
6
7
8
9
<leader>fe   当前
<leader>fE /

<leader>e 当前
<leader>E /

-- 侧边栏进入文件后,在进入侧边栏
<C-h>
<C-l>

lazy.nvim 插件管理非常受欢迎。作者开发的插件和配置。

lecture4: data wrangling

处理数据的某些手段

grep,搜索

1
2
grep "word" tmp.txt   # 查询
-R # 递归,可以查找文件夹下的文件

sed,在搜索替换是一个好用的工具。需要学习一下正则表达式

1
2
3
4
5
sed 's/<pattern>/<replace>/'
pattern: 匹配模式,支持正则表达式
replace: 将匹配到的pattern 转换为 replace。当捕获时 \num 代表第num个捕获,打印

# sed 默认支持很老的正则表达式,使用 `-E` 参数,支持现代化的正则

一些正则表达式使用,使用在线网站练习。regular expression

1
2
3
4
5
6
[0-9]   0,1,2...9 其中一个数字
* 贪婪模式,1次或多次
? 0 或 1 次
. 任意字符
() 捕获括号内的内容
^ 开头 $ 末尾

wc

1
wc -l    word count,统计大小 -l line 几行

排序,字典序。去重

1
2
3
4
5
sort    默认字典序排序
-n 按照数值的大小进行排序
-k 指定排序的列数
uniq 可去除重复内容
-c 记录出现次数

awk,基于流的处理,是一门编程语言

1
2
3
4
awk 'script'  执行一个脚本
awk '{print $0}'

-F 指定分隔符

cut,类似编程语言的split

1
2
3
4
5
6
7
cut [option] filename
-d 字节为单位分割
cut -d 3 第三个字符 -d 3-9,12 第3-9,12个字符
-f fields 与-d一起使用,表示区域
cut -d : -f2 第1-2个冒号之前的内容
-c character 字符为单位,处理中文好用
-b

编程语言在命令行的使用? 各种管道,图片音频处理?

lecture5: command-line environment

优雅的使用命令行

job control

Linux 系统的 signal机制

1
2
3
4
man signal : 会看到不同的number 和 name

ctrl-c SIGINT signal interrupt
ctrl-\ SIGQUIT

ctrl+z

1
2
3
4
5
# 暂停,可执行其他任务
& 在命令后加入,表示后台执行
jobs 查看后台任务,暂停或者运行 pid
fg %num / bg %num 使暂停的任务继续运行,fg 恢复到前台。bg 恢复到后台执行。front back ground
kill %num 停止

terminal multiplexers

  • 终端复用,在一个terminal window干很多事情。

  • 很多的终端都存在分屏等操作,但是课程介绍神器 tmux,更加神奇。

    • 不 kill session,其中的命令会一直执行下去。只需要开启一次终端。
    • 在ssh 服务器时非常的好用
  • session, window, pune

    • 启动tmux, 会开启一个session, 明显的是下面会出现一行数据。
    • window 创建一个新的shell 终端。
    • pune 面板,一个窗口可以分很多的面板。

命令行操作

1
2
3
4
5
6
7
8
9
10
11
12
tmux
下部会出现状态栏,分别是session window1 windiow2... 时间等

tmux a # attach 进入session
-t name 指定名称

tmux new -t <name> 创建并指定名称
tmux kill-session -t <num/name> 杀死指定的session

tmux ls 查看所有的session

tmux splitw -h/-v pane 横竖分割window

默认快捷键。可以先按一下 ctrl-b 在按其余的,不需要同时按。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
prefix = Ctrl-b
# session prefix keymap
prefix d dettach session
prefix s 列出所有session vim快捷键下的选择

# window
prefix c create a new window
prefix p previous window
prefix n next window
prefix <n> 第n个窗口,n是个数字
prefix w 列出window 和 session(s ?), jk 选择,enter进入
prefix , 重命名


# pune
prefix x 关闭pune
prefix % 竖直切割
prefix " 水平切割
prefix 方向键 选择 pune
prefix x 关闭pune
prefix z 最大化当前窗口,在按一次退出
prefix ! 分离pune 进入window
持续按prefix 方向键 改变pune大小

配置快捷键, ~/.tmux.conf。我使用的是基于网上找的 tmux-conf

1
2
3
4
5
6
ctrl-b 距离非常有点阴间,因此需要改。ctrl-a 是qemu快捷键。因此选择ctrl-x

prefix I install plugins
prefix alt I uninstall

alt + 方向键,pune 移动

aliases

给常用的命令设置别名

  • bash 的~/.bashrc 文件
1
2
3
4
# 注意,不要存在 ll = 'ls -l' 因为在shell script中,空格是有意义的
alias ll='ls -alF'
alias la='ls -A'
alias l='ls -CF'

dotfiles

各种配置文件。大部分是文件

  • bash -> ~/.bashrc
  • zsh -> ~/.zshrc。我们的zsh美化也是修改此文件。
  • vim -> ~/.vimrc
  • tmux -> ~/tmux.conf
  • neovim -> ~/.config/nvim 目录下的 基于lua 配置
  • ssh -> ~/.ssh 这是一个目录。可以配置公私钥,免密登录。
    • 自己的机器 ssh-keygen 命令生成公私钥
    • 将公钥放入服务器的 authorized_keys。权限一般是600
1
2
3
4
ssh-copy-id -i ~/.ssh/id_rsa.pub  <username>@<IP>

# 常用的ssh 传输文件 scp 和cp命令差不多,给出路径
scp tmp.txt <user>@<IP>:/tmp

感兴趣的可以学习一下 NixOS, 一个基于配置文件的操作系统😋

大部分配置都可在 github 查找到,如果不想自己配置,直接 clone/fork 一个其他人的。

bash 前面的一串的修改

1
PS1="user>"   我们shell 前面的一串

问题:如何在本机和服务器同时使用tmux

相同的配置会可能产生冲突,或者会使我们不知道操作的是哪一个,因此我们需要不同的配置文件。最简单的就是使用两个 prefix

lecture6: virsion control(git)

git版本控制

开发项目,团队合作,文件损坏的回退。。。git都可以在很大程度上帮助我们,不需要删除在重新下载😘

  • git 抽象建模。使用有向无环图进行抽象
    • 顶层root,文件夹抽象为tree, 文件抽象为blob
    • commit: 每次commit 产生一个类似 snapshot(快照)的东西,保存当前的状态以及一些信息(作者,描述…)。
    • 通过 mapping<string, object> 进行管理.string指文件的哈希值(SHA-1),object是我们文件保存的地址(‘snapshot’在磁盘中的地址)。每次修改,commit会产生新的hash
  • reference: git需要的是文件的哈希值,对于人类毫无意义,因此存在另一个 mapping<string, string> 。我们使用可以人类方便阅读的字符串,映射到hash,然后在寻找到文件
  • git 几个状态,可以看看 Git工作流和核心原理,非常有趣。在学习时,可以想象一下有向无环图进行理解。
    • 工作区
    • 暂存区
    • 本地仓库
    • 远程仓库

git 的配置

1
2
3
# 配置用户名,邮箱。自己的账户。
git config --global user.name "your name"
git config --global user.email "your email"

创建一个git仓库

1
2
3
4
5
6
mkdir demo
cd demo

git init # 本地仓库初始化,出现一个 `.git` 的目录

git status # 查看仓库状态,非常常用

日志,查看提交的文件

1
2
# oneline 去除一定的信息
git log --all --graph --decorate --oneline

其中存在一个 HEAD 指针指向当前工作的分支。

我们想忽略某些文件时

1
2
3
4
# 使用 gitignore 文件配置
touch .gitignore

*.jpg # 忽略所有的 jpg文件

本地操作

1
2
3
4
5
6
7
8
9
10
git init
# 写点新文件
echo "hello" > readme.md # 处于untracked 状态,使用git status 会存在提示

# 暂存区
git add readme.txt # 处于tracked 状态

# 提交到本地仓库
git commit # 进入文件,进行描述修改的内容
-m "message" # message 代表描述,简短描述可以这样使用

提交到远程代码仓库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 1. 远程创建后直接clone下来,然后再写内容
git clone "your-repo"
git remote -v # 查看和那些仓库有联系
git push # 有.git文件夹,可以找到仓库提交

# 2. 我们在本地写了很多文件,但是中途想起来没有使用git,我们如何做?
## 还是需要先创建仓库
## 然后再本地
git init
git add .
git commit
git remote add "name" git@<your-repo> # 关联远程仓库。
# name 自己取,可以关联很多仓库,根据名字区分

## 但是本地仓库一般是master,某些平台是main(也可以选,但是默认是main),这一步可能出错,需要切换分支 `git checkout -b main`

## 远程分支不存在会创建一个
git push "仓库名称" "本地分支名":"远程分支名" # 如果本地分支名与远程分支名相同,则可以省略冒号

## 删除远程仓库
git remote rm "仓库名"

从远程仓库到工作区。比如团队合作中,远程仓库更新了,我们需要先同步。

1
2
3
4
5
6
7
8
9
10
11
# 直接同步到本地,会直接更新本地文件
git pull

# 由于pull 一步到位,也可分步进行
## 先更新到到本地仓库
git fetch
## diff 对比区别
git diff
# 查看本地仓库和工作区的区别
## pull 合并 = git fetch + git merge
git pull

分支操作。我们参与开源项目时,建议创建一个新分支push,由项目负责人决定是否合并(merge)。

1
2
3
4
5
6
7
8
9
10
git branch "name"  # 创建一个分支 
-vv # 查看分支信息

git checkout "name" # 切换分支
-d "name" # 删除分支
-D "name" # 暴力删除
-b "name" # 创建一个分支然后切换

git merge # 将别的分支合并到 `当前分支` 中
# 可能会存在冲突,在某一个相同的位置存在不同的内容.保存的话,自己决定然后修改冲突文件就行

git clone 会复制远程的所有文件,包含其所有的 snapshot 我们可以使用 --shallow 忽略这些

git 回滚:当我们在一个分支中commit后发现一个巨大的错误,需要回退到之前的版本。改变 HEAD 指针

1
2
3
4
5
6
7
8
9
10
11
12
git stash   # 回退到上一个commit 版本
git bisect # 比较强大的工具

git reset
--hard # 会丢失最新的代码修改
--soft # 将 HEAD 指针回退到指定提交,不改变暂存区和工作区的内容

HEAD^ # 上一个 版本
HEAD~<num> # 回退num个版本
<hash> # 回退到指定哈希值的版本

git revert -n hash # 将版本复制一份,不会销毁

现在的IDE中存在git相关的工具,也更加方便了我们的使用。

也存在其余的版本控制工具,svn, repo……

repo 使用

repo 更适合多个仓库的管理,平常我们见的项目都是一个仓库。但是向Android这样的依赖上百个git仓库来说,依靠git并不是多么好使用,因此google 开发了repo工具,本质是一个python脚本,基于git。

初始化

1
2
3
4
repo init
-u git@<repo>/mainfest.git # 默认为google的仓库 https://gerrit.googlesource.com/git-repo
# 这个目录下最简单只需要 default.xml
-b # 指定branch

-u后的仓库, xml文件(默认default.xml, 我们可以自己选择),然后我们拉取的时候会将所有的git仓库拉取下来

1
2
repo init -u <repo>
-m 指定xml文件

拉取代码到本地

1
repo sync -c

分支

1
2
3
repo branch  # 查看分支

repo start <branch_name> --all # 创建分支并进入

lecture7: debugging & profiling

调试程序以及性能分析

查看日志,打印日志,制作日志

调试器

  • GNU gdb,可以调试几乎所有的二进制程序
  • python pdb,python调试
  • 浏览器调试js

性能测试

  • 测试一个程序运行 time

lecture8: metaprogramming

如何更高的管理项目、测试、依赖管理。使用 makefile

Makefile 的使用比较简单。

1
2
3
4
5
目标: 依赖
命令 //前面必须是tab键

main: main.c
gcc main.c -o mian

一些语法

1
2
3
4
5
6
$^   所有的依赖项
$< 一个依赖项
$@ 目标文件
echo 打印
% 通配符
* 也是通配符

我们编译某些开源项目时也会使用 make 命令,可以看看别人怎么写的。

当然,还有其他的选择,比如 cmake

lecture9: security & crypto

安全很多,也是一门专门的学科,想要深入就需要学专业课

hash: 用作信息摘要,签名,检查文件的完整性。

  • md5
  • sha-1/2/3

对称加密

  • DES
  • AES

非对称加密

  • RSA
  • ECC

数字签名

lecture10: potpourri

大杂烩:讲述一些概念,熟练还得在以后多练习

键盘映射

  • 键盘上的Caps Lock几乎不怎么使用,我们可以重新配置一下(比如比较小的Esc),让其发挥作用
  • windows可以使用powertoy

守护进程,daemon

命令行参数

  • 我们使用的命令可以带有参数

Window Manage

VPN

Jupyter Notebook: 交互式编程

GitHub: 代码托管平台

  • 创建自己的仓库
  • 提issue,解决问题
  • pr: pull request,自己写的提交给作者
  • merge 别人的请求

lecture11: Q&A

来自学生的问题

如何进行操作系统的学习?

  • learn by exercise: 学习比较出名的 OS 课程,完成相应的lab,实现自己的OS

…… 自己看