OS预习教程笔记(1)
Linux
基本概念
Linux操作系统是基于Linux内核的开源操作系统,课程使用Linux发行版为Ubuntu。
对网上的Linux环境访问后出现界面为:
1 |
|
最后一行git为用户名,学号为主机名。
本界面即为Ubentu操作系统界面,这种没有图形界面的界面被称为命令行界面CLI。
操作系统的“外壳”(Shell),也即用户界面分为命令行界面(CLI Shell)和图形用户界面(GUI Shell),我们使用的操作系统一般是GUI Shell,服务器运行的Linux一般是CLI Shell。
目录操作
在命令行界面中用户要通过命令指示操作系统,命令格式一般是:命令名 [选项] [参数] ...
cd命令
1 |
|
举例测试:
1 |
|
指令意思是通过cd指令进入了/这一目录。
Linux文件系统最顶层为根目录,就用/表达,因此上面的操作就是“切换到根目录”。根目录中有所有的二级目录,其中包括:
Linux内核启动需要的/boot,系统配置文件的/etc,存放普通用户主目录的目录/home,系统管理员的用户主目录/root,用户系统资源存放的目录/usr等。
第一行中的则是当前用户主目录的简写,用就是cd ~
指令就能回到当前用户的主目录。对于一般用户/home/用户名
,root用户就是/root
。
两行中都有的$代表当前的用户是普通用户,如果是管理员用户,即root用户,就会显示#,教程也提到后续会用$ xxx
表示需要在终端中输入xxx
,而用$代表终端中显示的符号。
同时cd命令也支持相对路径寻找目录。例如:假设目前处于根目录/,我们如果想进入/etc,不但可以通过cd /etc
完成,也可以通过cd etc
完成,此时会在当前所处目录中寻找etc目录并进入。
还需要了解返回上一级目录的方式:在Linux中用.表示当前目录,..表示上一级目录,所以cd .
就是会到当前目录,等于没有操作,而cd ..
表示回到上一级目录。
输入cd -
可以跳转到上一次访问的目录。
(在输入文件、目录名时可以用tab联想,按一次可以自动补全,按两次可以显示所有可能选择)
ls命令
1 |
|
mkdir命令
1 |
|
举例测试(在用户主目录,即~下面创建一个叫做newdir的新目录并进入):
1 |
|
pwd命令
1 |
|
举例测试(查看刚才新建的newdir目录的绝对路径):
1 |
|
rmdir命令
1 |
|
举例测试(退回用户主目录并删除newdir目录):
1 |
|
文件操作
Linux的哲学是一切皆文件,即目录、设备等都以文件形式存储在操作系统中的,以下的操作都是最普适的针对文件的命令,而之前的目录操作是针对目录这一独有文件的命令。
touch命令
1 |
|
举例测试(在用户主目录中创建一个helloworld.c文件):
1 |
|
rm命令
1 |
|
注意:rm -rf 文件
是危险命令,非必要不使用,因为这相当于不经确认的删除所有目标文件。如果使用root用户执行rm -rf /就会将所有文件和Linux一起删除。
在rm删除多个文件时可用*匹配任意长度字符串,如果要删除当前目录下的所有文件,只可以用rm -rf *
(相对路径)或rm -rf ./*
(绝对路径),而不能使用rm -rf /*
,这样就全删除了。
cp命令
1 |
|
举例测试(在用户主目录创建一个test.txt和一个dir目录,再执行cp命令)
1 |
|
这里可以发现cp在这种文件+目录的情况下就是一个复制的作用,原来的文件并不会消失。
同时我比较疑惑的是此处dir文件是否需要写成dir/
,如果看作相对路径能否省略最后的反斜杠。上网查证得到的信息是这样:
1 |
|
mv命令
1 |
|
举例测试(删除dir目录下的test.txt然后利用mv操作用户主目录中的test.txt):
1 |
|
证明在文件+目录状态下mv可以被看作一个移动或者剪切指令,将文件转移到新的目录,同时原来的位置的文件消失。
mv可以用来重命名文件(利用文件+文件的命令组合),相当于把一个文件用不同的名字移动到当前目录,例如在dir中用mv test.txt test2.txt
就可以将test.txt重命名为test2.txt。
diff命令
1 |
|
本命令只能对两个纯文本文件使用。
Vim编辑器
教程中介绍的Vim的基本操作举例:
1.创建helloworld.c文件(利用touch命令)
2.打开这个文件,需要在创建这个文件的目录下输入vim helloworld.c
来打开
3.输入内容,需要用I键进入插入模式后输入内容
4.完成输入后用Esc回命令模式,用Shift加;进入底线命令模式。观察到左下角有一个冒号,输入w按下回车即保存文件;再进入底线命令模式输入q关闭文件(可以利用wq同时完成)
在目录中创建并进入文件:
1 |
|
插入:
1 |
|
最后用Esc,Shift+;和wq保存并返回。
查找操作
在Linux下需要命令来完成搜索文件的功能。
find命令
1 |
|
例如在用户主目录中查找之前创建的在dir目录下的test.txt文件:
1 |
|
可以发现,给不给路径都可以基于当前所处目录给出正确路径。
grep命令
1 |
|
教程提到了:grep和find的区别是grep是要匹配文件内容来进行查找,而find是根据文件的属性(例如文件名)来查找。所以可以使用grep来查找某个函数名等特定文本。
例如搜查刚才编辑过的helloworld.c文件(PATTERN处要用单引号):
1 |
|
tree命令
1 |
|
tree命令是一个比ls命令更强的命令,但并不是Linux发行版预装载的。我尝试在自己的用户主目录下执行tree:
1 |
|
操作总结
命令总结
1 |
|
还有一个帮助命令:man
1 |
|
常用快捷键
1 |
|
实用工具
GCC
GCC是GNU编辑器套件,包含了C语言编辑器gcc,使用方法如下:
1 |
|
利用之前编辑的helloworld.c进行编译尝试:
1 |
|
a.out文件是gcc编译器默认的可执行程序文件名。
我们利用命令$ ./a.out
进行执行,./
代表当前目录下,如果不加上就会在系统PATH中寻找,因为不存在就会报错。
当运行a.out后就出现了输出Hello World!
。
make & makefile
GNU中有一个“make”工具,用来进行C语言项目构建,其本质为“制作出一个文件”。在开发大项目时可以用make工具将这些文件编译链接成可执行文件。
这些编译链接工作需要人工指导,而人工指导文件就是Makefile。
Makefile的基本格式:
1 |
|
target是构建的目标,可以是目标文件、可执行文件,也可以是标签;dependencies是构建目标需要的其他文件或目标;command是构建出这个目标需要执行的命令,需要注意每个命令前需要用tab而非空格进行缩进,否则会报错。
这些规则的意义就是:想要构建target需要先准备好dependencies然后一条条执行command,最后得到target。
在写完这些后,只需要在shell中输入make target就可以执行相应命令生成目标。
举例测试(用这种方式再次编译刚才的helloworld.c)
1 |
|
在Makefile文件中的编写:
1 |
|
在教程中出现了一个all,我暂时还不知道它的意思,上网搜索好像是这样:相当于给所有的目标(比如这里的hello)取一个名字,这样进行make命令时可以依次生成(此处需要后续再看看)。
ctags
ctags是方便代码阅读的工具,在教程中需要用代码跳转功能。
根据教程,需要先修改Vim的配置文件来支持ctags。修改.vimrc文件(用vim ~/.vimrc
进入并编辑),修改如下,增加两句:
1 |
|
更改helloworld.c文件:
1 |
|
相当于引入一个ctags_test.h的头文件,并创建一个叫做Pair的结构体类型,然后对其中的变量进行赋值。因此我们需要新建这个引入的头文件并进行编辑(`touch ctags_test.h):
1 |
|
相当于在头文件中给出了Pair这一结构体的具体结构,只有这样才能够在helloworld.c文件中直接利用这一人为定义的结构体。
之后执行命令ctags -R *
就会在目录中出现tags文件,用来索引符号名:
1 |
|
再次打开helloworld.c文件并将光标放在a或b上,按下ctrl+]就可以跳转到定义结构体a,b的头文件对应位置处。再利用ctrl+o就可以回到原位。
tmux
很多时候Linux要同时运行多个程序并观察输出,所以不能让它只显示一个程序;而且当终端和Linux断开连接时终端前台进程会被杀死,而我们很多时候需要一直保持连接状态,所以我们可以利用tmux来进行终端窗口和进程分离并同时显示多个进程运行。
tumx基本操作如下:
1 |
|
Git
Git是一种版本控制系统。
Git的存储
Git有三种存储位置:工作区(Working Directory),暂存区(Staging Area)和储存库(Repository)
项目在Git中以目录存在,包含工作区(.git子目录之外)和储存库(.git子目录),工作区是:目前正在编辑的版本;储存库在隐藏目录.git中,存放已提交版本内容和联系;暂存区存放确认修改但是没有提交的文件(也在.git中,但和储存库不同),如图所示:
Git也有四种状态:未跟踪(Untracked),未修改(Unmodified),已修改(Modified)和已暂存(Staged)
一个文件在储存库版本信息中都没有被记录过(例如新建的文件)就是未跟踪状态;一个文件在被跟踪后没有被改动或改动已经提交就是未修改状态;当文件已经被修改但是没有加入暂存区的情况是已修改状态;当文件已经被加入暂存区,在下次提交时会被提交则是已暂存状态。如图所示:
Git常用命令
git init
将当前目录变成空Git仓库,开始版本管理。
git clone <URL>
从互联网地址URL处克隆一个已有的储存库到当前目录,可以看作下载。
git status
查看分支状态,工作区变动和暂存区内容。
git add
将一个新文件或已修改文件加入暂存区,例如完成部分实验内容以后可以用git add .
把所有修改加入暂存区,或git add <filename>
将指定文件加入。
git restore
当文件没有通过git add
加入暂存区时可以使用git restore <filename>
撤销该文件修改到上一次commit的状态,如果已经加入了,就要用git restore --staged <filename>
来取消暂存。
git checkout
利用git checkout lab<x>
可以在各Lab(其实就是各分支)之间切换。要保证切换时所有文件是未修改状态。
git commit
用git commit -m <message>
将暂存区修改提交到储存库,若message参数有空格要用引号将其包括,此处用来给出说明性文字,可以任意填写增加可读性。
git push
这个命令将本地commit推送到远程仓库,这里应该就是GitLab。
git pull
这个命令将远程新建分支下载到本地并将远程更改合并到当前分支。评测机进行实验分支初始化后可以在开发机中用这个命令将新分支下载到本地。
Shell脚本
需要学习Shell脚本语言,需要学习其使用的Bash语法。
Shell脚本的执行
在Linux中脚本文件的后缀为.sh,该后缀对于运行没有影响,但是可以用于区分其他文件。
脚本文件的运行方式和gcc编译产生的可执行文件运行方式一样,利用./文件名
的命令即可,此处文件名中需要有.sh。
但是被执行的文件需要有”执行“权限,每个文件对不同用户都分别有”读“、”写“、”执行“的权限,新用touch
创建的文件默认没有”执行“权限,要手动添加(执行时报错Permission denied很可能就是未添加执行权限)
手动添加方式:chmod +x 文件名
Bash Shell语法
第一个例子
首先写出了教程中给出的例子(在用户子目录中创建一个新脚本文件hello.sh并vim编辑):
1 |
|
这些内容的意思是:
第一行:指定脚本默认运行程序(解释器),这里指定的是bash(#!出现在脚本文件第一行前两个字符被称为Shebang,当存在时加载器会分析其后内容并作为解释器)。
第二行:以#开头,是注释。
第三行:输出,此处是用echo将后面的字符串输出,由于Shell脚本是命令的组合,可把echo也看成命令。如果直接在终端写入这一行命令:
1 |
|
变量
在Shell中定义变量不需要指定类型,定义方法为(var_name变量名,value变量值):
var_name=value
这种定义方式等号两侧不允许有空格。同时用$var_name
可以获得变量值(建议使用${var_name}
,可以标识变量名的边界)
教程中也对刚才的脚本做出了更改:
1 |
|
运行脚本./hello.sh
也能弹出Hello, world!
脚本参数(特殊变量)
在Shell中也可以进行参数传递,在其中参数体现为特殊变量,在执行语句中参数用空格分隔,每个参数在脚本中都是一个字符串变量。第一个字符串映射到变量1,第二个映射到2……
教程中再次修改hello.sh:
1 |
|
此时运行时如果参数传递如下(应该是将字符串’world’和’OS‘当作参数进行了传递):
1 |
|
还要注意只能在双引号中添加参数变量$1等,若用单引号则会原样输出。
特殊变量:$# 传递的参数个数;$* 一个字符串,内容是传递的全部参数。
条件与循环
if语句块如下:
1 |
|
fi是if的倒写,代表语句块结束,elif和else都可以省略。
试一下教程给出的例子,这里(())是比较整数大小的表达方式:
1 |
|
尝试输入:
1 |
|
while语句块如下:
1 |
|
教程举例:
1 |
|
这段程序应该意思是读取参数$1,然后将i从1开始递增并在files目录中新建对应文件名的文件。观察结果如下:
1 |
|
函数
函数的定义方式如下:
1 |
|
几点注意:首先function和()可以省略一个;其次返回语句非必要,可以不返回值;最后若要返回则只能返回一个[0,255]间的整数Int_value,否则会出现错误结果。
函数的调用方法就是:fun_name param1 param2 ... paramN
。其中第N个参数直接在函数体中用$N调用即可,但若N>=10就要用${N},否则只会读取第一位数字。如果函数有返回值则后续用$?获取。
教程举例:
1 |
|
尝试运行:
1 |
|
可见,每个函数体中的参数变量都只会接受这个函数给的值,同时$?给出的是最后一个函数的返回值。
sed的使用
之前对文本的编辑只用了Vim工具,但是也可以通过Shell脚本中的命令对文本文件内容进行编辑输出,其工具就是sed。通过sed命令可以实现文本内容编辑和选择输出的自动化。
1 |
|
教程举例:
1 |
|
重定向和管道
重定向
重定向能够把命令的控制台输出写入文件中。Linux定义了三种流:
- 标准输入:stdin,用0表示
- 标准输出:stdout,用1表示
- 标准错误:stderr,用2表示
一般来说这些流使用的设备是控制台(所以我们能在控制台看到命令输出),但是在命令后使用>符号可以重定向输出。
例如$ ls / > lsoutput.txt
就是将根目录下文件名输出转移到lsoutput.txt这一文件中并覆盖原有内容,若想在文件后追加命令输出而不是覆盖,就使用>>符号。当然由于重定向无法在控制台中看到输出。
还要注意,实际上>是1>的简写,也就是只有标准输出被重定向,如果是标准错误的报错信息就无法进行重定向,若将错误输出也重定向到文件要使用2>符号。
自然也可以将输入重定向,这样我们就可以从文本文件中读取输入信息了。
管道
管道符号|被用来连接命令:command1 | command2 | command3 | ...
,其意义是将command1的输出传给command2的输入,以此类推。
教程举例是,cat命令是将文件内容转移到标准输出,而grep命令支持从标准输入读取文本进行查找。因此这两个命令就可以用管道连接:cat hello.txt | grep 'Hello'
,这个命令意思是将hello.txt文件输出,并把内容传入grep命令,由其在内容中查找Hello字符串。