Skip to main content

字符界面操作进阶

关于 Linux 字符界面相关操作的讲解。

内容要点

  1. 链接文件
  2. 信息显示和文本操作命令
  3. 文件查找
  4. vi 文本编辑器
  5. 命令补全、命令别名、命令历史
  6. 重定向、管道、命令替换、命令聚合
  7. 掌握 Shell 变量的定义、作用域和使用
  8. 掌握 Shell 脚本的使用

链接文件

ln 命令详解

功能:创建链接文件。 格式:ln [参数] <被链接的文件> <链接文件名> 参数:

  • -s :创建软链接(符号链接)。
  • -f :强行创建链接,不论其是否存在。
  • -i :覆盖原有文件之前先询问用户。

如:

 ln somefile hardlinkfile
ln -s somefile softlinkfile
ln -s somedir softlinkfile

文件链接概念

  • 文件都有文件名与数据,这在 Linux 上被分成两个部分:用户数据 (user data) 与元数据 (metadata)。用户数据,即文件数据块 (data block),数据块是记录文件真实内容的地方。
  • 元数据则是文件的附加属性,如文件大小、创建时间、所有者、文件所在的 inode(节点)、文件名等信息。 在 Linux 中,元数据中的 inode 号,才是文件的唯一标识,而非文件名。文件名仅是为了方便人们的记忆和使用。系统或程序通过 inode 号寻找正确的文件数据块。
  • 多个 filename 可以对应同一个 inode。但一个 filename 不能有多个 inode。

image-20220913193948713

硬链接和软链接的比较

  • 从硬链接的角度看:硬链接是多个文件名对应同一个 inode。如果将任何一个文件名删除,档案的 inode 与 block 都还存在,依然还可以通过另一个文件名来读取正确的数据。此外,不论用哪一个文件名来编辑,最终的结果都会写入相同的 inode 和 block 中,因此均能进行数据的修改。

    如:A 是 B 的硬链接(A 和 B 都是文件名),则 A 的目录项中的 inode 节点号与 B 的目录项中的 inode 节点号相同,即一个 inode 节点对应两个不同的文件名,两个文件名指向同一个文件,A 和 B 对文件系统来说是完全平等的。如果删除了其中一个,对另外一个没有影响。每增加一个文件名,inode 节点上的链接数增加一。每删除一个对应的文件名,inode 节点上的链接数减一,直到为 0,inode 节点和对应的数据块被回收。

  • 从软链接的角度看:软链接就是建立一个独立的文件,而这个文件会让数据的读取指向它 Link 的那个文件的档名,由于只是作为指向的动作,所以当来源档案被删除之后,软连接的档案无法开启,因为找不到原始档名。

    如:A 是 B 的软链接(A 和 B 都是文件名),A 当中的 inode 节点号与 B 当中的 inode 节点号不相同,分别指向两块不同的数据块,存储的内容是不同的。A 的数据块中存放的只是 B 的路径。A 和 B 之间是“主从”关系,如果 B 被删除了,A 仍然还存在(因为两个是不同的文件),但指向的是一个无效的链接。

硬链接和软链接的不同

硬链接:

  • 链接文件和被链接文件必须位于同一个文件系统内
  • 不能建立指向目录的硬链接

软链接:

  • 链接文件和被链接文件可以位于不同文件系统
  • 可以建立指向目录的软链接

文本文件操作命令

常用的文本文件提取和分析命令:

命令功能
cat, more显示文本文件内容
head, tail显示文本文件的前或后若干行(横向截取文本文件内容)
cut纵向切割出文本指定的部分(纵向截取文本文件内容)
grep在文本文件中查找指定的字符串(按关键字提取文本文件中匹配的行)

如:

# 滚屏显示文件 /etc/passwd 的内容
cat /etc/passwd
# 滚屏显示文件 /etc/passwd 的内容,并显示行号
cat -n /etc/passwd
# 分屏显示文件 /etc/passwd 的内容(注意空格键、回车键和q的使用)
more /etc/passwd
# 从第10行分屏显示文件 /etc/passwd 的内容
more +10 /etc/passwd
# 分屏显示文件 /etc/passwd 的内容(注意空格键、回车键、PgDn键、PgUp键和q的使用)
less /etc/passwd
# 显示文件 myalllist 前4行的内容
head -4 myalllist
# 显示文件 myalllist 后4行的内容
tail -4 myalllist
# 显示文件 myalllist 从10行开始到文件尾的内容
tail +10 myalllist
# 跟踪显示不断增长的文件结尾内容(通常用于显示日志文件)
tail -f /var/log/messages

管道命令详解

管道命令的背景

UNIX 系统的一个基本思想:一连串的小命令能够解决大问题。

理想情况下,一连串小命令里的每个小命令都能够很好地完成一项单一的工作。现在需要有一些东西能够将这些简单的命令连接起来,这样管道就应运而生。

许多 Linux 命令具有过滤特性,即一条命令通过标准输入端口接受一个文件中的数据,命令执行后产生的结果数据又通过标准输出端口送给后一条命令,作为该命令的输入数据。后一条命令也是通过标准输入端口而接受输入数据。

管道命令的使用

管道(使用符号“|”表示)用来连接命令:命令1 | 命令2

→ 这表示将命令 1 的 STDOUT 发送给命令 2 的 STDIN。但注意 STDERR 不能通过管道转发。

我们可以用来组合多种工具的功能:命令1 | 命令2 | 命令3 | ……

→ 管道线中的每一条命令都作为一个单独的进程运行,每一条命令的输出作为下一条命令的输入。同时由于管道线中的命令总是从左到右顺序执行的,因此管道线是单向的。

管道使用练习:

  1. 从 /etc/passwd 文件中提取用户信息,向终端上输出用户的信息格式为name:uid:gid

    head -5 /etc/passwd | tail -1 | cut -d ":" -f 1,3,4
  2. 如何从/etc/passwd 当中查找指定的用户名(第一个字段),并只显示该用户的行号?

sort 命令详解

功能:以行为单位对文件进行排序

格式:sort [选项] [<文件> …]

选项:

  • -r :逆向排序
  • -f :忽略字母的大小写
  • -n :根据字符串的数值进行排序
  • -u :对相同的行只输出一行
  • -t c :使用给定的字符 c 做为列的间隔符
  • -b :忽略前导的空格
  • -i :只考虑可打印字符
  • -k N :以第 N 列进行排序(默认以空格或制表符作为列的间隔符)

如:

ls -l | sort -nk 5
sort -nrk3 -t : /etc/passwd # 按 uid 和”:“分隔符显示信息
sort -nrk3 -t : /etc/passwd | cut -d : -f 3
df | sort -nrk 2 # 按文件系统磁盘使用空间统计(k 必须和数字直接在一起)
du --max-depth=2 | sort -nk 1 # 按目录显示占用空间大小

一般来说,选项/参数是没有顺序要求的,但如果是带第二参数或有先后顺序区别时,顺序要求就会变得很重要。

find 命令详解

功能:在文件系统中查找匹配的文件

格式:find [<起始目录> ...] [<选项表达式>] [<条件匹配表达式>] [<动作表达式>]

说明

  • <起始目录>:对每个指定的 <起始目录> 递归搜索目录树(省略 <起始目录> 表示当前目录)
  • <选项表达式>:控制 find 命令的行为
  • <条件匹配表达式>:根据匹配条件查找文件
  • <动作表达式>:指定对查找结果的操作,默认为显示在标准输出

其中不带任何参数的 find 命令将在屏幕上递归显示当前目录下的文件列表。

基础选项表达式

选项说明
-maxdepth LEVELS设置最大的查找深度
-mount/ -xdev查找文件时不跨越文件系统
-L如果遇到符号链接文件,就跟踪链接所指的文件
-depth/ -d查找目录自身之前先处理目录中的文件(即深度优先)
--help显示 find 命令帮助信息
--version显示 find 的版本

如:

find # 递归显示当前目录的文件列表
find / # 递归显示 / 目录的文件列表
find / -maxdepth 3 # 递归显示 / 目录的文件列表(仅限于3层目录)
find / -xdev # 递归显示 / 目录的文件列表(仅限于 / 文件系统)
find /home /www /srv # 递归显示 /home、/www、/srv 目录的文件列表

条件匹配表达式

文件名相关

条件说明
-name PATTERN匹配文件名
-iname PATTERN匹配文件名(忽略大小写)
-lname PATTERN匹配符号链接文件名
-ilname PATTERN匹配符号链接文件名(忽略大小写)
-wholename PATTERN匹配文件的完整路径(不把 ”/“ 和 ”.“ 作为特殊字符)
-iwholename PATTERN匹配文件的完整路径(忽略大小写)

PATTERN 使用 Shell 的匹配模式,可以使用 Shell 的通配符(*、?[]),但要用 “” 或 ‘’ 括起来

如:

find -name myfile
find -maxdepth 2 -name symphony
find -name 'd*'
find -name '???'
find -name 'ch[0-2][0-9].txt*'
find -iname '[a-z]*'
find ~ -name "*.txt"
find . -name "[A-Z]*"
find /etc -name "host*"

时间属性相关

条件说明
-amin N查找 N 分钟以前被访问过的所有文件
-atime N查找 N 天以前被访问过的所有文件
-cmin N查找 N 分钟以前文件状态被修改过的所有文件(如权限修改)
-ctime N查找 N 天以前文件状态被修改过的所有文件(如权限修改)
-mmin N查找 N 分钟以前文件内容被修改过的所有文件
-mtime N查找 N 天以前文件内容被修改过的所有文件
-used N查找被改动过之后,在 N 天内被存取过的所有文件

以上选项中的 N 可以有三种输入方式:N, +N, -N。

如:

  • 20 表示等于 20
  • +20 表示大于 20(21,22,23 等)
  • -20 表示小于 20(19,18,17 等)

如:

# 在 /home 下查找最近2天之内内容修改过的文件:
$ find /home -mtime -2
# 在 /home 下查找最近2天之内被访问过的文件:
$ find /home -atime -2
# 在 /home 下查找60分钟之前改动过的文件:
$ find /home -mmin +60
# 在 /home 下查找最近5分钟之内修改过的文件:
$ find /home -mmin -5
# 在 /home 下查找30分钟之前被访问过的文件:
$ find /home -amin +30
# 在 /home 下查找最近在2日内被改动之后被访问过的文件:
$ find /home -used -2

此外,我们还有:

条件说明
-newer FILE查找所有比 FILE 的内容修改时间新的文件
-cnewer FILE查找所有比 FILE 的状态修改时间新的文件(比如权限修改)
-anewer FILE查找所有比 FILE 的访问时间新的文件

其中 FILE 为要比对的样板文件或目录。

如:

# 在/home下查找修改时间比 tmp.txt 新的文件或目录:
$ find /home -newertmp.txt
# 在/home下查找访问时间比 tmp.txt 新的文件或目录:
$ find /home -anewer tmp.txt

用户和组相关

条件说明
-uidNID 为 N 的用户的所有文件
-gidNID 为 N 的组的所有文件
-user USERNAME用户名为 USERNAME 的所有文件
-group GROUPNAME组名为 GROUPNAME 的所有文件
-nouser文件属于不在 /etc/passwd 文件中的用户
-nogroup文件属于不在 /etc/group 文件中的组

同样的,N 可以使用 N, +N, -N。

如:

# 查找在系统中已作废用户(在 /etc/passwd 中无记录)的文件
$ find / -nouser
# 查找在系统中已作废组(在 /etc/group 中无记录)的文件
$ find / -nogroup
# 查找属于 osmond 用户的文件
$ find / -user osmond
# 查找属于 students 组的文件
$ find / -group students
# 查找 UID 为 502 的所有文件
$ find / -uid 502
# 查找 UID 大于 500 的用户的所有文件
$ find / -uid +500
# 查找 GID 小于 60 的所有文件
$ find / -gid -60

文件权限相关

条件说明
-perm MODE精确匹配权限模式为 MODE 的文件
-perm -MODE匹配权限模式至少为 MODE 的文件(用户、组和其他人这三种权限必须都匹配)
-perm /MODE 或 -perm +MODE匹配权限模式至少为 MODE 的文件(用户、组和其他人这三种权限中有一种匹配即可)

MODE 与 chown 命令的书写方式一致,既可以使用字符模式也可以使用八进制的数值模式,通常使用八进制的数值模式

如:

# 在 /home 目录下查找权限为 700 的所有文件或目录
$ find /home -perm 700
# 在/home目录下查找权限至少为 740 的所有文件或目录
$ find /home -perm -740
# 在 ~/bin 目录下查找权限为 111 的所有文件或目录(或者所属用户可执行,或者组可执行,或者其他人可执行)
$ find ~/bin -perm /111

类型和大小相关

条件说明
-type TYPE查找类型为 TYPE 的文件,可用的类型:b-块设备文件;c-字符设备文件;d-目录文件;p-命名管道;f-普通文件;l-符号(软)链接;s-socket 文件
-links N查找有 N 个(硬)链接的文件
-inum N查找文件 inode 为 N 的文件
-samefile NAME查找与 NAME 文件具有相同 inode 的文件
-size N[bcwkMG]查找文件大小为 N 的文件,单位可以为:b-512 字节的块(省略单位的默认值);c-字节;k-Kilobytes;M-Megabytes;G-Gigabytes
-empty查找空文件(大小为 0)

同样的,N 可以使用 N, +N, -N。

$ find . -type d
$ find /home -links +2
$ find . -size +10k
$ find /tmp -size 1G
$ find . -size-10M
$ find /var/log -empty

组合条件表达式

  • 可以使用逻辑运算符与、或、非组成的复合条件,并可以用”()“改变默认的操作符优先级。
  • 若以空格作为各个表达式的间隔符,则各个表示式之间是的关系。
表达式说明
(EXPR)改变操作符优先次序,一些 UNIX 版的 find 命令要使用 \( EXPR \) 的形式
!EXPR表示对表达式取反
EXPR1 EXPR2与逻辑,若 EXPR1 为假,将不再评估 EXPR2
EXPR1 -a EXPR2与 EXPR1 EXPR2 功能一致
EXPR1-o EXPR2逻辑或,若 EXPR1 为真,将不再评估 EXPR2
EXPR1, EXPR2若 EXPR1 为假,继续评估 EXPR2

如:

find /tmp -size +10M -a -atime +21
find /home \( -user jjheng -o -user osmond \) -size +10M
find /tmp ! -user osmond
find /mnt -name '*.txt' ! -fstype vfat
find /tmp -name 'l*' -type l
find /var/log -name '*.log' -mtime +5
find -newer FILE1 ! -newer FILE2

动作表达式

表达式说明
-print在标准输出上列出查找结果(每行一个文件)
-ls使用 ‘ls -dils’ 在标准输出上列出查找结果
-prune忽略对某个目录的查找
-exec COMMAND {} ;对符合查找条件的文件执行 Linux 命令
-ok COMMAND {} ;对符合查找条件的文件执行 Linux 命令;与 -exec 不同的是,它会询问用户是否需要执行
  • {} 两个大括号之间不能有空格,表示查找到的对象
  • \; 表示命令结束,反斜杠与前面的大括号之间必须要留空格

如:

# 查找并删除当前目录及其子目录下所有扩展名为 .tmp 的文件
$ find . -name '*.tmp' -exec rm {} \;
# 在logs目录中查找7天之内未修改过的文件并在删除前询问
$ find logs -type f -mtime +7 -ok rm {} \;
# 从当前目录下查找所有以.repo为后缀的文件,并为其改名(添加.bak后缀)
$ find . -name '*.repo' -type f -exec mv {} {}.bak \;
# 查询并删除一周以来从未访问过的以 .o 结尾,或名为 a.out 且不存在于 vfat 文件系统中的所有文件
$ find / ( -name a.out -o -name ‘*.o’ ) -atime +7 ! -fstype vfat -exec rm {} \;

# 排除文件:
# 查找当前目录下(除了 book 目录)之外的所有 .sh 文件
$ find . -name book -prune -o -name '*.sh' -print
# 显示当前目录下(除了 book/server 目录)之外的所有文件
$ find . -path ./book/server -prune -o -print
# 查找当前目录下(除了 book/server 目录)之外的所有 .sh 文件
$ find . -path ./book/server -prune -o -name '*.sh' -print
# 显示当前目录下除 book/server 和 book/server-utf8 目录的所有文件
$ find . -path './book/server*' -prune -o -print
# 查找当前目录下(除了 book/server 和 book/server-utf8 目录)的所有 .sh 文件
$ find . -path './book/server*' -prune -o -name '*.sh' -print
# 查找当前目录下(除了 book/server 和 book/basic 目录)的所有 .sh 文件
$ find . (-path ./book/server -o -path ./book/basic) -prune -o -name '*.sh' -print
# 注意 -prune -o 一般为固定用法,表示排除的列表不再执行后续操作

# 以下表达式等价:
$ find -name \*.sh -exec cp {} /tmp \;
$ find -name '*.sh' -exec cp {} /tmp ';'
$ find -name "*.sh" -exec cp {} /tmp ";"
$ find -name \*.sh -exec cp \{\} /tmp \;
$ find -name '*.sh' -exec cp '{}' /tmp ';'
$ find -name “*.sh” -exec cp{}” /tmp ";

vi 文本编辑器详解

简介

  • vi 是 “Visual interface” 的简称,它可以执行输出、删除、查找、替换、块操作等众多文本操作,而且用户可以根据自己的需要对其进行定制,这是其他编辑程序所没有的。
  • vi 不是一个排版程序,它不像 M$ Word 或 WPS 那样可以对字体、格式、段落等其他属性进行编排,它只是一个文本编辑程序。
  • vi 是全屏幕文本编辑器,它没有菜单,只有命令。
  • vim 即 Vi Improved,vi 克隆版本之一。

命令

命令说明
vi直接进入
vi filename打开或新建文件 filename,并将光标置于第一行首
vi +n filename打开文件 filename,并将光标置于第 n 行首
vi+ filename打开文件 filename,并将光标置于最后一行首
vi +/pattern filename打开文件 filename,并将光标置于第一个与 pattern 匹配的串处
vi -r filename打开上次用 vi 编辑时发生系统崩溃,恢复 filename

3 种运行模式

  • 普通(normal)模式
  • 插入(insert)模式
  • 命令行模式(末行模式)

image-20220913212347673

模式须知:

  • Normal 模式下,用户按冒号 “:” 即可进入 Command 模式,此时 vim 会在显示窗口的最后一行 (屏幕的最后一行) 显示一个 “:” 作为 Command 模式的提示符,等待输入命令。
  • 多数文件管理都是在此模式下执行的 (如保存文件等)
  • Command 模式中所有的命令都必须按 <回车> 后执行,命令执行完后,vim 自动回到 Normal 模式。
  • 若在 Command 模式下输入命令过程中改变了主意,可按 Esc 键,或用退格键将输入的命令全部删除之后,再按一下退格键,即可使 vi 回到 Normal 模式下。

常见内容操作

  • 复制单行(普通模式):yy
  • 剪切单行(普通模式):dd 或 cc
  • 选取内容(普通模式):输入 v,高亮选取
  • 复制指定开始和结束行号之间的内容(命令行模式):21,27y,然后回车
  • 剪切开始和结束行之间的文本:上述方法中的 y 改成 d
  • 粘贴文本(普通模式):在粘贴位置(上一行)按 p 键
  • 查找字符串(普通模式):/或?,输入查找内容,回车。普通模式下按 n 键跳到下一条查找结果,按 N 键上一条结果。
  • 撤销上一次操作(普通模式):u
  • 恢复上一次操作:ctrl+u
  • 替换当前行内所有指定字符串(命令行模式):s/old/new/g,如果不加 g 只替换第一个 old
  • 替换指定行内所有指定字符串:2,10 s/old/new/g
  • 全文替换:% s/old/new/g
  • 替换第 n 行到最后一行所有指定字符串:n,$ s/old/new/g
  • 替换当前行到最后一行:.,$, s/old/new/g
  • 存盘退出 Vi:ZZ

常见命令

:n1,n2 co n3 # 块复制,复制到 n3 后面
:n1,n2 m n3 # 块移动,移动到 n3 后面
:w # 保存当前编辑文件,但并不退出
:w newfile # 存存为名为 “newfile” 的文件
:wq # 存盘退出 Vi
:q! # 用于不存盘退出 Vi
:q # 直接退出 Vi(不做修改)

设置 vi 环境:

:set number # 在编辑文件时显示行号
:set nonumber # 不显示行号
:set autoindent # 缩进,常用于程序的编写
:set noautoindent # 取消缩进
:set tabstop=value # 设置显示制表符的空格字符个数
:set # 显示设置的所有选项
:set all # 显示所有可以设置的选项

文本文件分析命令

wc # 统计文本
grep # 在文本文件中查找指定的字符串
sed # 文本流编辑器

wc 命令

功能:统计文本文件的行数、字数、字符数 格式:wc [选项] [<文件> …] 如:

wc file
wc -l file # 统计行数
wc -w file # 统计字数
wc -c file # 统计字符数
wc -L file # 统计最长一行的长度

grep 命令

grep 是一个强大的文本搜索工具。

命令格式:grep [options] PATTERN [FILE...]

说明:

  • PATTERN:查找条件,可以是普通字符串,也可以是正则表达式(RE,通常用单引号将 RE 括起来)。
  • FILE:是要查找的文件,可以是用空格间隔的多个文件,也可是使用 Shell 的通配符,定义在多个文件中进行查找,省略时表示在标准输入中查找。

grep 命令不会对输入文件进行任何修改或影响,可以使用输出重定向将结果存为文件。

命令选项:

选项说明
-c只显示匹配行的次数
-i搜索时不区分大小写
-n输出匹配行的行号
-v输出不匹配的行(反向选择)
-r对目录(子目录)的所有文件递归地进行
-l列出匹配 PATTERN 的文件名
--color=auto对匹配内容高亮显示
-A NUM同时输出匹配行的后 NUM 行
-B NUM同时输出匹配行的前 NUM 行
-C NUM同时输出匹配行的前、后各 NUM 行

如:

# 在文件 myfile 中查找包含字符串 mystr的行
$ grep mystr myfile
# 显示 myfile 中第一个字符为字母的所有行
$ grep '^[a-zA-Z]' myfile
# 在文件 myfile 中查找首字符不是 # 的行(即过滤掉注释行)
$ grep -v '^#' myfile
# 过滤掉/etc/samba/smb.conf的注释行和空行
$ egrep -v ‘^#|^$|^;’ /etc/samba/smb.conf

正则表达式

正则表达式是使用某种模式(pattern)去匹配(matching)一类字符串的一个公式。

通常使用正则表达式进行查找、替换等操作。在适当的情况下使用正则表达式可以极大地提高工作效率。

支持正则表达式的文本处理工具

基本的正则表达式 Basic regular expression (BRE):

  • qgrep 按模式匹配文本
  • qsed 一个流编辑器
  • qvim 一个屏幕编辑器
  • qemacs 一个屏幕编辑器

扩展的正则表达式 Extended regular expression (ERE):

  • qegrep 按模式匹配文本
  • qawk 进行简单的文本处理

正则表达式的组成

正则表达式由一些普通字符和一些元字符(metacharacters)组成。

普通字符包括大小写的字母、数字(即所有非元字符);元字符则具有特殊的含义。

正则表达式的元字符:

元字符含义类型举例说明
^匹配首字符BRE^x以字符 x 开始的字符串
$匹配尾字符BREx$以 x 字符结尾的字符串
.匹配任意一个字符BREl..elove, life, live …
?匹配任意一个可选字符ERExy?x, xy
*匹配零次或多次重复BRExy*x, xy, xyy, xyyy …
+匹配一次或多次重复ERExy+xy, xyy, xyyy …
[…]匹配任意一个字符BRE[xyz]x,y, z
()对正则表达式分组ERE(xy)+xy, xyxy, xyxyxy, …
{n}匹配 n 次BREgo{2}glegoogle
{n,}匹配最少 n 次BREgo{2,}glegoogle, gooogle, goooogle …
{n,m}匹配 n 到 m 次BREgo{2,4}glegoogle, gooogle, goooogle
{n}匹配 n 次EREgo{2}glegoogle
{n,}匹配最少 n 次EREgo{2,}glegoogle, gooogle, goooogle …
{n,m}匹配 n 到 m 次EREgo{2,4}glegoogle, gooogle, goooogle
|以或逻辑连接多个匹配EREgood|bon匹配 good 或 bon
\转义字符BRE\**
^非(仅用于启始字符)BRE[^xyz]匹配 xyz 之外的任意一个字符
-用于指明字符范围 (不能是首字符和尾字符)BRE[a-zA-Z]匹配任意一个字母
\转义字符BRE[\.].

sed 命令详解

sed 简介

sed 是一个流编辑器(stream editor)。sed 是一个非交互式的行编辑器,它在命令行中输入编辑命令、指定被处理的输入文件,然后在屏幕上查看输出。输入文件可以是指定的文件名,也可以来自一个管道的 输出。

与 vi 不同的是 sed 能够过滤来自管道的输入。在 sed 编辑器运行的时候不必人工干涉,所以 sed 常常被称作批编辑器。

sed 默认不改变输入文件的内容,且总是将处理结果输出到标准输出,可以使用输出重定向将 sed 的输出保存到文件中。

sed 的工作方式

sed 以按顺序逐行的方式工作,过程为:

  • 从输入读取一行数据存入临时缓冲区,此缓冲区称为模式空间(pattern space)。
  • 按指定的 sed 编辑命令处理缓冲区中的内容。
  • 把模式空间的内容送往屏幕并将这行内容从模式空间中删除。
  • 读取下面一行。重复上面的过程直到全部处理结束。

sed 命令

格式:sed [选项] [-e] cmd1 [[-e cmd2] ... [-e cmdn]] [input-file]...

说明:

  • 在命令行上执行 sed 编辑命令。可以指定多个编辑命令,每个编辑命令前都要使用 -e 参数,sed 将对这些编辑命令依次进行处理。若只有一个编辑命令时,-e 可以省略。
  • 每个 sed 的编辑命令 cmdX 均应使用单引号括起来。
  • input-file:sed 处理的文件列表,若省略,sed 将从标准输入中读取输入,也可以从输入重定向或管道获得输入。
  • 所有用于查询或替换的字符串都要使用 /…/ 引起来

选项:

  • -r:使用扩展正则表达式进行模式匹配。
  • -i:直接对输入文件进行 sed 的命令操作,而不是由屏幕输出。
  • -n:使用安静(silent)模式。在一般 sed 的用法中,所有来自 STDIN 的资料一般都会被列出到屏幕上。但如果加上 -n 参数后,则只有经过 sed 特殊处理的那一行(或者动作)才会被列出来。如果使用删除(d)的命令,不能使用 -n,否则将会不显示任何内容。
  • -e:直接在指令列模式上进行 sed 的动作编辑。
  • -f:直接将 sed 的动作写在一个档案内,-f filename 则可以执行 filename 内的 sed 动作。

命令:

  • a:新增,a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)。

  • i:插入,i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行)。

  • c:取代,c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行。

  • d:删除,因为是删除,所以 d 后面通常不接任何内容;

  • p:列印,将某个选择的资料列出。

  • /字符串/p:查询包含字符串的所有行。

  • s:取代,可以直接进行取代的工作。通常这个 s 的动作可以搭配 g 使用。

  • g: 行内全面替换。

    如:sed ‘1,20s/old/new/g’ abc

    • 如果不加 g,那么每行第一次出现的 old 用字符串 bew 替换;
    • 加上 g,使得 sed 对所有符合的字符串都被替换。

如:

删除某行

sed '1d' ab # 删除第一行
sed '$d' ab # 删除最后一行
sed '1,2d' ab # 删除第一行到第二行
sed '2,$d' ab # 删除第二行到最后一行

显示某行

sed -n '1p' ab # 显示第一行
sed -n '$p' ab # 显示最后一行
sed -n '1,2p' ab # 显示第一行到第二行
sed -n '2,$p' ab # 显示第二行到最后一行

使用模式进行查询

sed -n '/ruby/p' ab # 查询包括关键字ruby所在所有行
sed -n '/\$/p' ab # 查询包括关键字$所在所有行,使用反斜线\屏蔽特殊含义

增加一行或多行字符串

$ cat ab
# Hello!
# ruby is me,welcome to my blog.
# end
$ sed '1a drink tea' ab #第一行后增加字符串"drink tea"
# Hello!
# drink tea
# ruby is me,welcome to my blog.
# end
$ sed '1,3a drink tea' ab #第一行到第三行后增加字符串"drink tea"
# Hello!
# drink tea
# ruby is me,welcome to my blog.
# drink tea
# end
# drink tea
$ sed '1a drink tea\nor coffee' ab #第一行后增加多行,使用换行符\n
# Hello!
# drink tea
# or coffee
# ruby is me,welcome to my blog.
# end
# 代替一行或多行
$ sed '1c Hi' ab# # # #第一行代替为Hi
# Hi
# ruby is me,welcome to my blog.
# end
$ sed '1,2c Hi' ab# # #第一行到第二行代替为Hi
# Hi
# end

插入

# 将 myfile 文件中每行第一次出现的foo用字符串bar替换,然后将该文件内容输出到标准输出
sed -e 's/foo/bar/' myfile
# g 使得 sed 对文件中所有符合的字符串都被替换
sed -e 's/foo/bar/g' myfile
# 选项 i 使得 sed 修改文件
sed -i 's/foo/bar/g' myfile
# 批量操作当前目录下以 m 开头的文件
sed -i 's/foo/bar/g' ./m*

其他示例:

# 将myfile中的所有Windows替换成Linux
sed -i 's/Windows/Linux/g’ myfile
# 功能同上,但不对输入文件本身进行替换,仅在屏幕输出结果
sed 's/Windows/Linux/g' myfile
# 将myfile中所有连续出现的c都压缩成单个的c,仅在屏幕输出结果
sed 's/cc\*/c/g' myfile
# 删除myfile中每一行前导的连续“空白字符”(空格,制表符),仅在屏幕输出
sed 's/^[ \t]\*//' myfile
# 删除myfile中每行结尾的所有空格,仅在屏幕输出
sed 's/ \*$//' myfile
# 删除所有空白行,仅在屏幕输出
sed 's/^$//' myfile
# 在每一行开头加上一个尖括号和空格(引用信息),仅在屏幕输出
sed 's/^/> /' myfile

tar 命令详解

基本功能:打包和解包

格式:tar [选项] 文件或者目录

常用选项:

  • -c:创建新的打包文件。
  • -t:列出打包文件的内容,查看已经打包了哪些文件。
  • -x:从打包文件中释放文件。
  • -f:指定打包文件名。
  • -v:详细列出 tar 处理的文件信息。
  • -z:用 gzip 来压缩/解压缩打包文件。
  • -j:用 bzip2 来压缩/解压缩打包文件。

如:

tar -cvf myball.tar somedirname
tar -tf myball.tar
tar -xvf myball.tar
tar -zcvf myball.tar.gz somedirname
tar -ztf myball.tar.gz
tar -zxvf myball.tar.gz
tar -jcvf myball.tar.bz2 somedirname
tar -jtf myball.tar.bz2
tar -jxvf myball.tar.bz2

注意:-f 文件名|设备名 是一个整体,所以,

-cvf myball.tar 不能写成:-cfv myball.tar-fcv myball.tar

在 BASH 中提高工作效率

命令补全

通常用户在 bash 下输入命令时不必把命令输全, shell 就能判断出你所要输入的命令。

该功能的核心思想是:bash 根据用户已输入的信息来查找以这些信息开头的命令,从而试图完成当前命令的输入工作。用来执行这项功能的键是 Tab 键,按下一次 Tab 键后,bash 就试图完成整个命令的输入,如果不成功,可以再按一次 Tab 键,这时 bash 将列出所有能够与当前输入字符相匹配的命令列表。

如:

# 执行 system-config-network-tui
system <Tab> -config -n<Tab>etwork -t<Tab>ui
# 进入 /etc/sysconfig/network-scripts/ 目录
cd /e<Tab> sys<Tab> c<Tab> ne<Tab> - <Tab>
# 显示 $BASH 变量的值
echo $B<Tab>ASH

命令历史

bash 可以记录一定数目的以前在 Shell 中输入的命令。

记录历史命令的文本文件由环境变量 HISTFILE 来指定,默认的记录文件是 .bash_history,这是一个隐含文件,位于用户自己的目录中。可以记录历史命令的数目由环境变量 HISTSIZE 的值指定,默认为 1000。

查看命令历史:

history
history 30 # 查看最近 30 个历史命令
fc –l 30 50 # 列出命令历史中第30到第50之间的命令

键盘快捷键:

最简单的方法是用 ↑/↓/←/→、PgUp 和 PgDn 键来查看历史命令。

如果需要的话,可以使用键盘上的编辑功能键对显示在命令行上的命令进行编辑。

感叹号的用法:

!! 执行最近执行过的命令 用 !<命令事件号> 执行已经运行过的命令 用 !<已经使用过的命令前面的部分> 执行已经运行过的以该字符串开头的最近的命令

命令别名

允许用户按照自己喜欢的方式对命令进行自定义。

格式:alias [alias_name='original_command']

说明:

  • alias_name 是用户给命令取的别名。
  • original_command 是原来的命令和参数。若命令中包含空格或其他的特殊字符串必须使用引号。
  • 在定义别名时,等号两边不允许有空格
  • 不带任何参数的 alias 命令显示当前已定义的所有别名。
  • 可以使用 unalias alias_name 命令取消某个别名的定义。
  • 如果用户需要别名的定义在每次登录时均有效,应该将其写入用户自家目录下的 .bashrc 文件中。

如:

alias lh='ls -lh'
alias grep='grep --color=auto'
alias gitcam='git commit -a -m'

注意:

  • 若系统中有一个命令,同时又定义了一个与之同名的别名(例如,系统中有 grep 命令,且又定义了 grep 的别名),则别名将优先于系统中原有的命令的执行。
  • 要想临时使用系统中的命令而非别名,应该在命令前添加“\”字符,例如,$ \grep 命令将运行系统中原来的 grep 命令而不是 grep 别名,它不在输出中显示颜色。

信息显示命令

常用的系统信息显示命令:

命令功能
hostname显示主机名称
uname显示操作系统信息
dmesg显示系统启动信息
lsmod显示系统加载的内核模块
date显示系统时间(cal 可以显示系统时间的日历)
env显示系统环境变量
locale显示当前语言环境(cat /etc/sysconfig/i18n)
cat /etc/redhat-release显示操作系统版本(head -1 /etc/issue)
cat /proc/cpuinfo显示 CPU 信息
lspci/lsusb显示 PCI/USB 接口信息
rpm -qa显示系统已安装的所有软件包

常用的资源显示命令:

命令功能
top显示当前系统中耗费资源最多的进程
free显示当前内存的使用情况(cat /proc/meminfo)
du -h显示指定的文件(目录)已使用的磁盘空间的总量
df -h显示文件系统磁盘空间的使用情况
uptime显示系统运行时间、用户数、负载
fdisk -l查看所有分区
mount查看已经挂装的分区
swapon -s查看所有交换分区
ps -ef查看所有进程
pstree显示进程树
chkconfig --list列出所有系统服务

常用的用户相关显示命令:

命令功能
who/w显示在线登录用户
whoami显示用户自己的身份
tty显示用户当前使用的终端
id显示当前用户的 id 信息
groups显示当前用户属于哪些组
last查看用户登录日志
crontab -l查看当前用户的计划任务

常用的网络信息显示命令:

命令功能
ifconfig显示网络接口信息
route显示系统路由表
iptables -nL显示包过滤防火墙的规则设置
netstat显示网络状态信息
cat /etc/resolv.conf显示 DNS 配置
cat /etc/hosts显示静态主机解析表

进一步使用 SHELL

标准输入/输出设备

Linux 命令在执行时常常期望接收输入数据,命令执行后又期望将产生的数据结果输出。

Linux 的大部分命令都具有标准的输入/输出设备端口。

名称文件描述符含义设备说明
STDIN0标准输入键盘命令在执行时所要的输入通过它来取得
STDOUT1标准输出显示器命令执行后的输出结果从该端口送出
STDERR2标准错误显示器命令执行时的错误信息通过该端口送出

如:

# 标准输入输出
[root@soho ~]$ cat # 命令等待标准输入
hello # 标准输入的屏幕回显
hello # 标准输出
^D

# 错误输入输出
[root@soho ~]$ cat x
cat: x: 没有那个文件或目录 # 标准错误输出

重定向

重定向(Redirection),就是不使用系统的标准输入端口、标准输出端口或标准错误端口,而进行重新的指定,所以重定向分为输出重定向、输入重定向和错误重定向。通常情况下重定向到一个文件。

在 Shell 中,要实现重定向主要依靠重定向符实现,即 Shell 是检查命令行中有无重定向符来决定是否需要实施重定向。

重定向符:

重定向符说明
<输入重定向
>覆盖式的输出重定向
>>追加式的输出重定向
2>覆盖式的错误输出重定向
2>>追加式的错误输出重定向
&>同时实现输出重定向和错误重定向(覆盖式)

如:

$ ls -l /tmp >mydir
$ ls -l /etc >>mydir

$ myprogram 2> err_file
$ myprogram &> output_and_err_file

$ find ~ -name *.mp3 > ~/cd.play.list
$ echo “Please call me : 68800000>message

$ cat <<! >mytext
> This text forms the content of the heredocument ,
> which continues until the end of text delimiter
> !

输出重定向与空设备

空设备(/dev/null)是个黑洞,发往它的任何内容都将不复存在,经常用于屏蔽命令的输出或错误输出,尤其用于 Shell 脚本中。 如:

# 屏蔽命令的输出和错误输出
$ myprogram &> /dev/null
$ myprogram >/dev/null 2>&1
# 清空文件内容
$ cp /dev/null myfile
$ > myfile

管道应用

如:

$ ls -lR /etc | less
$ tail +15 myfile | head -3
$ man bash | col -b > bash.txt
# echo "p4ssW0rd" | passwd --stdin user1

$ ls -l | grep "^d"
$ cat /etc/passwd | grep username
$ dmesg | grep eth0
$ rpm –qa | grep httpd

$ echo "test email" | mail -s "test" user@example.com
$ echo "test print" | lpr

# 统计磁盘占用情况
# 统计当前目录下磁盘占用最多的10个一级子目录
$ du . --max-depth=1 | sort -rn | head -11
# 以降序方式显示使用磁盘空间最多的普通用户的前十名
$ du * -cks | sort -rn | head -11
# 以排序方式查看当前目录(不包含子目录)的磁盘占据情况
$ du -S | sort -rn | head -11

# 统计进程
# 按内存使用从大到小排列输出进程。
$ ps -e -o "%C : %p : %z : %a"|sort -k5 -nr
# 按CPU使用从大到小排列输出进程。
$ ps -e -o "%C : %p : %z : %a"|sort -nr

T 型管道

T 型管道(tee)

格式:命令1 | tee 文件名 | 命令2

功能:将命令 1 的 STDOUT 保存在文件名中,然后管道输入给命令 2

应用场景:

  • 保存不同阶段的输出
  • 复杂管道的故障排除
  • 同时查看和记录输出

命令替换

命令替换(Command Substitution)

使用命令的输出,常用于:在文本中嵌入命令的执行结果,或命令参数是另一个命令执行的结果。

使用方法: $(command)`command`

cmd1 $(cmd2) 或 cmd1 `cmd2`

如:

echo The present time is `date`
rpm -qi $(rpm -qf $(which date)) # 嵌套

命令聚合

命令行形式说明举例
CMD1 ; CMD2顺序执行若干命令pwd;date;ls
CMD1 && CMD2当 CMD1 运行成功时才运行 CMD2gzip mylargefile && echo “OK.”
CMD1 || CMD2当 CMD1 运行失败时才运行 CMD2write osmond || mail -s test osmond < my.log
(CMDLIST)在子 Shell 中执行命令序列(date; who | wc -l ) > ~/login-users.log
{CMDLIST}在当前 Shell 中执行命令序列{ cd /home/jjh; chown jjh:bin s* ;}