OS预习教程笔记(1)

Linux

基本概念

Linux操作系统是基于Linux内核的开源操作系统,课程使用Linux发行版为Ubuntu。

对网上的Linux环境访问后出现界面为:

1
2
3
4
5
6
7
8
开始连接到 git@10.134.170.246  0.1
Welcome to Ubuntu 22.04.1 LTS (GNU/Linux 5.15.0-46-generic x86_64)

* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
Last login: Mon Feb 20 14:15:31 2023 from 10.134.170.231
git@21373343:~ $

最后一行git为用户名,学号为主机名。

本界面即为Ubentu操作系统界面,这种没有图形界面的界面被称为命令行界面CLI。
操作系统的“外壳”(Shell),也即用户界面分为命令行界面(CLI Shell)和图形用户界面(GUI Shell),我们使用的操作系统一般是GUI Shell,服务器运行的Linux一般是CLI Shell。

目录操作

在命令行界面中用户要通过命令指示操作系统,命令格式一般是:命令名 [选项] [参数] ...

cd命令
1
2
3
cd命令
用法:cd [选项] 目录
作用:切换到某个对应目录

举例测试:

1
2
git@21373343:~ $ cd /
git@21373343:/ $

指令意思是通过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
2
3
4
ls命令
用法:ls [选项] [目录]
作用:列出目录中的文件,如果目录参数没有给出就列出当前所在目录中的文件
一些常见选项: -a 显示隐藏的文件 -l 每行只列出一个文件
mkdir命令
1
2
3
mkdir命令
用法:mkdir [选项] 目录
作用:创建一个新目录

举例测试(在用户主目录,即~下面创建一个叫做newdir的新目录并进入):

1
2
3
4
git@21373343:/ $ cd ~
git@21373343:~ $ mkdir newdir
git@21373343:~ $ cd newdir
git@21373343:~/newdir $
pwd命令
1
2
3
pwd命令
用法:pwd [选项]
作用:输出当前目录的绝对路径

举例测试(查看刚才新建的newdir目录的绝对路径):

1
2
@21373343:~/newdir $ pwd
/home/git/newdir
rmdir命令
1
2
3
rmdir命令
用法:rmdir [选项] 目录
作用:删除一个空的目录(当目录非空时无法用这条命令删除)

举例测试(退回用户主目录并删除newdir目录):

1
2
3
4
git@21373343:~/newdir $ cd ..
git@21373343:~ $ rmdir newdir
git@21373343:~ $ ls ~
21373343 //(已经没有newdir目录在用户主目录中)

文件操作

Linux的哲学是一切皆文件,即目录、设备等都以文件形式存储在操作系统中的,以下的操作都是最普适的针对文件的命令,而之前的目录操作是针对目录这一独有文件的命令。

touch命令
1
2
3
touch命令
用法:touch [选项] 文件名
作用:当文件存在时更新文件时间戳,当文件不存在时创建新文件

举例测试(在用户主目录中创建一个helloworld.c文件):

1
2
3
git@21373343:~ $ touch helloworld.c
git@21373343:~ $ ls
21373343 helloworld.c //证明这个新建文件可以被ls看到
rm命令
1
2
3
4
5
6
7
rm命令
用法:rm [选项] 文件
作用:删除文件
一些常见选项:
-r 递归删除目录和其内容,如果要删除非空目录就必须要此选项,否则无法删除
-f 强制删除,不提示用户确认
-i 系统要一一提示用户确认文件是否要删除

注意:rm -rf 文件是危险命令,非必要不使用,因为这相当于不经确认的删除所有目标文件。如果使用root用户执行rm -rf /就会将所有文件和Linux一起删除。

在rm删除多个文件时可用*匹配任意长度字符串,如果要删除当前目录下的所有文件,只可以用rm -rf *(相对路径)或rm -rf ./*(绝对路径),而不能使用rm -rf /*,这样就全删除了。

cp命令
1
2
3
4
5
cp命令
用法:cp [选项] 源文件 目标路径
作用:将源文件(目录等也都可以)复制为目标路径对应的文件(若目标路径是文件)或复制到目标路径(若目标路径是目录)
一些常见选项:
-r 递归复制目录及子目录内的所有内容

举例测试(在用户主目录创建一个test.txt和一个dir目录,再执行cp命令)

1
2
3
4
5
6
7
8
9
git@21373343:~ $ touch test.txt
git@21373343:~ $ mkdir dir
git@21373343:~ $ ls
21373343 dir helloworld.c test.txt
git@21373343:~ $ cp test.txt dir/
git@21373343:~ $ ls
21373343 dir helloworld.c test.txt //原先位置的text.txt并没有消失
git@21373343:~ $ ls dir
test.txt //在新创建的dir目录下也出现了text.txt文件

这里可以发现cp在这种文件+目录的情况下就是一个复制的作用,原来的文件并不会消失。

同时我比较疑惑的是此处dir文件是否需要写成dir/,如果看作相对路径能否省略最后的反斜杠。上网查证得到的信息是这样:

1
2
3
4
譬如C:\Program Files\Java\jdk1.8.0_05\bin与C:\Program Files\Java\jdk1.8.0_05\bin\的区别?

加上的话表示,在bin的文件夹里的所有文件和文件夹,
不加的话,可以访问到bin文件夹里的所有文件和与bin同节的文件。
mv命令
1
2
3
4
5
mv命令
用法:mv [选项] 源文件 目标路径
作用:将源文件(目录等也都可以)移动为目标路径对应文件(若目标路径是文件)或移动到目标路径(若目标路径是目录)
一些常见选项:
-r 递归移动目录及其子目录中的所有内容

举例测试(删除dir目录下的test.txt然后利用mv操作用户主目录中的test.txt):

1
2
3
4
5
6
7
git@21373343:~ $ rm -r dir/test.txt
git@21373343:~ $ ls dir //这行后面没反应证明已经成功删除文件
git@21373343:~ $ mv test.txt dir/
git@21373343:~ $ ls //在用户主目录中已经没有了test.txt
21373343 dir helloworld.c
git@21373343:~ $ ls dir
test.txt //在dir目录中依然有test.txt

证明在文件+目录状态下mv可以被看作一个移动或者剪切指令,将文件转移到新的目录,同时原来的位置的文件消失。

mv可以用来重命名文件(利用文件+文件的命令组合),相当于把一个文件用不同的名字移动到当前目录,例如在dir中用mv test.txt test2.txt就可以将test.txt重命名为test2.txt。

diff命令
1
2
3
4
5
6
diff命令
用法:diff [选项] 文件1 文件2
一些常用选项:
-b 不检查空白字符的不同
-B 不检查空行
-q 只检查有无差异,不显示详细信息

本命令只能对两个纯文本文件使用。

Vim编辑器

教程中介绍的Vim的基本操作举例:
1.创建helloworld.c文件(利用touch命令)
2.打开这个文件,需要在创建这个文件的目录下输入vim helloworld.c来打开
3.输入内容,需要用I键进入插入模式后输入内容
4.完成输入后用Esc回命令模式,用Shift加;进入底线命令模式。观察到左下角有一个冒号,输入w按下回车即保存文件;再进入底线命令模式输入q关闭文件(可以利用wq同时完成)

在目录中创建并进入文件:

1
2
3
4
git@21373343:~ $ touch helloworld.c
git@21373343:~ $ ls
21373343 dir helloworld.c
git@21373343:~ $ vim helloworld.c

插入:

1
2
3
4
5
#include<stdio.h>
int main(){
printf("Hello World!\n");
return 0;
}

最后用Esc,Shift+;和wq保存并返回。

查找操作

在Linux下需要命令来完成搜索文件的功能。

find命令
1
2
3
4
5
find命令
用法:find [路径] <选项>
作用:在给定路径下递归查找文件并输出符合要求的路径。没有给出路径时就在当前目录下查找(即使没有给出路径,在当前目录下也是递归查找)。
一些常用选项:
-name <文件名> 指定需要查找的文件名

例如在用户主目录中查找之前创建的在dir目录下的test.txt文件:

1
2
3
4
git@21373343:~ $ find -name test.txt
./dir/test.txt
git@21373343:~ $ find . -name test.txt
./dir/test.txt

可以发现,给不给路径都可以基于当前所处目录给出正确路径。

grep命令
1
2
3
4
5
6
7
8
grep命令
用法:grep [选项] PATTERN FILE(Pattern匹配字符串,File是文件或目录路径)
作用:输出匹配PATTERN的文件及相关行
一些常用选项
-a 不忽略二进制数据进行搜索
-i 忽略大小写差异
-r 从目录中递归查找
-n 显示行号

教程提到了:grep和find的区别是grep是要匹配文件内容来进行查找,而find是根据文件的属性(例如文件名)来查找。所以可以使用grep来查找某个函数名等特定文本。

例如搜查刚才编辑过的helloworld.c文件(PATTERN处要用单引号):

1
2
git@21373343:~ $ grep -n 'include<stdio.h>' ./helloworld.c 
1:#include<stdio.h>
tree命令
1
2
3
4
5
6
tree命令
用法: tree [选项] [目录名]
作用:直接输出给出目录下的文件树
一些常用选项:
-a 列出全部文件(包括隐藏文件)
-d 只列出目录

tree命令是一个比ls命令更强的命令,但并不是Linux发行版预装载的。我尝试在自己的用户主目录下执行tree:

1
2
3
4
5
6
7
8
git@21373343:~ $ tree .
.
├── 21373343
├── dir
│ └── test.txt
└── helloworld.c

2 directories, 2 files

操作总结

命令总结
1
2
3
4
5
6
7
8
9
cd:进入目录
ls:列出目录内容
pwd:显示当前目录绝对路径
mkdir:创建一个目录
rmdir:删除一个空目录
touch:新建文件
rm:删除文件
cp:复制文件
mv:移动或重命名文件

还有一个帮助命令:man

1
2
3
man命令
用法:man [选项] 命令
作用:查看命令详细说明手册
常用快捷键
1
2
3
4
5
6
7
ctrl+c  终止当前程序执行,由于在Linux CLI中ctrl+c是结束进程,因此复制终端中         内容不能用这个快捷键而应该右键复制
ctrl+z 挂起程序,暂停程序并放到后台,会显示挂起编号,回复程序就用fg [挂起编 号]即可
ctrl+d 终止输入,若在使用shell则退出,在标准输入中使用相当于输入EOF
ctrl+l 清屏,相当于命令clear
ctrl+s 在终端中作用是暂停终端,编辑时误触会卡死,用ctrl+q恢复
左右方向键 控制光标位置
上下方向键 切换最近使用命令

实用工具

GCC

GCC是GNU编辑器套件,包含了C语言编辑器gcc,使用方法如下:

1
2
3
gcc命令
用法: gcc [选项] 源代码文件
作用:编译源代码文件

利用之前编辑的helloworld.c进行编译尝试:

1
2
3
4
5
git@21373343:~ $ gcc helloworld.c
git@21373343:~ $ ls .
21373343 a.out dir helloworld.c //编译后出现a.out文件
git@21373343:~ $ ./a.out
Hello World!

a.out文件是gcc编译器默认的可执行程序文件名。
我们利用命令$ ./a.out进行执行,./代表当前目录下,如果不加上就会在系统PATH中寻找,因为不存在就会报错。
当运行a.out后就出现了输出Hello World!

make & makefile

GNU中有一个“make”工具,用来进行C语言项目构建,其本质为“制作出一个文件”。在开发大项目时可以用make工具将这些文件编译链接成可执行文件。
这些编译链接工作需要人工指导,而人工指导文件就是Makefile。

Makefile的基本格式:

1
2
3
4
5
target: dependencies
command 1
command 2
...
command n

target是构建的目标,可以是目标文件、可执行文件,也可以是标签;dependencies是构建目标需要的其他文件或目标;command是构建出这个目标需要执行的命令,需要注意每个命令前需要用tab而非空格进行缩进,否则会报错。

这些规则的意义就是:想要构建target需要先准备好dependencies然后一条条执行command,最后得到target。

在写完这些后,只需要在shell中输入make target就可以执行相应命令生成目标。

举例测试(用这种方式再次编译刚才的helloworld.c)

1
2
3
4
5
6
7
8
9
10
git@21373343:~ $ touch Makefile  //创建Makefile文件
git@21373343:~ $ ls .
21373343 a.out dir helloworld.c Makefile
git@21373343:~ $ vim ./Makefile
git@21373343:~ $ make hello //产生可执行文件hello
gcc -o hello helloworld.c
git@21373343:~ $ ls .
21373343 a.out dir hello helloworld.c Makefile
git@21373343:~ $ ./hello //执行相应命令
Hello World!

在Makefile文件中的编写:

1
2
3
all: hello
hello: helloworld.c
gcc -o hello helloworld.c

在教程中出现了一个all,我暂时还不知道它的意思,上网搜索好像是这样:相当于给所有的目标(比如这里的hello)取一个名字,这样进行make命令时可以依次生成(此处需要后续再看看)。

ctags

ctags是方便代码阅读的工具,在教程中需要用代码跳转功能。

根据教程,需要先修改Vim的配置文件来支持ctags。修改.vimrc文件(用vim ~/.vimrc进入并编辑),修改如下,增加两句:

1
2
set tags=tags
set autochdir

更改helloworld.c文件:

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

相当于引入一个ctags_test.h的头文件,并创建一个叫做Pair的结构体类型,然后对其中的变量进行赋值。因此我们需要新建这个引入的头文件并进行编辑(`touch ctags_test.h):

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

相当于在头文件中给出了Pair这一结构体的具体结构,只有这样才能够在helloworld.c文件中直接利用这一人为定义的结构体。

之后执行命令ctags -R *就会在目录中出现tags文件,用来索引符号名:

1
2
3
git@21373343:~ $ ctags -R *
git@21373343:~ $ ls
21373343 a.out ctags_test.h dir hello helloworld.c Makefile tags

再次打开helloworld.c文件并将光标放在a或b上,按下ctrl+]就可以跳转到定义结构体a,b的头文件对应位置处。再利用ctrl+o就可以回到原位。

tmux

很多时候Linux要同时运行多个程序并观察输出,所以不能让它只显示一个程序;而且当终端和Linux断开连接时终端前台进程会被杀死,而我们很多时候需要一直保持连接状态,所以我们可以利用tmux来进行终端窗口和进程分离并同时显示多个进程运行。

tumx基本操作如下:

1
2
3
4
5
6
7
8
9
输入命令tumx  进入tmux新会话
ctrl+B+shift+% 左右分屏(同时按下Ctrl和B,然后松开这两个键,紧接着立刻输入“%”,下面同理)
ctrl+B+shift+' 上下分屏
ctrl+B+上下左右 根据案件方向切换到某窗格(不能三个一起按,先前两个)
ctrl+B+space 切换布局,上下变左右、左右变上下
ctrl+B+X 关闭正在使用的窗格(按Y确认)
ctrl+B+D 分离当前会话并回到Shell环境,此时程序仍然在会话中状态
输入命令tmux ls 查看当前有哪些会话并记住会话名(一般为数字)
输入命令tmux a -t 会话名 回到原来的对应会话

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
2
3
1 #!/bin/bash
2 #My first Shell script!
3 echo "Hello, world!"

这些内容的意思是:

第一行:指定脚本默认运行程序(解释器),这里指定的是bash(#!出现在脚本文件第一行前两个字符被称为Shebang,当存在时加载器会分析其后内容并作为解释器)。

第二行:以#开头,是注释。

第三行:输出,此处是用echo将后面的字符串输出,由于Shell脚本是命令的组合,可把echo也看成命令。如果直接在终端写入这一行命令:

1
2
git@21373343:~ $ echo "Hello, world!"
Hello, world!
变量

在Shell中定义变量不需要指定类型,定义方法为(var_name变量名,value变量值):

var_name=value

这种定义方式等号两侧不允许有空格。同时用$var_name可以获得变量值(建议使用${var_name},可以标识变量名的边界)

教程中也对刚才的脚本做出了更改:

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

运行脚本./hello.sh也能弹出Hello, world!

脚本参数(特殊变量)

在Shell中也可以进行参数传递,在其中参数体现为特殊变量,在执行语句中参数用空格分隔,每个参数在脚本中都是一个字符串变量。第一个字符串映射到变量1,第二个映射到2……

教程中再次修改hello.sh:

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

此时运行时如果参数传递如下(应该是将字符串’world’和’OS‘当作参数进行了传递):

1
2
git@21373343:~ $ ./hello.sh world OS
Hello, world and OS!

还要注意只能在双引号中添加参数变量$1等,若用单引号则会原样输出。

特殊变量:$# 传递的参数个数;$* 一个字符串,内容是传递的全部参数。

条件与循环

if语句块如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if condition1
then
command11
command12
......
elif condition2
then
command21
command22
......
else
command31
command32
......
fi

fi是if的倒写,代表语句块结束,elif和else都可以省略。

试一下教程给出的例子,这里(())是比较整数大小的表达方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
 1 #!/bin/bash
2 if (( $1 > $2))
3 then
4 echo "first > second"
5 elif (( $1 < $2 ))
6 then
7 echo "first < second"
8 elif (( $1 == $2))
9 then
10 echo "first == second"
11 else
12 echo "I don't know..."
13 fi

尝试输入:

1
2
3
4
5
6
7
8
9
10
11
12
13
git@21373343:~ $ ./hello.sh 2 3
first < second
git@21373343:~ $ ./hello.sh 3 3
first == second
git@21373343:~ $ ./hello.sh 3 2
first > second
git@21373343:~ $ ./hello.sh world 0S
./hello.sh:2: ((: world > 0S:数值太大不可为算术进制的基 (错误符号是 "0S")
./hello.sh:5: ((: world < 0S:数值太大不可为算术进制的基 (错误符号是 "0S")
./hello.sh:8: ((: world == 0S:数值太大不可为算术进制的基 (错误符号是 "0S")
I don't know...
git@21373343:~ $ ./hello.sh a b
first == second

while语句块如下:

1
2
3
4
5
6
while condition
do
command1
command2
...
done #表示do语句块结束

教程举例:

1
2
3
4
5
6
7
8
9
#!/bin/bash
mkdir files
cd files
i=1
while (($i <= $1))
do
touch "file$i.txt"
let i=i+1 #或者i=$((i+1)),此处let是为变量赋值的命令
done

这段程序应该意思是读取参数$1,然后将i从1开始递增并在files目录中新建对应文件名的文件。观察结果如下:

1
2
3
4
5
6
git@21373343:~ $ ./hello.sh 2  //运行,给参数赋值2
git@21373343:~ $ ls
21373343 files hello.sh //出现files目录
git@21373343:~ $ cd files
git@21373343:~/files $ ls
file1.txt file2.txt //其中有两个新建文件
函数

函数的定义方式如下:

1
2
3
4
function fun_name() {
#body...
return int_value
}

几点注意:首先function和()可以省略一个;其次返回语句非必要,可以不返回值;最后若要返回则只能返回一个[0,255]间的整数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 "$?"."

尝试运行:

1
2
3
4
5
git@21373343:~ $ ./hello.sh
Hello, world!
I've got 2 and 3!
I'm computing first + second!
The sum is 9.

可见,每个函数体中的参数变量都只会接受这个函数给的值,同时$?给出的是最后一个函数的返回值。

sed的使用

之前对文本的编辑只用了Vim工具,但是也可以通过Shell脚本中的命令对文本文件内容进行编辑输出,其工具就是sed。通过sed命令可以实现文本内容编辑和选择输出的自动化。

1
2
3
4
5
6
7
8
9
10
11
12
sed命令
用法:sed [选项] '命令' 输入文本
一些常用选项:
-n 安静模式,只显示sed处理过的内容,否则显示输入文本全内容
-i 直接修改读取的档案内容而不输出。否则只输出不编辑,即当不加此选项时只是输出了编辑后的文件内容,而文件本身没有被改变
一些常用命令:
<行号>a<内容>: 新增,在行号后新增一行相应内容。行号可以是“数字”,在这一行之后新增,也可以是“起始行,终止行”,在其中的每一行后新增。当不写行号时,在每一行之后新增。使用$表示最后一行。后面的命令同理
<行号>c<内容>:取代。用内容取代相应行的文本
<行号>i<内容>:插入。在当前行的上面插入一行文本
<行号>d:删除当前行的内容
<行号>p:输出选择的内容。通常与选项-n一起使用
s/<re>/<string>:将<re>(正则表达式)匹配的内容替换为<string>

教程举例:

1
2
3
4
5
6
7
8
9
10
11
12
sed -n '3p' my.txt
# 输出 my.txt 的第三行
sed '2d' my.txt
# 删除 my.txt 文件的第二行
sed '2,$d' my.txt
# 删除 my.txt 文件的第二行到最后一行
sed 's/str1/str2/g' my.txt
# 在整行范围内把 str1 替换为 str2。
# 如果没有 g 标记,则只有每行第一个匹配的 str1 被替换成 str2
sed -e '4astr ' -e 's/str/aaa/' my.txt
#-e 选项允许在同一行里执行多条命令。例子的第一条是第四行后添加一个 str,
# 第二个命令是将 str 替换为 aaa。命令的执行顺序对结果有影响。

重定向和管道

重定向

重定向能够把命令的控制台输出写入文件中。Linux定义了三种流:

  1. 标准输入:stdin,用0表示
  2. 标准输出:stdout,用1表示
  3. 标准错误: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字符串。