BUAA-OS-lab0
lab0主要侧重工具链的教学,一定程度上培养了同学们读项目的能力。
上机考点可以大致分为 Makefile 和 bash 文件编写,26年恰好把它们分成了两部分,exam只考了 Makefile ,extra只考了 bash 。
当然如果你不幸被分到了新主机房且恰巧上机时段网络不稳定,可能还会考察一点 git 相关知识。
课下学习
文件操作
ls
-l长格式(权限、属主、大小、时间)-a显示隐藏-d只显示目录-R递归列出子目录-S按文件大小排序-r反向
touch
- 可以批量创建
touch file1 file2 file3 - 只更新修改时间
touch -m file
mkdir
- 递归创建父目录
mkdir -p dir - 一次创建多个
mkdir dir1 dir2 dir3 - 设置权限
mkdir -m 755 dir - 补充:
- 不加
-p时,父目录不存在会报错 - 目录已存在时,
mkdir dir报错;mkdir -p dir不报错
- 不加
cd
.当前目录..上级目录
rmdir
删除空目录
rm
-r递归删除-f强制删除
pwd
显示完整目录
mv
- 递归复制不加
-r - 重命名
mv old.txt new.txt - 重命名目录
mv folder1 folder2 - 移动到目录
mv file folder/ - 移动多个文件到目录
mv file1 file2 file3 folder/ - 不覆盖已存在文件
mv -n a.txt /folder
cp
复制多个文件到目录
cp file1 file2 file3 /folder递归复制目录
cp -r /folder1 /folder2-i复制前确认-f强制-n不覆盖已存在-u仅当源文件较新时复制
rm
- 可同时删除多个
-r递归-f强制-i删除前确认-v显示删除过程
tree
tree显示当前目录树tree dir显示指定目录树tree -L 2显示层级tree -d只显示目录tree -a显示隐藏文件tree -htree -p显示文件大小和权限tree -f显示完整路径
cat
- 查看多个文件,顺序拼接
cat file1 file2 - 带行号
cat -n file - 仅给非空行编号
cat -b file - 显示不可见字符
cat -A file - 显示行尾$
cat -e file - 显示tab
cat -t file
echo
输出变量/文本到终端
- 输出字符串
echo "hello" - 输出变量的值
name="dawn";echo "$$name"(在Makefile里要用$$获得变量的值) - 常见选项
-n不换行-e启用转义echo -e "a\nb\tc"-E禁用转义echo -E "a\nb\tc"
head
-n显示前x行内容-c显示前x字节内容
tail
-n显示后x行内容-c显示后x字节内容-f输出后续添加内容
查找操作
find
find FileDir
-name按文件名 ,不区分大小写-iname按文件名区分大小写-type f只看文件-type d只看文件夹-maxdepth 2最大搜索层次-mindepth 1最小搜索层次-perm 644按权限搜索-path ./xxx/file指定文件夹搜索
grep
grep content Dir
-i忽略大小写-n显示行号-r递归搜索如果Dir位置是一个文件名,默认省略文件名前缀,只显示 行号:内容
强制显示要带
-H如果Dir位置真的是Dir,显示结果为 Dir/…/file:行号:内容
注意这里
./Dir和Dir亦有区别-w整词搜索-x整行匹配(一模一样)-c统计content在文件夹下每个文件内出现次数
wc
- 统计行数
wc -l file - 统计字符数
wc -m file - 统计单词数
wc -w file - 统计字节数
wc -c file - 只想获得数字可搭配重定向
wc -l < file
或许也可用循环做
1 | 统计行数: |
编辑操作
chmod
1 | -rwxr-xr-- |
每组三位分别是 r(读)、w(写)、x(执行),没有权限用 - 表示
三位代表的2进制数传化成十进制
数字模式赋权
chmod num file如chmod 440 file1符号模式赋权
1
2
3
4chmod +(-)x(r\w) file #所有人
chmod u(g/o/a)+(-)x(r/w) file #特定人
chmod u(g/o/a)=x(r/w) #设置
chmod g-x a+x file # 同时修改多个递归修改整个目录
-R查看权限
ls -l file
diff
比较文件差异 diff [option] file1 file2
-r递归比较目录-q仅显示文件是否不同,不显示具体差异diff file1 file2 > /dev/null 2>&1(防止污染终端)echo $?获取返回值- 0相同
- 1不同
- 2文件不存在(出错)
-i忽略大小写-w忽略所有空白
sed
基本语法
1 | sed [选项] '指令' 文件 |
替换s
1 | # 基本替换(只替换每行第一个) |
注意,-i在指令外面代表直接修改,在指令末尾代表忽略大小写
g代表一整行,行数的选择看s之前
删除 d
1 | # 删除第 3 行 |
打印 p(配合 -n 使用)
1 | # 只打印第 3 行(-n 抑制默认输出) |
插入 i 和追加 a和代替c
1 | # 在第 3 行前插入 |
多个指令同时执行
1 | # 用 -e 串联 |
实用例子
1 | # 删除行首空格 |
常用选项
| 选项 | 含义 |
|---|---|
-i |
直接修改原文件 |
-n |
抑制默认输出 |
-e |
指定多个指令 |
-r / -E |
使用扩展正则表达式 |
awk
基本格式
awk '条件 {动作}' file
分隔符处理
分隔符可以是字符串or正则表达式
- 多字符串分隔
awk -F"::" '{print $1,$2}' file - 例如“逗号或分号”都算分隔
awk -F'[,;]' '{print $1,$2}' file
1 | # 用 : 分列 |
默认空白分隔是特殊规则
FS=" "时,会把连续空格/tab 当一个分隔符,并忽略行首行尾空白像
|、.、*这类正则元字符,按需转义
BEGIN/END 块
awk 'BEGIN{print "start"} {print $1} END{print "end"}' file
统计/计算
1 | # 第一列求和 |
条件判断
1 | # 第三列大于100的行 |
常用指令
1 | # 打印整文件 |
cut
按列切文本,如 cut -d ':' -f 1 file 为以 : 为分隔符,取第一行
-d选择分隔符-f选择列数-c按字符位置截取cut -c 1-3 file按字符取前三个
sort
按行排序
-n数字排序-r倒序-t选择分隔符-k选择排列列数sort -t ':' -k 2 file按第二列排序
uniq
去除相邻重复行(比较单位是行,所以最好要先切割,再排序,再去重)
-c统计每行出现次数-d只显示重复的行-u只显示不重复的行
1 | # 切割去重 |
gcc
基本用法
gcc [选项] 源文件 -o 输出文件
-o(指定输出)-S(生成汇编)-Wall(显示警告)-c(仅编译不链接)-M(列出依赖)-I(指定头文件目录)
GCC 编译过程大致可以理解成:
- 预处理:-E (.i)
- 编译成汇编:-S (.s)
- 汇编成目标文件:-c (.o)
- 链接成可执行文件
Makefile
基本规则
1 | target:depandence |
每一个command代表一个shell,所以如果要连续需要用 ;
- 伪目标
.PHONY(是target,每次make都重新执行) - 变量
- 定义
:=直接展开 - 引用
$(变量名)
- 定义
- 自动变量
$@目标$<第一个依赖$^所有依赖
Git
- 常用命令:
git init:初始化仓库git status:查看文件状态git add:将修改加入暂存区git commit:提交暂存区内容(-m添加说明)git log:查看提交历史git branch:查看/创建分支(-d删除、-D强制删除、-a查看所有)git checkout:切换分支或恢复文件(-- <file>丢弃工作区修改)git reset:版本回退(--hard、HEAD~、<hash>)git push/git pull:与远程仓库同步git rm --cached:从暂存区移除文件git clean -f:清除未跟踪文件git restore:撤销工作区修改(与checkout --类似)git config:设置用户信息(user.email、user.name)
- 三棵树模型:工作区、暂存区、HEAD
shell脚本编程
脚本结构
#!/bin/bash 指定解释器
如果题目说用 bash xxx 运行则可以不写
- 位置参数:
$0(脚本名)、$1、$2… 表示传入参数,$#参数个数,$*或$@所有参数 - 特殊变量:
$?(上一条命令返回值)
条件判断
if-else
1 | if [ 条件 ]; then |
| result | C | bash退出码 |
|---|---|---|
| true | !=0 | 0 |
| false | 0 | !=0 |
1 | if diff -q file1 file2 > /dev/null 2>&1 ; then |
[[ $a == foo* ]]模式匹配[[ $a == "foo*" ]]纯字符串比较
循环
for
for 变量 in 列表; do ... done
while
while [ 条件 ]; do ... done
按行读取
1 | while read -r line; |
循环控制
continue break
算术运算
(())
算术语句/条件执行,用于流程控制、变量自增
1 | i=1 |
$(())
把里面当“算术表达式”求值后再展开成字符串(用于赋值、输出、传参)
1 | i=1 |
函数运算
- 定义:name() { … }
- 调用:直接写 name
- 参数:$1、$2、$@
- 状态码:return 0(真)/1
- 真正传回文本:用 echo + $(…)
1 | add() { |
重定向
重定向只重定向标准输出,错误信息仍显示在屏幕上,因为错误走的是 stderr
1 | # 只重定向stdout |
>等价于1>,重定向标准输出&1指代stdout当前指向的地方,所以>&1之前要有>/dev/null虚拟设备,相当于丢弃>:覆盖输出到文件>>:追加输出到文件<:从文件输入2>:重定向错误输出&>:同时重定向标准输出和错误
管道符
| 将前一个命令的stdout作为后一个命令的stdin
在sed中如果对同一段文字进行多次修改,用 | 和用 -e 效果是一样的
1 | echo "hello 123" | sed -e 's/hello/hi/' -e 's/123/456/' |
课上测试
exam
一上来给干晕字了,vim一下Makefile更是花花绿绿。本人的做题顺序是先看代码,看不懂的地方再回去看字。
考试的时候被卡的地方是编译依赖出问题,因为当时我把所有target都加入了 .PHONY 里,只保留 run、 all 和 clean 就过了。试后得知此处为助教的小巧思。但很符合Makefile的复用思想,减少了重复编译对资源的浪费。总结来说就是,.PHONY 里放用来执行动作的target,不放用来生成文件的target。
考试后发现一个很有意思的点。以下是部分能够测试通过的代码。
1 | COMMON_FILE = main.c post_calc.c common.h |
但课下与同学交流的时候发现有同学根本没注意到要使用 -I 来链接头文件库,即这样看上去完全没有链接库的代码也可以通过。
1 | ver1: $(COMMON_FILE) calc1.c |
询问助教之后发现此处因为.h文件和调用它的.c文件在同一级文件夹下,C编译的时候会按#include "common.h",在main.c所在当前目录搜到common.h。
extra
可能extra在要独立完成一个shell脚本吧。
感觉这道题本人爽在看题不仔细。上机时助教有给提示说可以调整if-else顺序实现退出,当时还困惑这里为什么会给提示,直到下机后听到同学吐槽不知道该exit到哪去()。
可能要注意的点如下
mkdir -p -m xxx不会设置递归创建的父级文件夹的权限,需要手动设置grep "ERROR" "./logs/$1/error.log">/dev/null在为true时返回0,所以有两种写法1
2
3
4if grep "ERROR" "./logs/$1/error.log">/dev/;
grep "ERROR" "./logs/$1/error.log">/dev/null
if [[ $? -eq 0 ]];
可能遇到的网络问题
前半小时一直在断网,本人误把 makefile.inc 文件当成Makefile的swap文件删了,这个时候就要用到伟大的git。
当时的本人还不太会git,所以执行了git restore .,复原了完工大半的Makefile。重活一次本人一定要使用git restore makefile.inc 来恢复被删掉的文件。