讲解部分梳理

Linux 基本操作

  • head 输出文件首部内容
    • -n <n> 显示前 行内容
    • -c <n> 显示前 个字节内容
  • tail 输出文件尾部内容
    • -n <n> 显示末尾 行内容
    • -c <n> 显示末尾 个字节内容
    • -f 当文件增长时,输出后续添加的数据(适用于文件不断变化的情况,如日志文件)
  • ps 显示当前进程状态
    • -aux / -ef 显示所有进程
  • mv 还可以用于重命名
  • 递归参数
    • -p 从指定目录向上级目录递归 eg 创建目录
    • -r 从指定目录向其下级目录递归 eg 递归删除、拷贝
  • 在命令后加上&符号,代表让命令在后台进行
  • Ctrl Q 进入块选模式,方便删注释和缩进
    • 选择之后可以通过 I 进入插入模式,方便段落缩进
    • { } 向上/下选中 1 段

Makefile 补充

  • 开头最好写.PHONY xx (一般会写.PHONY:clean

    • .PHONY后面跟的目标都被称为伪目标,对应的命令一定会被执行
    • 作用:避免伪目标同名文件冲突;提高执行效率
  • 每一个指令 (command) 之前必须按一次制表符键来控制间隔,而不能是空格,否则 make 会报错

  • 简洁高效的写法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 使用变量维护Makefile
    # %.o: %.c 缩小编写量
    CC = gcc
    OBJ = function.o test.o
    TARGET = test

    $(TARGET): $(OBJ)
    $(CC) $(OBJ) -o $(TARGET)

    %.o: %.c
    $(CC) -c $< -o $@
  • 外层 makefile 调用内层 makefile

    1
    2
    3
    4
    5
    all:
    $(MAKE) -C subdir

    clean:
    $(MAKE) clean -C subdir

Git 补充

git branch

git branch <branch-name> 本地创建一个基于当前分支产生的分支。

git status

查看当前分支的状态,以及当前工作区的变动和暂存区的内容

git add

把一个新文件或者已经修改过的文件加入暂存区

1
2
git add <file_name>         # 将指定文件加入暂存区
git add . # 或使用.将项目所有文件加入暂存区(除.gitignore指定的文件外)

git restore

放弃修改(如果涉及到文件的增删,建议用 reset,用的时候慎重)

  • 当文件还没有通过  git add  加入暂存区时,使用  git restore <filename>  来撤销修改,使其退回到上一个 commit 的状态
  • 如果文件已经加入了暂存区,通过  git restore **--staged** <filename>  来取消暂存。
  • 其他错误回退命令
    • 若文件未添加至暂存区:使用  git checkout <filename>  将文件替换成暂存区版本,或  git checkout .  将工作区内的所有文件替换成暂存区内的文件(谨慎使用)
    • 若文件已添加到暂存区但未提交到本地仓库:使用  git reset HEAD .  撤销暂存区的所有修改至工作区,接下来就回到了上一步
    • 若已提交到本地仓库:使用  git reset --hard HRAD^  将所有文件回退至上一版本

git reset

git reset --hard 可以恢复文件的版本,既可以往旧版本恢复,也可以往新版本“恢复”。但–hard 是 reset 命令唯一的危险用法,使用时如果工作区的文件还未提交,git 会覆盖它从而导致无法恢复。所以使用时要小心谨慎

git checkout

切换分支

注意,在切换时,需要保证目前所有文件的状态均为“未修改”(没有修改过,或者已经提交)

git commit

使用  git commit -m <message>  这个命令将暂存区的修改提交到储存库中。当 message 参数有空格时需要把 message 用引号括起来

git rm

  • --cached 从暂存区删除,但不删除工作区文件,不加则两边都删

删除分支

git branch -d(D) <branch-name> 删除本地对应分支

git push origin --delete [branch_name] 删除远程分支

建立孤儿分支

  1. git checkout --orphan <branch name> 创建孤立分支
  2. git rm -rf . 删除拷贝的所有文件
  3. touch add commit
  4. git push -u origin <branch name> 创建远程分支并将其关联

提交空文件夹

Git 只对跟踪文件进行版本控制,而不跟踪文件夹本身。如果想要提交一个空文件夹到 Git 仓库中,可以在文件夹中添加一个占位文件,例如“.gitkeep”

合并分支

git merge 自动合并

  • 如果自动合并不成功(存在冲突)
    • 不想继续合并了,退出合并的中间状态
      1
      git merge --abort
    • 处理冲突 最好在图形化界面上解决,更方便
      1. git status查看冲突的文件
      2. 编辑冲突文件,解决冲突(记得删除三行分隔线)
      3. git add 冲突文件
      4. git commit -m "提交信息"

文本处理三剑客

grep 查找

1
2
3
4
5
6
7
grep [选项] PATTERN FIL
选项(常用):
-a 不忽略二进制数据进行搜索。
-i 忽略大小写差异。
-r 从目录中递归查找。
-n 显示行号
-q 安静模式,不打印结果

sed 替换、删除等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
sed [选项] '命令' 输入文本
选项(常用):
-n:安静模式,只显示经过sed处理的内容。否则显示输入文本的所有内容。
**-i:直接修改读取的档案内容,而不是输出到屏幕。否则,只输出不编辑。
-E 选项用于启用扩展正则表达式,这样我们就可以使用更加灵活的正则表达式语法。**
命令(常用):
[行号]a[内容]:新增,在行号后新增一行相应内容。行号可以是“数字”,在这一行之后新增,
也可以是“起始行,终止行”,在其中的每一行后新增。当不写行号时,在每一
行之后新增。使用$表示最后一行。后面的命令同理。
[行号]c[内容]:取代。用内容取代相应行的文本。
[行号]i[内容]:插入。在当前行的上面插入一行文本。
[行号]d:删除当前行的内容。
[行号]p:输出选择的内容。通常与选项-n一起使用。
s/re(正则表达式)/string(/g):将re匹配的内容替换为string /g表示全文替换,不加则替换第一个

常用用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
awk '$1>2 {print $1,$3}' my.txt
# awk 'pattern action' file ,pattern 为条件,action 为命令,file 为文件
# 命令中出项的 $n 代表每一行中用空格分隔后的第 n 项
# 该命令的意义是文件 my.txt 中所有第一项大于 2 的行,输出第一项和第三项

awk -F, '{print $2}' my.txt
# -F 选项用来指定用于分隔的字符,默认是空格
# 该命令的意义是打印用 , 分隔后的第 2 项

grep -n "all" my.txt > result.txt
# 打印含有“all”的行并显示行号

sed -i "s/$2/$3/g" $1
# 将传入的第一个参数(文件)里的所有 第二个参数 替代为 第三个参数

shell 编程注意

  • 首句#!/bin/bash

  • 等号与运算符两边不允许有空格,-op的两边与[]内部两边必须有空格

    • [ 实际是一个可执行程序
    • 双中括号同样用于判断条件,比单中括号更高级。例如在双中括号中可以直接使用逻辑运算:[[ $a -gt 1 && $a -lt 100 ]],而使用单中括号只能写为[ $a -gt 1 ] && [ $a -lt 100 ]
  • 获取变量值建议使用 ${var},花括号避免歧义

  • 引用变量必须在双引号中进行

  • $# 传递的参数个数;$* 一个字符串,内容是传递的全部参数

  • let 赋值运算 eg let i=i+1 等价于 i=$(($i+1)) (双括号是计算值)

  • 流程控制

    1
    2
    3
    4
    5
    6
    7
    if [ 表达式1 ]; then
    命令列表1;
    elif [ 表达式2 ]; then
    命令列表2;
    else [ 表达式3 ]; then
    命令列表3;
    fi
    1
    2
    3
    4
    5
    6
    7
    8
    while [ 表达式 ];do
    命令列表;
    done

    for name in [参数列表];do
    命令列表;
    done
    #最后都有done

Lab0

踩坑

在评测前先确保自己对于文件的变更已经全部添加到暂存区(使用合适的  git add  和  git commit )!!注意先addcommit ,新建的文件需要先添加跟踪才会到暂存区,才能用commit提交。

  • 整型转字符串 sprintf
    1
    2
    3
    4
    5
    int n;
    scanf("%d",&n);
    char str[20];
    sprintf(str,"%d",n);
    printf("%s",str)
  • 三剑客不熟悉
  • 变量使用忘了
    1
    2
    3
    4
    5
    自增操作!
    #错误写法
    a=a+1
    #正确写法
    a=$[$a+1]
  • 在 sed 命令中
    • 使用变量时,正则用双引号(一般只能用单引号)
      1
      2
      3
      #!/bin/bash
      name=tomas
      sed -i "s/rose/${name}/g" name.txt
      • 单引号‘’
        • 括起来的字符均作为普通字符出现
      • 双引号“”
        • 括起来的字符除 $\‘’、和 “” 之外都将作为普通字符对待,所以如果要用变量值就要用“”
      • 倒引号````
        用于命令替换,其所括字符串在被 Shell 解释时,首先执行其中的命令并将其结果代替该命令
      • $俩小括号/$中括号计算结果替换,$单小括号执行结果替代
    • 要更改源文件内容要加参数-i
      1
      2
      # 替换字符串,并更改原文件内容
      # 在sed后面加 -i,即编辑文档“edit files in place”选项
  • gcc 编译时如果头文件不在/usr/include里,一定记得-I <头文件所在位置路径>

难点分析

Linux 下命令种类多,参数复杂

  • lsmvtouch等常用命令倒是能熟练掌握,但像sedawk等参数复杂功能复杂的命令,在使用时就容易一下想不起来正确的使用方法
  • 解决方法:先通过多练习熟练,使用时若不确定先查阅资料或参考书

shell 编程中符号和空格使用非常容易出错

比如

  • 各种引号的使用
    • 单引号‘’
      括起来的字符均作为普通字符出现
    • 双引号“”
      括起来的字符除 $\‘’、和 “” 之外都将作为普通字符对待,所以如果要用变量值就要用“”
    • 倒引号````
      用于命令替换,其所括字符串在被 Shell 解释时,首先执行其中的命令并将其结果代替该命令
  • 各种括号的使用
    • $(())/$[]计算结果替换,$()执行结果替代
  • 空格的使用
    • 等号与运算符两边不允许有空格,-op的两边与[]内部两边必须有空格

Makefile 嵌套

  • makefile 嵌套的写法有很多,用$(Make) -C subdir-C 是跳转到指定路径再执行 make 操作) 或者 cd subdir && make 都可以,需要特别注意不要用混了
  • $(Make)这种操作时想要做 make clean,是$(MAKE) clean -C subdir
  • gcc编译时如果依赖的库文件不在本文件夹下,要记得用-I进行链接
  • 注意相对路径和绝对路径的使用要准确

体会

编写 Shell 脚本和 make 文件时熟练度至关重要

虽然上学期学习了系统编程,有了一定的编写 Shell 脚本和 make 文件的基础。但由于课后自主训练较少,加上假期疏于复习,导致对相关操作并不完全熟练。但 Shell 脚本和 make 文件的编写是在 Linux 环境下顺利进行实验的基础,因此需要在课后多多练习,尽快熟悉相关操作,以免后续实验中因基础操作不熟练浪费时间

debug 时利用好讨论区 & 利用好git操作

Excecise1 测评错误后,一直以为是自己哪里写错,反复检查每个文件,提交了 4、5 次,折腾了快 2 个小时还是错误。后来用git status才发现是没有用git add对新建的文件添加跟踪,导致一直没能把这部分文件成功提交 😢

后来在看讨论区时,更是发现一篇精华帖的开头就用大写加粗强调了这个问题……

exam 前准备

血泪教训

  • 在 bash 中,整个程序的返回值$?=最后一条语句的返回值$?;且在评测机中一旦程序的返回值$?为 1,它就认为这个程序报错了;
    故最好在每一个脚本文件末尾都手动写一个exit 0
    • $?表示上一条语句的返回值;$#表示传参个数

不熟操作

  • 输出 hello.c 中所有包含“os_hello”(区分大小写)的行的“os_hello”(区分大小写)以左的内容,如果一行出现多个,则输出第一次出现以左的部分
    • grep & sed
      1
      grep -E '.*os_hello' hello.c | sed -E 's/(.*)os_hello.*/\1/'
    • awk (两种均可)
      1
      2
      awk -F 'os_hello' '{print $1}' hello.c
      awk '/os_hello/ { sub(/os_hello.*/, ""); print }' hello.c
  • #var 是获取变量的长度,${#var}获取长度值
  • 字符串切片操作
    ${variable:start:count} 表示从变量 variablestart 位置开始,截取 count 个字符

  • 多层if容易忘记闭合fi
  • >> 追加会自动换行,想要不换行echo-n xx >> xx
  • $?返回值为 0 表示上一条命令执行成功,1 表示执行失败

exam & extra 复盘

踩的坑(现场解决)

  • 编写脚本文件时,想要输出文件内容,不能用 more 要用 cat
    • more 是交互式的命令,一般在终端直接使用

知识漏洞(现场没解决)

  • 书写条件判断语句时注意!debug 时优先检查条件判断是否出错!
    • less than,应该是-lt 不是 -le 🥲

awk 用法补充

  • awk + if
    1
    awk '{if($2=="'$PID'") print $3}' $FILE
    • if 语法和正常语法比较像,if() 后加空格,之后直接写命令(多为 print
  • 在 awk 中使用在 shell 中定义的变量
    使用 "' 把 shell 变量包起来,即 "'$var'" ;注意是“双引号+单引号+shell 变量+单引号+双引号”的格式
  • 将 awk 结果赋值给变量
    使用倒引号,如 var=`echo $result|awk '{print substr($result,16,3)}'