Linux

主目录

~是一个缩写,代表用户的主目录(home),课程所用虚拟机中用户的主目录的路径为/home/co-eda。双击虚拟机桌面上的“文件管理器”图标,即可进入用户主目录。

在表示文件路径时,我们常用这个缩写,例如 ~/VCS-Example 表示 /home/co-eda/VCS-Example

==目录与文件夹==

目录 (directory) 又称文件夹 (folder),这两者一般表示相同的含义。

终端与shell

在虚拟机桌面上双击“终端”图标,即可打开终端。终端是一个==程序==,它使我们能以==文本方式==以计算机交互。

打开终端后,其中将运行一个被称作shell的程序,在终端中的提示文本都是 shell 负责输出的,你的输入也将交由shell处理。shell启动后,会显示co-eda@co-eda ~>提示,向用户展示一些基本的运行状态信息。

==shell 提示格式==

1
2
3
4
5
6
上述终端提示由 4 部分组成。
`co-eda@co-eda ~>`
1.`co-eda`表示当前用户名;
2.`co-eda`表示当前主机名;
3.`~`表示当前所在目录;
4.`>`表示当前用户为普通用户;如果当前用户为超级用户,则会显示红色的`#`号。

你可以在shell提示后输入命令,并按回车键,即可执行该命令。例如,执行ls命令,将列出当前所在目录下的所有文件。

shell分为命令行界面(CLI Shell)和图形用户界面(GUI Shell)

当前目录

shell打印的~表示当前所在目录。如果从桌面上启动终端,则当前目录默认为用户的主目录。

若想改变当前所在目录,可使用cd <目录名>命令。例如,想要进入VCS-Example目录,则可输入cd VCS-Example,可看到~变为 ~/VCS-Example,表示当前目录已经更改。

Linux 中,有两个特殊的目录:.(一个点)和 ..(两个点)。一个点表示当前所在目录;两个点表示当前目录的上一级目录。执行cd .(注意空格)不会有任何作用;执行cd ..(注意cd和两个点之间的空格)可返回==上一级==目录。

上面的“目录名”是相对路径,即相对于当前目录的路径。cd 命令也可直接到达绝对路径。例如,无论当前目录在何处,输入 cd ~都可到达用户主目录下。

若觉得使用cd命令进入目录太麻烦,也可在文件管理器中打开想进入的地方,然后按下F4键,即打开终端并直接进入该目录。

执行命令

shell中,输入命令的格式为 <命令名称> <参数 1> <参数 2> <参数 n> ...。其中,“命令名称”有两种类型:系统命令路径命令

系统命令

系统命令==直接==由==命令名==表示,命令的程序存放于特定的系统程序目录内。例如,cdls皆为系统命令,我们后面要用到的vcs也是系统命令。

路径命令

路径命令的格式则为 路径/程序文件名,程序可以位于任何地方,不一定是系统程序目录。例如,用户主目录中有hello.sh文件(打开文件管理器即可看到)。在shell中输入~/hello.sh./hello.sh,即可执行这个文件(注意后者只能在用户主目录下执行)。只有特定的文件(例如==脚本==、==可执行文件==)才能被执行,文本文件、Verilog 源代码文件等都是不能执行的。

终止命令

如果一个命令执行太久,你想终止这个命令,可以在终端中按下^Ctrl+C,终端会向当前进程发送中断信号,停止其执行。

一些常用的命令

  • ls: 查看当前目录下的==所有==文件。命令格式为:ls [选项] [目录],常用的选项有-a(显示隐藏的文件)和-l(每行只列出一个文件)。

    文件的组成:文件名、i节点和文件内容——[文件名]->[i节点]->[文件内容],其中i节点中记录了文件权限、所有者、修改日期、长度、存放位置等

  • ln file1 file2:为file1所对应的i节点增加一个文件名file2:,此时,若通过rm file2会删除文件名file2,对应i节点和文件内容仍在;再通过rm file1删除file1,由于没有文件名指向该i节点,系统同时也会删除该i节点及其对应的文件内容。

  • cd: ==进入==其他目录。/表示的是linux文件系统的最顶层根目录,而根目录下包含所有二级目录;~是当前用户主目录的简写。使用cd ~回到当前用户主目录。对一般用户,主目录是/home/用户名;对root用户,主目录是/root$表示当前的用户是普通用户,反之若当前为系统管理员用户,会显示#。Linux中,.表示当前目录,..表示上一级目录,$cd表示用户登录时所在的位置,因此cd ..可返回上一级目录,cd -可跳转到上一次访问的目录。

    tips:输入文件名或目录名时,可使用Tab自动补足全名,有多种补全方案时双击Tab可显示所可能选项。

  • date: 查看当前日期时间。

  • cp [选项] <源文件名> <目标文件名>: 复制(copy)文件。常用选项**-r用于递归复制目录及其子目录内的所有内容**。

  • rm [选项] <文件名>: 删除(remove)文件。常用选项有:-r递归删除目录及其内容,删除非空目录必须有此选项,否则无法完成删除-f强制删除,不提示用户确认,忽略不存在的目录;-i逐一提示用户确认每个将要被删除的文件。rm -rf是危险的命令,执行前一定再三确认,它会删除一切文件,包括Linux本身rm -rf *或者rm -rf ./*可以删除当前所在目录的所有文件,而rm -rf /*删除的是根目录下的所有文件,相当于删除所有的文件,十分危险。

  • mv [选项] 源文件 目标路径:将源文件(也可是目录)移动为目标路径对应的文件或移动到目标路径中,常用选项为:-v显示详细操作信息;i进行交互式操作,覆盖前询问;-u仅在源文件较新,或目标文件不存在时执行移动操作。可以移动文件同样也可以用来移动目录,还可以重命名文件:也就是把一个文件以不同的名字移动到当前目录下,也就是mv oldname newname命令可以实现对文件的重命名。mv相当于移动原有文件,故移动后原位置处没有该文件了。

  • diff [选项] 文件1 文件2:进行比较的操作,常用选项:-b不检查空白字符的不同;-B不检查空行;-q仅显示有无差异,不显示详细信息。

  • echo <文本>: 原样==显示==文本。

  • cat <文件名>: 查看文件内容(catch?)。

  • head -n <number> <filename>:可以显示filename文件的头number行。head -c <number> <filename>会显示<filename>文件的前<number>个字节的内容。

  • tail -n/-c <number> <filename>:同理;此外,-f选项还可以在文件增长时,输出后续添加的内容, 适用于内容不断变化的文件。

  • mkdir [选项] 目录:创建一个新目录。加上-p选项可以在当前不存在的子目录下存在目录,实质就是按照文件树格式同时创建两个目录。

  • rmdir [选项] 目录:删除空目录,只有空的才可被删除。

  • pwd [选项]:查看当前目录的绝对路径。

  • touch [选项] 文件名:当文件存在时更新文件的时间戳,当文件不存在时创建新文件。

  • find [路径] <选项>:在给定路径下递归查找文件,输出符合要求的文件路径,若没给定路径,则在当前目录下查找。常用选项:-name <文件名>:指定需要查找的文件名。

    find命令加上-name可在当前目录下递归查找符合参数所示文件名的文件,并将文件路径输出到终端上。

    find加上-maxdepth <number>,可以指定递归查找深度。

  • grep [选项] PATTERN FILE匹配文件内的内容查找文件和文件中的匹配位置,(PATTERN是匹配字符串,可以是正则表达式,FILE是文件或目录的路径)。将会输出匹配PATTERN的文件和相关的行。常用选项为:-a:不忽略二进制数据进行搜索;-i忽略大小写差异;-r:从目录中递归查找;-n显示行号,当需要在整个项目目录中查找某个函数名、变量名等特定文本时,grep将十分有力。fgrep虽然功能较弱,但处理大文件的能力强,egrep使用扩展正则表达式。

  • tree [选项] [目录名](非Linux发行版预装命令):可以直接输出一个目录下的文件树,常用选项有-a列出全部文件(包含隐藏文件);-d只列出目录。

  • man [选项] 命令(wcis)命令,可以用来查看命令的详细说明手册。

  • echo:回显指令,其中echo $?用于显示上一次命令的退出状态。

awk

awk:强大的文本分析工具,其主要是对于分隔好的列进行操作。

一般使用形式

awk 'program' filename(program形式:pattern1{action1} pattern2{action2}) //模式间为“或”关系,其中patterned的正则表达式,逻辑表达式;action:如printprintf(使用方式同正常编程语言,例:awk '{printf("%d:%s\n",NR,$0)}' file)等

例子:

awk '$1>2{print $1,$3}' my.txt,该命令格式为awk 'pattern action' file,pattern为条件,action为命令,file是文件,例子中出现的$n代表每一行中用空格分隔后的第n项,所以该命令是输出文件my.txt中所有第一项大于2的那一行的第一项和第三项。可以使用-F选项来指定分隔符。如awk -F, '{print $2}' my.txt中就指定了以,为分隔符。

缺省

  • awk '/pattern/' file:显示匹配模式的每一行,功能同grep

  • awk '{print}'cat

记录、字段

  • 一行为一条记录,其中NR为记录数,其实也就是行号
  • 一个非空字符串为一个字段,使用$1$2、…标记,$0代表整行,NF为字段数
  • 例:awk '{print NR,$0}' file,显示file并在行首加上行号;ls -l | awk '/^d/{print}'长格式列出当前目录下的子目录

pattern

请看示例:(在ls -l的显示中第5个字段是文件的字节大小)

  • ls -l | awk '$5~/.....*/':列出长度超999字节的文件(前四个.代表四位数,最后一个.*结合形成通配符)
  • ls -l | awk '$5!~/.....*/'不难理解这就是长度不超过999字节的文件
  • ls -l | awk '$5=="4096"'通俗易懂吧
  • ls -l | awk 'length($5)>3'列出长度超过999字节的文件

特殊pattern

  • awk 'BEGIN{初始化动作}'
  • awk 'END{结束动作}'

例:打印某个文件的行数、单词数和字符数

1
awk '{nw+=NF;nc+=length($0)+1;}END{print NR,nw,nc;}' filename #功能同wc,注意BEGIN后跟的只是初始化动作,可以理解成Verilog中的initial块?

流程控制

寻找文件中相同的相邻单词

1
2
3
4
5
6
7
8
awk 'NF>0{ \
if ($1==lastword) \
printf("double %s, line %d:\n%s\n", $1, NR,$0); \
for (i=2;i<=NF;i++) \
if ($i==$(i-1)) \
printf("double %s, line %d:\n%s\n", $i, NR,$0); \
lastword=$NF; \
}' filename

while

使用方式同正常编程习惯,

除了breakcontinue对循环进程的控制,还有next:下一条记录(也就是下一行),回到awk程序开始;exit:跳转至END模式或结束。

内部变量

  • FILENAME:当前输入文件名
  • RS:输入记录的分隔符(默认为换行符)
  • FS:输入字段的分隔符(默认为空格、制表符)
  • OFS输出字段分隔符(默认空格)
  • ORS输出记录分隔符(默认换行)

内部函数

  • cos(expr)求余弦
  • exp(expr)求自然对数
  • index(s1,s2)是否字符串s2位于s1中
  • int(expr)取整
  • split(s,a,c)按分隔符cs放入a[1]a[2],……中
  • substr(s,m,n)求s字串,从第m个字符开始,一共n个字符

数组

例:

1
2
3
4
awk '{line[NR] = $0}
END{ \
for (i=1;i<=NR;i++) print line[i] \
}' filename #同cat

进程相关的命令

  • sleep <time>:系统等待(秒)

  • ps [-option]:查看进程,-e:用来查看所有进程;-f:用来查看进程详细信息;-t <terminalname>:显示某个终端启动的进程;-u <username>:显示某个用户启动的进程。

  • top:查看进程和占用等

  • kill [option] <pid>向进程发送信号。kill -l可以查看能够发送的所有选项。

  • nohup command 注销后仍将运行,使程序运行时不挂起

  • at hh:mm定时运行某些命令,

    例如:$ at hh:mm < commandfile

    1
    2
    3
    4
    5
    $ at hh:mm
    command1
    command2
    ...
    ctrl+D
  • mkfifo:在当前目录下创建一个管道文件,可以利用向该文件中写入,读出数据实现进程间的通信

    例如,这里以两个不同的shell进程举例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #shell1
    $ mkfifo fi
    $ cat < fi #获取管道内的数据,但是此时fi中为空,因此该命令将会一直等待直到另个进程往fi中输入数据

    #shell2
    $ date > fi #另一个进程往fi中输入数据

    #shell1
    2025年 04月 14日 星期一 09:09:29 CST #原来等待的cat命令下将输出date的输出,并且结束命令子进程

其他命令

  • sudo <command>:以superuser的身份运行,用来提升安全性。

  • wc <filename>统计文本文件行数、单词数和字节数。

  • sort:将标准输入按行排序输出。-k <number>:是以指定列为关键字排序。-r:输出逆序

一些命令的小用法

  • <command1> && <command2>:前一指令正常退出才执行后一指令
  • <command> &:将命令放入后台执行

行编辑器 ed

基本命令

  • a从当前行开始添加(可以将输入位置的光标理解为一个指针)
  • .结束添加状态,注:必须在行首输入
  • W,存盘
  • q,退出ed编辑器
  • !,临时退出ed,进入shell

显示命令 p

  • p,显示当前行
  • 5,将当前行改为第5行并显示当前行
  • m,np,显示第m到n行
  • .表示当前行,$表示最后一行
  • 可以使用相对行,例如:.,.+3p$-5,$p .加偏移量时可省略.,也就是-5可以表示当前行往前数的第五行

查找

  • /模式/,查找下一个匹配模式的行
  • ?模式?,查找上一个匹配模式的行
  • //重复查找下一个匹配模式的行
  • ??重复查找上一个匹配模式的行
  • 可使用查找返回的行号
    • 1,/main/p,显示1到第一次出现main的行
    • /main/-1,$p,显示第一次出现main的行的上一行到最后一行
    • 5,?int?d,删除5到最后一次出现int的行(这种使用行号的情况下模式查找基准一般定位首行,则//从首行查找,??从尾行查找)

插入、删除、撤销

  • na,从n行后添加
  • ni,从n行前插入,i、a都以行首.来结束
  • m,nd删除m到n行
  • u撤销

替换

替换策略大致与sed相同(sed就是ed的衍生物,当然会很像),

不过还是有细微的差别:s/old/new/g是**将当前行中每个old都替换成new**,而1,$ s/old/new/g才是将文件中所有old都替换。

速记符&用来代表替换命令s右边的第一个模式1,$ s/big/very &/g是将文件中的big都换成very big。

正则表达式

和大多数正则表达式规则基本一致,下介绍我觉得新颖的:

  • /^$/空行
  • /./非空行
  • /^/任意行

全局命令

命令格式:m,ng/re/cmd,意即从m行到n行中对于匹配re模式的行执行命令cmd,若作用范围为全局,可省略m、n

  • g/.../p显示所有包含...的行
  • g/.../s//rep1g,将所有...替换成rep1
  • v/^$/p打印所有非空行

移动、复制

  • m,nmd,将m到n行移到d行之后
  • m,ntd,m到n行拷贝到d行后
  • g/^/m0,将所有输入倒序排列,也就是每行都按顺序移动到第0行之后那么显然原来的第一行将变为最后一行

文本编辑器Vim

通过vim 文件名,可以通过Vim打开相应的文本并进行文本编辑。

进入后按下I键进入”插入模式”后可以开始编辑文本内容。完成修改后,按Esc返回命令模式,再按Shift+进入底线命令模式,在左下角的冒号后输入W后回车可使文件被保存;再输入q可关闭文件返回命令行。同时输入wq可一并完成上述操作。

Vim语法

初级

注意以下命令只在Normal模式下有效

  • x——删除当前光标所在的一个字符
  • dd——剪切
  • p——粘贴
  • hjkl:分别对应左下上右
  • :help <command>/:help:前者可以进入相应指令的帮助文档,后者可以进入总的帮助文档,输入:q可以退出帮助文档

其他进入插入模式的按键(只能从Normal模式进入插入模式)

  • a——在光标后插入,与i不同,i是在光标前插入
  • o——在当前行后插入一个行并进入插入模式
  • O——在当前行前插入一个行并进入插入模式
  • cw——“吃掉”当前光标开始的到一个单词结尾的字符后进入插入模式,例如scanf、canf、anf都可以被一次吃掉。

移动光标的便捷方式

  • 0——移动到本行的行头
  • ^——移动到本行第一个不是blank字符(空格、Tab、换行等)的字符处
  • $——移动到本行行尾
  • g————移动到本行最后一个不是blank字符的位置。注意——-上的那个,也就是需要用shift转化。
  • /pattern:可以用于搜索符合pattern模式(可以使用正则表达式)的字符串,输入Enter后进入搜索模式,按n键可以切换。

拷贝/粘贴

  • p/P——粘贴,p是在当前行后一行粘贴,P在当前行之前一行
  • yy——拷贝当前行

撤销与复原

  • u——undo,撤销
  • ctrl+r——redo,复原

打开/保存/退出/改变文件

  • :e <path/to/file>——打开一个文件
  • :saveas <path/to/file>——另存为<path/to/file>
  • :xZZ:wq——保存并退出,:x表示仅在需要时保存。
  • :q!——退出不保存
  • :qa!——强行退出所有正在编辑的文件,不论是否修改
  • :bn:bp——可以在用:e xxx打开多个文件后,使用这两个命令来切换到下一个或上一个文件。

进阶

同样是只能在Normal模式下使用

更好

.可以用来重复上一个命令

N<command>——重复某个命令N次

tips:对于N.它将重复上一个命令N次,假如上一次命令自带重复,则无视上一次命令重复的次数,只执行N次。

更强(使光标移动更有效率)
  1. NG——到第N行的行头。

  2. gg——到第一行行头,等效于:11G

  3. G——到最后一行

  4. 按单词移动: w——到下一个单词开头、e——到写一个单词结尾

    其中小写代表由字母、数字和下划线组成的单词

    WE代表由blank字符分隔符组成的单词,也就是**WE认为由空白符分隔开的算一个”单词”**。

​ 5.%——匹配括号移动,(){}[],需要将光标移动到括号处,按下%直接移动到对应匹配的括号处。

​ 6.*#匹配光标当前所在的单词(变量定义的单词或者非blank字符,注意,非blank字符只有在连续字符串中没有符合变量定义的单词时才被匹配,且可被匹配时会一并被匹配),移动光标到下一个(*)或上一个(#)匹配的单词。

更快

<start position><command><end position>可以用来进行光标的快速移动。

例如0y$意味着0先到行头、y从此拷贝、$拷贝到行尾,也就是拷贝该行。

ye——可以从当前位置拷贝到本单词(**此处的单词是标准的单词,也就是w**和e标记的单词)的最后一个字符

当然除了按y命令会进行拷贝外,下列的命令也会进行拷贝:

  • d(删除,一般来说是删除并拷贝当前行和下一行)

  • v(可视化选择,可以对一个文本块的整体进行操作,例如:可以先高亮选中一部分文本(即可视化),然后再使用d删除)。

    可视化的模式有以下三种:

    ​ 1.v进入字符可视化模式,以左右键移动进行的是以字符为单位的文本选择,但上下键移动会将从光标移动到的位置至原位置的所有字符都选上,区别于行选择,当然如果原光标就在行头,那么上下移动可以近似认为就是行选择

    ​ 2.V进入行可视化模式,此时关注的就只是光标在哪一行了,显然在哪一行,哪一行就全选。

    ​ 3.ctrl+V(注意:这里是大写的V)进入块可视化模式,有点类似于前两种模式的结合版,也就是从初始光标那行开始算起,上下键移动就是增加被选择的行,但是依靠左右键来决定每行从左至右由初始光标在的位置选择到第几个字符(每一个被选择的行被选择的字符是行对齐的)。

    此外,可以通过ctrl+Q来从行或字符可视化模式切换到块可视化模式(注意只能单向切换块可视化模式下再按ctrl+Q是退出块可视化了)。

    可视化下进行编辑,d删除高亮文本,D删除一整行,即使这一行中只有部分文本高亮。同理,y复制高亮文本,Y复制整行。

    块可视化模式下,使用c将删除高亮文本并进入插入模式,此后输入文本并点击Esc返回后,输入的文本将插入到原来块选中的每一行的字符区间上;使用C将删除高亮文本至末尾的所有字符并进入插入模式,其余同c

    ~可将高亮文本中的字母进行大小写转换;

    >:分为两种情况:不在可视化模式中,将为当前行和下一行增加缩进,也就是在行首加一个Tab;在可视化模式下,注意这里行/字符可视化模式同块可视化模式略有不同,前者仅为光标所在行增加缩进,而后者将为光标所在的地方开始增加缩进,也就是在每一行的光标对齐处增加一个Tab',但是上诉三种方式在增加完缩进后都会退出可视化模式

    <:用法同>,但功能是减少缩进。

    =会自动给缩进

    J:两种情况:不在可视化模式中,将当前行和下一行合并为一行并且以空格分隔开;在可视化模式中,哪种可视化模式都一样,将所有有高亮部分的行以空格分隔合并成一行并退出可视化模式。

    gJ:用法同J,只不过分隔的不是空格而是Tab

    g?:两种情况:不在可视化模式中,将依据rot 13算法,对当前行和下一行进行加密,再按一次即可解密;在可视化模式中,即对高亮文本进行加密并退出可视化。

    可视化模式下,按下:可对选定范围进行操作,例如:选中后执行:write xxx会将选中的文本写入xxx文件中,若该文件已经存在,则使用:write! xxx可以强制覆盖写入;

  • gU:将选中文本中的字母变为大写

  • gu:将选中文本变小写

vim超能力

在当前行进行移动
  • 0:到行头
  • ^:到本行第一个非空白符字符处
  • $:到行尾
  • g——:到本行最后一个不为空白符字符处
  • fx:到本行下一个为x字符的位置
  • tx:到本行下一个为x字符的前一个字符处
  • 3fx:在当前行查找从当前光标下一个字符开始第三个出现的x字符位置处
  • FT用法同ft类似,只不过方向相反
  • 一个可能很有用的命令dt":删除所有的内容直到遇到双引号"
区域选择

可视化模式下,命令格式为:<action>a<object><action>i<object>

  • action可为任意命令,如dyv
  • object可以是:w一个单词、W一个以空格分隔的单词、s一个句子、p一个段落,也可以是"、'、)、]。}、

例子:有一个字符串(map(+)("foo")),且光标在第一个o处,则

  • vi":会选择foo
  • va":会选择"foo"
  • vi):选择"foo"
  • va):选择("foo")
  • v2i):选择map(+)("foo")
  • v2a):选择(map(+)("foo"))
自动补齐

在插入模式下,在输入完一个词的开头的前提下,按ctrl+p可以开启选择可能的输出结果的序列。

宏录制:qa操作序列q@a@@
  • qa会将操作记录在寄存器a
  • 这样一来@a会返回被录制的宏
  • @@用来返回最新录制的宏

一个例子

在光标所在行只有1中,输入如下命令:

qa(开始录制)->Y(拷贝本行)->p(在下一行粘贴)-><ctrl+a>(将数自增)->q(结束录制)->@a(按顺序重复被a录制的命令)->100@@(按顺序重复最新录制进寄存器的命令)

分屏

:splitvsplit,前者创建分屏,后者创建垂直分屏

常用快捷键

ctrl+C:终止当前程序的执行。

ctrl+Z:挂起当前程序,放到后台,挂起程序后会显示编号,想要恢复可使用fg [job_spec]即可,job_spec为挂起编号,不输入时默认为最近挂起进程。

ctrl+D:终止输入,若正使用shell,则退出当前shell,**在标准输入中相当于输入了一个EOF**。

ctrl+L:清屏,相当于命令clear

ctrl+S:暂停该终端,使用ctrl+Q即可使终端再次运行。

脚本

​ 有时,我们需要执行一些固定的命令,每次都输入这些命令会比较麻烦。这时,我们需要使用“脚本” (script) 来简化操作。脚本的基本功能就是将许多命令==汇整==写在一起,让使用者能够一键进行较为复杂的动作。

我们可以像运行程序一样运行脚本。例如,上面提到的hello.sh就是一个脚本,在shell中输入~/hello.sh,就可以运行。如果上述方法不能正常运行,也可使用sh {name}.sh 来运行脚本。

hello.sh:

1
2
3
4
5
#!/bin/bash

# 使用 echo 命令打印文本
echo Hello, world!

脚本的实质为==文本文件==。hello.sh第一行为[shebang](关于 Bash 脚本中 Shebang 的趣事 - 知乎 (zhihu.com))(用来指定脚本的解释器路径),用于指定解释器为 bash,所有的 bash 脚本都需要带有这一行,否则无法正常执行。第二行为空行。第三行为注释。以#开头,类似python的写法。最后一行,脚本使用echo命令,完成一行输出。

如果使用Sublime Text编写脚本并尝试运行,系统会提示 fish: The file './1.sh' is not executable by this user。这是因为我们没有给脚本文件赋予执行权限。在用户主目录下运行ls命令,会发现hello.sh呈绿色加粗(表示有执行权限),而xxx.sh(xxx.sh为之前尝试运行的文件的文件名) 则呈现黑色(表示该文件为普通文件,无执行权限)。

要赋予执行权限,只需运行chmod +x xxx.sh即可。赋予后,脚本就可以直接执行了。再次运行ls命令,发现xxx.sh也已经是绿色加粗了。

Linux中的shell脚本

脚本文件的文件名一般都以.sh结尾,可以通过./文件名(此处文件名包含.sh)运行。(一般是使用bash xxx.sh来运行脚本的,想要使用./xxx.sh来运行需要先为脚本添加权限。)

需要注意:被执行脚本文件有“执行”权限,Linux中,每个文件对于拥有者、用户组和其他用户都有”读”、“写”、“执行”的权限,使用touch命令创建的文件默认没有“执行”权限的,要手动添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
chmod +x 文件名

#chmod的语法:
#chmod 权限设定字串 文件...
#权限设定字串格式: [ugoa...][[+-=][rwxX]...][,...]
#其中,u表示该文件的拥有者,g表示与该文件拥有者属于同一个群组,o表示其他以外的人,a表示三者皆是。
# +表示增加权限、-是取消、=表示唯一设定权限
# r表示可读取、w表示可写入、x表示可执行、X表示只有当该文件是个子目录或该文件已被设定过为可执行

#此外chmod也可以用数字来表示权限,格式为:
chmod ugo <file>
#ugo分别为三位的二进制数在十进制下的数值,从高到低分别表示rwx的权限是否打开
#示例:

#为run.sh的拥有者添加执行权限
chmod u+x run.sh

#修改run.sh权限为 rwxr-xr-x(对应二进制111(b)、101(b),也就是有权限代表二进制的1,没有代表0,对应翻译为十进制数)
chmod 755 run.sh

#移除所有人对run.sh的写权限
chmod -w run.sh

shell元字符集

  • >、>>、<、<<、|、*(匹配文件名中任意字符)、?、ccc、;(命令结束符,用于连接两个有顺序要求的命令)、&(后台命令结束符,将命令挂到后台)、...(将命令…运行的结果返回)、(…)(在子shell中运行…命令)

test命令

主要用于if、while、until命令中的条件判断,即判断文件类型或表达式是否为真

语法:
  1. test -option file
    • ~ -e file //判断file是否存在
    • ~ -b file //判断是否存在且为块设备文件
    • ~ -c file //判断存在且为字符设备文件
    • ~ -d file //存在且为目录文件
    • ~ -f file //存在且为普通文件
  2. test expression
    • ~ e1 //判断e1为真
    • ~ ! e1 //e1为假
    • ~ e1 -a e2 //都为真
    • ~ e1 -o e2 //都为假
    • ~ f1 -nt f2 //f1比f2新
    • ~ f1 -ot f2 //f1比f2旧
  3. test -option string
    • ~ -n str //str非空
    • ~ -z str //为空

shell内部变量

  • $# 命令行参数个数
  • $* 命令行参数集合
  • $@ 命令行参数集合 //与$*略有不同
  • $? 上一条命令的返回值
  • $$ 当前shell的进程号
  • $! 最后一个后台命令的进程号
  • $HOME、$PATH、$PS1、$PS2

其他命令

  • break
  • continue
  • exit n 终止shell程序,n为返回值
  • trap 设置中断处理命令,例如: trap 'rm -f tmpfile; exit 1' 1 2 15

实例 myWhich

which命令可以返回某个命令的可执行文件所在目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
!#/bin/bash
case $# in
0)
echo Usage: which cmd
exit -1;;
esac
for i in `echo $PATH | sed 's/:/ /g'`
do
if test -f $i/$1
then
echo $i/$1
exit 0
fi
done
echo not found

bash shell语法

初学者必打代码

第一行一定是要指定脚本的解释器路径:如:#!/bin/bash,其中#!称为Shebang,程序加载器会将文件中Sheband后的内容作为脚本文件的解释器。

变量

作为弱类型语言,定义变量时无需指定类型,采用:var_name=value方式定义变量,注意等号两边不能有空格

通过使用$var_name获取变量的值,建议变量名左右加上{来帮助解释器识别变量边界。这里加上花括号是指在引用变量的时候,创建变量的时候不建议使用。
通过set命令可以重置位置参数,不过不可以重置$0,其为命令名。
通过shift命令可左移位置参数,但不可移动参数$0,相当于用set命令把右边的参数重置到左边去。

1
2
3
#!/bin/bash
str="Hello, world!"
echo $str # or echo ${str}
数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/bash
#定义数组
arr=(ele1 ele2 ele3)
#获取数组长度
${#arr[*]}
#获取元素,这里获取的是第一个元素
${arr[0]}
#获取全体元素
${arr[@]} / ${arr[*]}
#添加元素
arr+=(ele4)
#删除元素
unset arr[1]
#变量更新
let a=a+1
((a=a+1))
a=$((a+1))
脚本参数——特殊的变量

参数在shell脚本中体现为特殊的变量,运行脚本语句中,一个参数在脚本中都是一个字符串常量,按顺序一个参数映射到相应的变量名上。

例如:

1
2
3
#!/bin/bash
str="Hello, $1 and $2 world!"
echo $str # or echo ${str}

因而在执行脚本(采用./(name).sh的方式执行,sh (name).sh后加参数实测会出现报错)时可以输入由空格分开的两个参数,根据顺序,前者对应$1,后者对应$2.

只有在双引号内才能正确的进行传参,若为单引号,则会输出原内容,并不会进行参数的传递。传入的变量若作为文件名,建议一定要加上双引号修饰,避免造成歧义。例如:grep -n "$2" "$1" | awk -F: '{print $1}' > "$3",其中双引号内的变量分别是$1:file、$2:int、$3:result

其他的特殊变量:

  • $#传递参数个数,即按空格分割开的参数的个数。
  • $*一个字符串,内容为传递的全部参数。也就是会将脚本输入时的所有参数打包成一个字符串当作参数。
条件与循环
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#if格式如下:
if condition1
then
command11
command12
......
elif condition2
then
command21
command22
......
else
command31
command32
......
fi #"if"的缩写,代表if块的结束
#也可写到一行:if condition; then command1; command2;...fi
#例:
#!/bin/bash
if (( $1 > $2 ))
then
echo "first > second"
elif (( $1 < $2 ))
then
echo "first < second"
elif (( $1 == $2 ))
then
echo "first == second"
else
echo "I don't know..."
fi
#其中,(())用于比较整数间大小,传入字符串或浮点数为未定义行为,注意else后并不需要添加then

#case语句

case word in
[[(] pattern [| pattern]...] command-list;;]...
esac
#case的pattern左括号可以进行省略

#例如:
case $ANIMAL in
"horse" | "dog" | "cat") echo -n "four";;
"man" | "kangaroo") echo -n "two";;
*) echo -n "an unknown number of";;
esac

#for格式如下:
for ((expr1;expr2;expr3))
do
commands
done
#除了for条件多加了一层小括号,其他与C语言相同
#
for name [[in[words ...]];]
do
commands
done
#这种有点像python的for语句
#例如:
for i in {1..5}; do
echo "hello!"
done


#while格式如下:
while condition
do
command1
command2
……
done #表示“do”语句块的结束
#while里可使用continuebreak
#例1:
#!/bin/bash
mkdir files
cd files
i=1
while (( ${i} <= $1))
do
touch "file${i}.txt"
let i=i+1 # or i=$((i+1)), let为进行变量赋值的命令
done
#通过加花括号的方式可以将自定义的变量同传入参数进行区分
#上述代码可以实现在files文件夹中创建相应后缀的共$1个txt文件
#例2:
#创建9个目录,名字为file1到file9
a=1
while [ $a -ne 10 ]
do
mkdir file$a
a=$[$a+1]
done

#until命令结构
until
list
do
list
done

注意等号前后不能有空格,左中括号后和右中括号前一定要有空格,例如:

1
2
3
4
5
6
a=1
if [ $a -ne 1 ]; then echo ok; fi
#左中括号[ 是一种常用作条件的命令,其参数是一个条件表达式的末尾的右中括号],该条件命令在关系成立时返回0,!在C语言中的作用在此处同样适用

#其中-ne是一种关系运算符,此外还有:
# -eq —— ==;-ne —— !=;-gt —— >;-lt —— <;-ge —— >=; -le —— <=
函数

定义:

1
2
3
4
5
6
7
function fun_name() { #function 和 ()可以省略其中一个
body...
return int_value; #可选,可以没返回值
}

#调用方式:
fun_name param1 param2 …… paramN

第N个参数在函数体内使用$N来获取,不需在函数定义开头声明。

注意:N>=10时,要用${N}获取参数,否则$只会带一位数字

若函数有返回值,则在函数调用后需要使用$?获取返回值。

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash
function fun1() {
echo "Hello, world!"
}
function fun2() {
echo "I've got "$1" and "$2"!"
}
function fun3() {
echo "I'm computing first + second!"
return $(($1 + $2)) # 这里$1和$2是函数后传入的参数。算出来值后获取的返回值即为运算结果
}
fun1
fun2 2 3
fun3 4 5
echo "The sum is "$?"." #使用$?获取返回值
用命令编辑和输出文本——sed的使用
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
sed [选项] ’命令‘ 输入文本 #注意一定要是单引号
#常用选项:
#-n:安静模式,只显示经过sed处理的内容,否则显示输入文本的所有内容
#-i:直接修改读取的档案内容,不是输出到屏幕上,否则,只输出不编辑;不加-i时只在控制台显示编辑后的文件内容,不修改文件本身,也就是有了-i就会修改源文件
#-e允许同一行内执行多条命令
#常用命令:
#<行号>a<内容>: 新增,在行号后新增一行相应内容。行号可以是“数字”,在这一行之后新增,也可以是“起始行,终止行”,在其中的每一行后新增。当不写行号时,在每一行之后新增。使用$表示最后一行。后面的命令同理。对于该命令而言,带上-n选项将只输出对应的行的内容,也就是要修改的内容,不添加会输出全部安静模式下修改后的文本结果。
#<行号>c<内容>:取代。用内容取代相应行的文本。
#<行号>i<内容>:插入。在当前行的上面插入一行文本。
#<行号>d:删除当前行的内容。
#<行号>p:输出选择的内容。通常与选项-n一起使用。
#s/<re>/<string>:将<re>(正则表达式)匹配的内容替换为<string>。

sed -n '3p' text.txt #输出text.txt中第三行的内容

sed '1d' text.txt #删除text.txt中第一行内容, 这里的删除并没有在源文件上进行,输出出来的只是删除后的效果

sed '1,$d' text.txt #删除文件从第一行到最后一行

sed '2a\"who am I\"' text.txt

sed 's/what/how/g' text.txt
#由于g(global)的存在该命令会将全部范围内的所有what换成how,否则只有每行第一个匹配的会被替换

sed -e '4astr' -e 's/who/aaa/g' text.txt

注意只有加入了-i选项才能对源文件进行修改。

重定向

在Linux中使用重定向将命令的输出写到文件中。

Linux中的三种流:由0表示的stdin(标准输入)、由1表示的stdout(标准输出)、由2表示的stderr(标准错误)

一般情况下,这些流使用的设备是控制台,即可在控制台上看到命令的输出。在命令后使用>可将输出重定向。

例:$ ls / > lsoutput.txt可将根目录下的文件名输出到当前目录的lsoutput.txt文件中,覆盖文件的原有内容通过>>可以实现在文件后追加命令的输出,并不会覆盖掉原来的内容,可以理解为在原内容基础上继续添加内容

实际上,>1>的简写,对应输出的是标准输出。

如果想要重定向标准错误,可以使用2>.例如可以通过gcc 2> gccerr.txtgccerr.txt文件中观察到gcc输出的编译错误。

使用<,代表标准输入重定向。
<<str是即时文件读入,直到str结束,可用于在shell程序中创建文件:
例:

1
$ vim mkafile #创建一个运行相应命令的脚本,下列内容为mkafile中的内容
    cat > $1 << end #将以下的内容输出到$1中直到遇到了end字符  
    hello world!
    end
    
1
2
3
$ sh mkafile ttt
$ cat ttt
# hello world!
管道

|可对命令进行连接:

command1 | command2 | ……

作用是将command1的输出传给command2的输入,以此类推。

例:

cat hello.txt | grep "Hello" cat命令将文件内容输出到标准输出,grep命令从标准输入读取文本,可将hello.txt文件输出,内容传入grep命令,其在这些内容中查找Hello字符串。

$()、``

$()用于隔离并执行命令,并将输出替换至原处,此外**``**也有同样的效果。

例如:

1
2
3
4
5
6
7
8
9
#!/bin/bash

if [ -z "$(diff file1 file2)" ] #$(diff file1 file2)将得到diff比较的输出结果,返回结果作为一个字符串(双引号决定的),若字符串为空,则判断相同。$(diff file1 file2)与`diff file1 file2`用法一致
then
echo "same"
else
then
echo "different"
fi

或许可以这样来理解$(command):1.bash会启动一个子进程来执行command。2.bash执行命令command时,会将'"所包裹的内容当作一个command arg/token。例如echo $(‘pwd') 会输出执行pwd命令后的结果。

此外,'"包裹的命令在交互终端中是可以直接使用的。
在交互终端中*表示当前目录下所有文件和子目录的文件名和目录名的集合。

1
2
3
4
5
6
7
8
9
10
11
12
$ echo 1234
1234
$ 'echo' '1234' #也就是告诉bash将'echo'、'1234'分别作为执行exec("/bin/echo",{"echo", "1234"})的第一个和第二个实参
1234
$ pwd
/home/git
$ pwd
/home/git
$ 'echo' "pwd"
pwd
$ "echo 1234"
-bash: echo 1234: 未找到命令
(())

(())用于算术表达式,

例如:

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash

a=1
b=2
c=$((a+b))

((a=10))
((b++))
a=$((a+2))
echo $((a+b+c))
#上述表达式都是合法的,且语法大致同去掉(())和$后的正常编程语法一致
[]

[]用于判断条件是否成立。至于为什么中括号中左右都需要有空格,那是因为我们写在判断条件时实际上是将中括号内的东东**作为参数传给可执行程序[**。[/bin中。

[[]]

同样用于判断条件,但比[]功能更加丰富。例如在[[]]中可直接使用逻辑运算:[[ $a -gt 1 && $a -lt 100 ]]注意:同样也要加空格

智能补全与历史联想

如果每次cd或执行其他命令都要输入全名,未免有些麻烦。在shell下,按Tab键可以使用智能补全功能,根据当前目录下的对象名称进行补全。例如,要想进入VCS-Example目录,只需输入 cd V,然后按下键盘的Tab键,shell会自动帮你补全剩下的字符。

虚拟机中的shell还提供了历史记录联想功能。输入命令的一部分后,如果你以前执行过类似命令,shell会以灰色显示联想出的部分。例如,如果你以前执行过cd VCS-Example,在输入cd VC后,终端会显示cd VCS-Example,灰色部分即为联想。这时,按下键盘上的==右方向键==,即可确认联想部分。注意按回车后,灰色的部分不会执行,需要确认后才能执行。若有多个联想,可用==上下方向键==来选择;如果联想的内容只有一部分是你想要的,则可用 ==Alt+右方向键==来逐单词确认。

不同shell之间的差异

Debian、Ubuntu、CentOS 等 Linux 发行版默认的shell是bash,而本课程提供的虚拟机映像的默认shellfish

fish的功能更加强大易用,但它与使用更广泛的 POSIX 兼容终端(如 bash)的行为不尽相同。以bash为例,fish和bash在命令替换语法、变量设置语法、流程控制语法等诸多方面上有显著不同。在尝试运行来源于网络的shell命令时,需要注意相关语法差异。

关于fish和bash的详细区别,可以阅读官方文档:Fish for bash users

你也可以在fish中启动bash或者其他shell,以求正确地执行来源于网络的shell命令。

关于在脚本文件中指定其执行shell,参考:Shebang

Linux中的实用工具

GCC

(GNU Compiler Collection,GNU编辑器套件),包含了著名的C语言编译器gcc。

gcc使用格式:gcc [选项] 源代码文件用来编译源代码文件。再没有进行其余选项的修饰下,经由gcc编译产生的可执行文件名均为其默认的a.out文件名。通过运行./a.out即可运行该可执行文件。**./是当前目录下的意思,Linux会在当前目录下查找可执行文件,若没有,则Linux会在“系统PATH”中寻找,而a.out不在系统PATH中,因此执行时会报错**。

若想要同时编译多个文件,可以使用如下命令:gcc testfun.c test.c -o test(可直接将多个文件编译连接)或gcc -c testfun.c && gcc -c test.c && gcc testfun.o test.o -o test(先用-c将每个文件单独编译成.o文件,再用-o.o文件链接)

1
2
3
4
5
6
7
8
9
10
语法: gcc [选项]...[参数]...
常用选项:
-o 指定生成的输出文件
-S 将C代码转换成汇编
-Wall 显示一些警告信息
-c 仅执行编译操作,不进行链接操作
-M 列出依赖
-I<path> 编译时指定头文件目录,使用标准库时不需指定目录,-I参数可用相对路径,比如头文件在当前目录中,可用-I指定
参数:
C源文件:指定C语言源代码文件

make & Makefile

在较大的项目中,make工具可以将这些文件编译链接成可执行文件,而进行编译和链接的操作是经由Makefile来指导的。make工具根据时间戳自动判断项目的哪些部分要重新编译,每次只编译必要部分。

1.不使用make工具,可以使用

1
$ gcc -o helloworld helloworld.c

来对helloworld.c进行编译,其中-o是为编译产生的可执行文件命名(如helloworld)。

2.使用make工具,需要在当前目录下创建并编辑Makefile文件,文件名就为Makefile

基本格式为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//target 是构建的目标,可是真的目标文件、可执行文件,也可是标签。
//一个目标构成一条规则,可以是一个文件名、也可是多个文件名之间用空格分隔,除了文件名,还可是某个操作名,即"伪目标",为了避免伪目标同文件同名,可以用.PHONY:修饰例如:
.PHONY: clean
clean:
rm *.o temp
//dependencies是构建目标所需的其他文件或其他目标,也就是说对于目标而言,其要运行的前提是这些依赖项都已经存,相当于前置条件,因此可以编写一个只有依赖项的目标,这样可以批量生成文件,提高效率
//之后是构建出该目标所需执行的命令,这里每一个命令前必要有一个Tab,不能是空格,否则会报错。如果想用其他键,可以使用内置变量.RECIPEPRFIX声明
例如:
.RECIPEPREFIX = >
all:
> echo Hello, world

//每条规则明确两件事:构建目标的前置条件是什么,如何构建。
target: dependencies
[tab]command 1
command 2
...
command n

若Make命令运行时没有指定目标,默认会执行Makefile文件的第一个目标。

每行命令在一个单独的shell中运行,之间没有继承关系,可以近似的理解为不同行的命令会同时运行,解决方法有:

1.把两行命令写在一起,中间用分号分隔:

1
2
var-kept:
export foo=bar; echo "foo=[$$foo]"

2.在换行前加反斜杠转义\;

1
2
3
var-kept:
export foo=bar; \
echo "foo=[$$foo]"

3.加上.ONESHELL:命令

1
2
3
4
.ONESHELL:
var-kept:
export foo=bar;
echo "foo=[$$foo]"

Makefile中书写的显示规则告诉make工具文件间的依赖关系:若想要创建target,先准备好dependencies,再执行command中的命令,得到target。在这之后只需在shell中输入make target即可执行相应命令、生成相应目标。

之后运行make targetmake即可产生相应的可执行文件。

make语法

注释

#代表注释

回声

一般情况下,用make运行目标(命令)时会打印每条命令,然后再执行,也就是回声(echoing),包括目标里包含的注释,而在命令或注释前加上@就可以关闭回声,由于构建时需要了解当前在执行哪条命令,所有常只在注释和纯显示的echo命令前加上@

通配符

用来指定一组符合条件的文件名(正则表达式)。与Bash的一致,主要有*?[...],其中*.o表示所有后缀名为o的文件。

模式匹配

实则是对文件名进行类似正则表达运算的匹配,主要的匹配符是%,旨在一条规则完成构建。例如:若当前目录下有f1.cf2.C两个源码文件,将它们编译为对应得对象文件:

1
2
3
4
5
%.o: %.c

#等同于
f1.o: f1.c
f2.o: f2.c
变量和赋值符

可以使用=自定义变量,例:

1
2
3
txt = Hello World
test:
@echo $(txt) #调用时,变量需要放在$()中

调用Shell变量需要在$前再加一个$。

可以变量给变量传递值吗?当然可以。但是为了避免一些传递的麻烦,Makefile提供了四种赋值运算符(=:=?=+=):

1
2
3
4
5
6
7
8
9
10
11
V = value
#执行时扩展,允许递归扩展

V := value
#定义时扩展

V ?= value
#只有变量为空时才设置值

V += value
#将值追加到变量尾端
内置变量

如,$(CC)指向当前使用的编译器,$(MAKE)指向当前使用的Make工具。

自动变量
$@

指代当前目标(this?)即Make命令当前构建的目标,如make foo的$@指代foo。

$<

指代第一个前置条件,若规则为 t: p1 p2,则$<指代p1.

$?

指代比目标更新的所有前置条件,之间以空格分隔,如,规则t: p1 p2,中若p2时间戳比t新,则$?指代p2.

$^

指代所有前置条件,之间以空格分隔。

$*

指代匹配符%匹配的部分,如%匹配f1.txt中的f1,则$*指代f1

$(@D)和$(@F)

前者指向**$@**的目录名(directory),后者指向文件名(file),$@是当前目标,如若$@src/input.c,则前者指代src,而后者指代input.c

$(<D)和$(<F)

分别指代$<目录名和文件名

判断与循环

语法同bash。

函数

格式为:

1
2
3
$(function arguments)
#或
${function arguments}

此外,还有内置函数。

shell函数

执行shell命令:

1
srcfiles := $(shell echo src/{00..99}.txt)
wildcard函数

替换Bash的通配符。

1
srcfiles := $(wildcard src/*.txt)
subst函数

进行文本替换:

1
$(subst from,to,text)

一个栗子:

1
2
3
4
5
6
7
comma:= ,
empty:=
# space变量用两个空变量作为标识符,当中是一个空格
space:= $(empty) $(empty)
foo:= a b c
bar:= $(subst $(space),$(comma),$(foo))
# bar is now `a,b,c'.
patsubst函数

用于模式匹配的替换:

1
2
3
4
5
$(patsubst pattern,replacement,text)
#pattern为原模式,replacement为替换的模式

#例子:将文件名"x.c.c bar.c",替换成"x.c.o bar.o"。
$(patsubst %.c,%.o,x.c.c bar.c)
替换后缀名

格式为:变量名+冒号+后缀名替换规则,实际是patsubst函数的简写

例如:

1
2
min: $(OUTPUT:.js=.min.js)
#该代码的含义就是将变量OUTPUT中的后缀名.js全替换成.min.js

提升效率的变量使用方法

示例
1
2
3
4
5
6
7
8
9
10
11
12
13
CC = gcc 
#设定编译器,后续还可能有icc、icx
CFLAGS = -g
#设定编译选项,后续还可能有-O1、-O3等等
SRCS = function.c test.c
#需要用的源文件
OBJS = $(SRCS:.c=.o)
#代码功能是将.c文件转化为.o文件
EXEC = test
#要生成的可执行文件
$(EXEC) : $(OBJS)
$(CC) $(CFLAGS) -o $@ $^
#$@代表目标文件,$^代表所有依赖文件
如何在嵌套目录中进行编译make呢?

可以使用$(MAKE)并配合-C选项来进入相应的目录进行make编译

例:

1
2
3
4
5
all : child_dir
$(MAKE) -C child_dir
#这行代码的作用相当于进入子目录后再执行make命令
#同样可以在通过下列代码进入相应子目录进行make command命令
$(MAKE) -C <dir> <command>

Tips

  • 开头最好写上.PHONY xx,例如:.PHONY: clean避免同名文件冲突。

ctags

方便代码阅读的工具,起到代码跳转功能。

使用前在~/.vimrc文件中添加:

1
2
set tags=tags
set autochdir

使用实例:

hello.c中为:

1
2
3
4
5
6
7
#include "ctags_test.h"
int main() {
struct Pair p;
p.a = 1;
p.b = 2;
return 0;
}

ctags_test.h中输入:

1
2
3
4
struct Pair {
int a;
int b;
};

再执行命令ctags -R *会在目录下发现ctags为我们创建的符号索引文件tags,之后在c文件中光标移动到结构体相应变量中按下ctrl+],即可跳转到其定义处,按ctrl+o可返回原来的位置,其实只要是在一个文件中使用另一个文件里定义的==东东==都可以通过ctags进行移动此外,vim中按:进入底线命令模式后输入tag xxx也可跳转到xxx定义的文件的中xxx的位置处

tmux

实现终端窗口和进程分离,在窗口中同时显示多个进程的运行。

输入tmux进入tmux的新会话。

通过以下快捷键可以对tmux进行操作。

窗格操作

ctrl + B(同时按下ctrl+B后松开这两个键,紧接着输入%,可以将窗口左右分屏。

ctrl + B同上松开后再输入"shift + ‘,将窗口上下分屏。

ctrl + B再输入O可以依次切换当前窗口下的各个窗格

ctrl + B前面松开后紧接着(上下左右按键)可以根据按键方向切换到某个窗格。

ctrl + B +space,切换窗格布局(上下变左右,左右同理)

ctrl + B + Z最大化当前窗格

ctrl + B + X,关闭当前正在使用的窗格。

ctrl + B + D分离当前会话,回到shell终端环境,程序仍保持在tmux会话中的状态。

窗口操作

ctrl + B C:创建后多一个窗口

~ P切换到上一个窗口

~ N切换到下一个

~ num切换到num号窗口

~ W列出当前会话(session)所有窗口,可通过上、下键切换

~ &关闭当前窗口,会有提示。

会话操作

  • tmux new -s 会话名新建会话

  • Ctrl+B D 退出会话,回到 shell 的终端环境

  • tmux ls 终端环境查看会话列表

  • tmux a -t 会话名从终端环境进入会话

  • tmux kill-session -t 会话名销毁会话

如何恢复会话呢?先使用tmux ls查看当前有哪些会话,记住会话名(是冒号左边的内容,默认情况下是一个数字),使用tmux a -t 会话名

图形界面与系统管理

特殊管理

激活超级用户(root)

使用sudo(super user do)提升权限,sudo passwd root:更改root用户口令;sudo reboot:重启系统

更改软件源

/etc/apt/sources.list是一个记录用于系统升级的软件源地址的文本,可修改为更优的软件源。apt-get update:使软件园生效,下载更新列表信息。

网络配置

IP地址、子网掩码、默认网关

域名服务器DNS

网络应用

创建WebServer

apt-get install apache2

共享文件

apt-get install samba

用户管理

sudo命令。

用户账号管理

命令:useraddadduseruserdelusermodpasswd,增删用户,修改用户权限、密码。

组管理

groupaddaddgroupgroupdelgroupmodgpasswd,类似用户账户管理。

系统数据文件

/etc/passwd/etc/shadow/etc/group

磁盘管理

硬盘设备文件:/dev/fd[01]/dev/hd[abcd](IDE硬盘)、/dev/sd[abcd](SCSI硬盘)、/dev/cdrom(光盘)。

硬盘必须分区和格式化后才可使用,分区后,每个分区对应设备名为”设备名”[1256],例如:/dev/sda1表示第一块SCSI硬盘的第一个分区。

一块硬盘最多分4分区,要更多区时,可分一个扩展分区,然后再分为若干逻辑分区

使用fdisk命令来管理硬盘分区,例:fdisk /dev/sdb是对第二块SCSI硬盘进行分区管理。

格式化分区就是在创建文件系统,mkfs <分区名>(格式化)、mkfs -t msdos <分区名>:指定了文件格式,默认文件格式为ext2

mount <分区名> <path>:将某硬盘分区挂载在某个空目录下,使用文件系统

umount <分区名或path>:将某硬盘分区和挂载点脱钩卸载文件系统

df查看分区挂载情况。

虚拟机中的文件题目快速提交工具

在虚拟机中内置了co-submit工具,这个工具可以帮你更方便地提交“文件上传”类型的题目。

要使用这个工具提交题目,首先需在题目页面中记下要提交的题号(题号位于题目名称的右侧),例如图中题目的题号为 712-23

例题

在虚拟机中完成题目后,打开“终端”,使用cd命令切换到答案文件所在地,然后运行co-submit 题号 文件或目录名即可提交该题目。首次提交的时候会提示登录,输入自己的用户名和密码即可。

![提交例题](/img/example of submission.png)

Verilog题目可能需要提交多个.v文件。此时可以在参数列表中指定多个文件名,例如co-submit 233-23 aaa.v bbb.v;也可以指定通配符,例如co-submit 233-23 *.v;还可以提交一整个目录,例如co-submit 233-23 directory-name。工具会==自动对多个文件进行压缩==,无需手动压缩。