MIT 的 missing-semester 课程学习笔记,学一些常用的计算机工具,总时长大概 个小时,不算太长。
Lecture 1: The Shell
查询时间
date # Sat Jul 1 16:38:09 CST 2023
打印
echo hello # hello
查询程序地址
which echo # /usr/bin/echo
查询当前目录
pwd # /mnt/c/Users/屋顶上的小丑
改变当前目录
cd /xxx # 打开 xxx 目录
cd ./xxx # 打开当前目录下的 xxx
cd .. # 回到父目录
cd - # 回到前一个所在的目录
cd ~ # 打开主目录
查看当前目录文件
ls # 当前目录下的文件:xxx...
ls .. # 父目录下的文件:xxx...
ls -l # 给出当前目录下文件的更多信息
# -a 查看所有文件 -h 以人类可以理解的格式输出,例如M和G,-t 以最近访问顺序排序,--color,以彩色文本输出
第一位字符中 d
表示该文件是一个目录。后面九个字符每三个字符为一组,分别代表文件所有者,用户组及其他所有人的权限。三个字符分别为 r
,w
和 x
,代表读入,写入与执行的权限。对于文件来说很好理解,对于目录来说 r
代表能否查看目录下的文件,w
代表能否重命名,创建和删除目录下的文件,x
代表能否搜索(如果要进入某个目录,需要对路径上的所有目录都有执行权限)。-
代表没有对应的权限。
查看帮助
xxx --help # 给出 xxx 指令的用法
重命名或移动文件
mv xxx yyy # 将文件 xxx 重命名为 yyy,或者将文件/目录 xxx 移动到 yyy 目录下
复制文件
cp xxx yyy # 将文件 xxx 复制到目录 yyy 下
删除文件
rm xxx # 删除文件 xxx
删除目录
rm -r xxx # 递归删除目录 xxx 及其下面的所有文件
rmdir xxx # 删除目录 xxx,当且仅当 xxx 为空
创建目录
mkdir xxx # 创建目录 xxx
查询手册
man xxx # 查询命令 xxx 的手册
可以用 tldr
看更简洁的手册。
清空命令行
ctrl+L # 清空命令行
输入输出流
echo xx > file # 覆盖输出 xx 到文件 file 中(不存在就新建)
echo xx >> file # 追加输出 xx 到文件 file 中
输出文件内容
cat file # 输出 file 中的内容
cat < file # 把 file 中的内容当作输入,由于此时未指定输出,因此默认为控制台,也会把内容输出到命令行中
cat < file > file2 # 把 file 中的内容当作输入输出到 file2 中
管道
| # 将 | 左侧的程序输出作为右侧程序的输入
ls -l / | tail -n1 # 将 ls -l / 当作输入,并输出最后一行到命令行中
进入 root 模式
sudo xxx # 以 root 的身份执行 xxx
sudo su # 切换用户为 root
exit # root 登出
向 /sys
目录写入内容只有 root
能办到。
多重定向
xxx tee yyy # 把输入 xxx 输出到 yyy 中,同时在屏幕上输出,即同时打印内容和写入文件
创建文件
touch xxx # 创建文件 xxx
Lecture 2: Shell Tools and Scripting
变量赋值
foo=bar # 把 foo 变量的值赋为 bar,不能有空格
echo $foo # bar
用 export
可以设置环境变量,全局 shell 有效。
字符串输出
echo '$foo' # 输出 $foo,单引号不转义
echo "$foo" # 输出 bar,双引号转义
特殊参数
-
$0
- 脚本名 -
$1
到$9
- 脚本的参数。$1
是第一个参数,依此类推。 -
$@
- 所有参数 -
$#
- 参数个数 -
$?
- 前一个命令的返回值 -
$$
- 当前脚本的进程识别码 -
!!
- 完整的上一条命令,包括参数。常见应用:当你因为权限不足执行命令失败时,可以使用sudo !!
再尝试一次。 -
$_
- 上一条命令的最后一个参数。如果你正在使用的是交互式 shell,你可以通过按下Esc
之后键入 . 来获取这个值。
逻辑运算符
如果指令正常执行则返回 ,否则说明有错误发生。true
的返回值一直是 ,false
的返回值一直是 。以上可以通过 &&
和 ||
连接,都是短路运算符。
命令替换
foo=$(pwd) # foo 变量存储着 pwd 这个指令,因此通过 $foo 得到的是 pwd 的返回值
echo "We are in $(pwd)" # 会将 pwd 的返回值替换 $(pwd) 再输出
进程替换
cat <(ls) <(ls ..) # 输出是当前目录和父目录下的文件的拼接。因为 <(ls) 会将 ls 的输出存入某个临时文件,然后再用这个临时文件替换掉 <(ls)。
diff <(ls foo) <(ls bar) # 显示这 foo 和 bar 两个文件夹的区别
实例
#!/bin/bash
echo "Starting program at $(date)" # date会被替换成日期和时间
echo "Running program $0 with $# arguments with pid $$"
for file in "$@"; do
grep foobar "$file" > /dev/null 2> /dev/null
# 如果模式没有找到,则grep退出状态为 1
# 我们将标准输出流和标准错误流重定向到Null,因为我们并不关心这些信息
if [[ $? -ne 0 ]]; then
echo "File $file does not have any foobar, adding one"
echo "# foobar" >> "$file"
fi
done
作用是扫描参数提供的所有文件,如果没找到 foobar
就在文件末尾追加一个。
其中 2>
为错误输出流,>
为正常输出流,即 strerr
与 stdout
,可以将两者结合为 &>
。-ne
即不等号
通配
?
匹配一个字符,*
匹配任意个字符。
{}
会把括号里的内容扩展开。例如 convert a.txt a.in
和 convert a.{txt,in}
是一样的。
shebang
#!/usr/local/bin/python
#!/usr/local/bin/env python
import sys
for arg in reversed(sys.argv[1:]):
print(arg)
第一行第二行即为 shebang,代表了 shell 去用这一行路径的东西来运行脚本。第二行使用了 env,即环境变量。
报错
要先安装 shellcheck 包。
shellcheck xxx.sh # 检查 xxx.sh 的错误并报告
查找文件
# 查找所有名称为src的文件夹
find . -name src -type d
# 查找所有文件夹路径中包含test的python文件
find . -path '*/test/*.py' -type f
# 查找前一天修改的所有文件
find . -mtime -1
# 查找所有大小在500k至10M的tar.gz文件
find . -size +500k -size -10M -name '*.tar.gz'
# 删除全部扩展名为.tmp 的文件
find . -name '*.tmp' -exec rm {} \;
# 查找全部的 PNG 文件并将其转换为 JPG
find . -name '*.png' -exec convert {} {}.jpg \;
-empty
可以查找空文件和目录。-mtime
代表文件上次修改的时间,-ctime
代表文件创造的时间,-atime
代表文件上次访问的时间。-print0
会直接将文件名后加 \0
一起输出,默认则是输出一个文件换行再输出。
可以用 fd
替代,fd PATTERN
代表以 PATTERN 模式搜索。用 locate
能更加高效,因为它依靠索引来查找,类似数据库,但是只能通过文件名查找。
查找代码
grep "xxx" yyy # 查找文件 yyy 中所有包含子串"xxx" 的行并输出,-i 表示不区分大小写,-n 可以打印行数,--color 可以高亮关键字,-C num 代表查找结果上下文 num 行,-v 将结果反选,-R 会递归进入子目录查找所有文件,-c 只输出行数
用 ripgrep(rg)
更加快速。
# 查找所有使用了 requests 库的文件
rg -t py 'import requests'
# 查找所有没有写 shebang 的文件(包含隐藏文件)
rg -u --files-without-match "^#!"
# 查找所有的foo字符串,并打印其之后的5行
rg foo -A 5
# 打印匹配的统计信息(匹配的行和文件的数量)
rg --stats PATTERN
查找 shell 命令
history
能输出输入的历史命令。能结合 |
查找特定的命令。用 ctrl+R
也能查找历史,可以配合模糊查找工具 fzf
使用。
文件夹导航
用 ls -R
就能递归输出目录内的文件结构,用 tree
或者 broot
能够输出目录树,或者用更完整的文件管理器 nnn
或者 ranger
。
用 fasd
或者 autojump
可以查找最常用或最近使用的文件和目录。
分支与循环
if [[ condition ]]; then
#statements
fi
while [[ condition ]]; do
#statements
done
until [[ condition ]]; do
#statements
done
for (( i = 0; i < 10; i++ )); do
#statements
done
统计数量
wc -c # 统计字节数
wc -l # 统计行数
wc -w # 统计词数
wc -m # 统计字符数
xargs
xargs
可以将输入分割成批,每个批中有很多分割片段,然后将这些片段按批交给 xargs
后面的命令进行处理。简而言之,它可以将标准输入中的内容作为参数传递。
-d
可以自定义一个分割符,以这个符作为分割成多个命令行参数传递给后面指令。特别的,如果分割符是 null
,可以直接用 -0
。
压缩
# 压缩与解压 .tar 文件
tar -cvf xxx.tar ...
tar -xvf xxx.tar
# 压缩与解压 .gz 文件
tar -czvf xxx.tar.gz ...
tar -xzvd xxx.tar.gz
# 压缩与解压 .bz2 文件
tar -cjvf xxx.tar.bz2 ...
tar -xjvf xxx.tar.bz2 ...
# 压缩与解压 .zip 文件
tar -czvf xxx.zip ...
tar -xzvf xxx.zip
# 压缩与解压 .rar 文件
rar a xxx.rar ...
rar x xxx.rar