Linux 正则表达式
环境示例
[yuxb@controller ~ 15:21:42]$ cat words
cat
category
acat
concatenate
dog
words
文件包含一些单词,用于测试正则表达式。
普通字符匹配
最简单的匹配方式:直接找文件里有 完全相同的文本。
[yuxb@controller lab 15:22:46]$ cat words | grep 'cat'
cat
category
acat
concatenate
解释:
-
grep 'cat'
→ 查找包含cat
的行 -
输出的每行都包含
cat
,不管它前面或后面还有什么字符。
字符集匹配
方括号 [...]
方括号表示 匹配其中任意一个字符。
[yuxb@controller lab 15:22:57]$ echo cbt >> words
[yuxb@controller lab 15:23:39]$ echo c1t >> words
[yuxb@controller lab 15:23:43]$ cat words | grep 'c[ab]t'
cat
category
acat
concatenate
cbt
解释:
-
[ab]
→ 匹配 a 或 b -
'c[ab]t'
→ c + (a 或 b) + t -
只要符合规则的行都会被打印。
范围 [a-z] [A-Z] [0-9]
-
[a-z]
→ 匹配任意小写字母 -
[A-Z]
→ 匹配任意大写字母 -
[0-9]
→ 匹配任意数字
[yuxb@controller lab 15:23:48]$ cat words | grep 'c[a-z]t'
cat
category
acat
concatenate
cbt
[yuxb@controller lab 15:24:30]$ cat words | grep 'c[A-Z]t'
cCt
[yuxb@controller lab 15:24:34]$ cat words | grep 'c[0-9]t'
c1t
组合使用:
[yuxb@controller lab 15:24:38]$ cat words | grep 'c[a-z0-9]t'
cat
category
acat
concatenate
cbt
c1t
⚠️ 注意:字符集中的
-
如果要匹配普通字符,要放在最前面或最后面。
[yuxb@controller lab 15:24:49]$ echo c-t >> words
[yuxb@controller lab 15:25:10]$ cat words | grep 'c[-a-zA-Z0-9]t'
cat
category
acat
concatenate
cbt
c1t
cCt
c-t
取反 [^...]
^
放在方括号开头 → 匹配 不在里面的字符。
[yuxb@controller lab 15:25:39]$ cat words | grep 'c[^ab]t'
c1t
cCt
c-t
解释:
-
[^ab]
→ 不是 a 或 b 的任意字符 -
'c[^ab]t'
→ c + 不是 a 或 b 的字符 + t
⚠️ 注意:如果
^
放在中间,就不取反,只是普通字符。
[yuxb@controller lab 15:25:40]$ cat words | grep 'c[a^b]t'
cat
category
acat
concatenate
cbt
元字符匹配
.
任意字符
.
匹配除换行符外的 任意单个字符。
[yuxb@controller lab 15:25:52]$ cat words | grep 'c.t'
cat
category
acat
concatenate
cbt
c1t
cCt
c-t
解释:
-
'c.t'
→ c + 任意一个字符 + t -
所以 c 后面跟任意字符再跟 t 都能匹配。
转义符 \
让元字符变成普通字符,或者匹配特殊序列。
[yuxb@controller lab 15:26:50]$ echo c.t >> words
[yuxb@controller lab 15:26:50]$ cat words | grep 'c\.t'
c.t
解释:
-
.
→ 任意字符 -
\.
→ 真正的小数点 -
⚠️ 小白注意:转义符可以让符号按"原义"匹配。
或 |
(必须用 egrep
或 grep -E
)
匹配多个模式之一。
[yuxb@controller lab 15:27:02]$ cat words | egrep 'cat|dog'
cat
category
acat
concatenate
dog
解释:
-
cat|dog
→ cat 或 dog -
|
相当于"或者"
定位符
符号 | 含义 | 举例 |
---|---|---|
^ |
行首 | grep '^cat' → 行开头是 cat |
$ |
行尾 | grep 'cat$' → 行结尾是 cat |
\b |
单词边界 | grep '\bcat\b' → 精确匹配独立的 cat |
\B |
非单词边界 | grep '\Bcat' → 匹配不是独立单词的 cat |
\< |
单词左边界 | grep '\<cat' |
\> |
单词右边界 | grep 'cat\>' |
示例:
[yuxb@controller lab 15:28:52]$ echo hello cat >> words
[yuxb@controller lab 15:29:23]$ cat words | grep '\bcat\b'
cat
hello cat
解释:
-
\bcat\b
→ 找到独立的单词 cat -
不会匹配 category,因为 category 不是独立单词。
重复次数限定
* 0次或多次
[yuxb@controller lab 16:05:29]$ cat words | grep 'do*g'
dog
dg
doog
解释:
'do*g'
→ d + 任意数量的 o(包括0个) + g
+ 1次或多次
[yuxb@controller lab 16:05:32]$ cat words | egrep 'do+g'
dog
doog
- 至少有一次 o 才能匹配。
?
0次或1次
[yuxb@controller lab 16:05:46]$ cat words | egrep 'do?g'
dog
dg
- o 出现 0 次或 1 次。
{n}
精确匹配 n 次
[yuxb@controller lab 16:06:30]$ cat words | egrep 'do{2}g'
doog
- o 必须出现 2 次。
{m,n}
匹配 m~n 次
[yuxb@controller lab 16:06:43]$ cat words | egrep 'do{2,3}g'
doog
dooog
- o 出现 2 到 3 次。
{m,}
匹配至少 m 次
[yuxb@controller lab 16:06:50]$ cat words | egrep 'do{2,}g'
doog
dooog
doooog
- o 至少出现 2 次。
{,n}
匹配至多 n 次
[yuxb@controller lab 16:07:05]$ cat words | egrep 'do{,3}g'
dog
dg
doog
dooog
- o 出现 0~3 次。
()
标记子表达式
[yuxb@controller lab 16:07:42]$ echo dogdog >> words
[yuxb@controller lab 16:07:42]$ echo dogdogdog >> words
[yuxb@controller lab 16:07:42]$ echo dogdogdogdog >> words
[yuxb@controller lab 16:07:42]$ cat words | egrep '(dog){2,3}'
dogdog
dogdogdog
解释:
-
(dog){2,3}
→ 匹配 2 到 3 个连续的 dog -
记法:先看小括号里面的内容,再看
{}
决定重复次数。
grep 命令实践
grep 命令语法
[yuxb@controller ~ 18:58:23]$ grep --help
用法: grep [选项]... PATTERN [FILE]...
在每个 FILE 或是标准输入中查找 PATTERN。
默认的 PATTERN 是一个基本正则表达式(BRE)。
例如: grep -i 'hello world' menu.h main.c
PATTERN → 你要查找的文本模式 FILE → 待查找的文件,如果不写,默认从标准输入读取(可以用管道
|
)
正则表达式类型
选项 | 含义 |
---|---|
-G |
基本正则表达式 (BRE) |
-E |
扩展正则表达式 (ERE),支持 `+ ? |
-F |
固定字符串匹配,不解析正则 |
-P |
Perl 正则表达式,语法更丰富 |
匹配模式来源
选项 | 含义 |
---|---|
-e PATTERN |
直接指定模式 |
-f FILE |
从文件中读取模式,每行一个 |
忽略大小写 / 完整匹配
选项 | 含义 |
---|---|
-i |
忽略大小写 |
-w |
匹配完整单词 |
-x |
整行完全匹配 |
输出控制
选项 | 含义 |
---|---|
-v |
反转匹配,显示不匹配的行 |
-m NUM |
最多匹配 NUM 行 |
-c |
只显示匹配行数 |
-b |
显示字节偏移量 |
-n |
显示行号 |
-o |
只输出匹配的文本 |
-q |
静默模式,不输出内容,只返回退出状态 |
-s |
抑制错误信息 |
grep 命令选项实例
模式选择和解释选项
-E
或 egrep
-
作用 :支持扩展正则表达式,能用
+ ? | {}
等高级语法 -
示例:
[yuxb@controller lab 19:08:06]$ cat words | grep -E '(dog){3}'
dogdogdog
dogdogdogdog
# 或者等价写法
[yuxb@controller lab 19:08:08]$ cat words | egrep '(dog){3}'
dogdogdog
dogdogdogdog
提示:
(dog){3}
→ 连续匹配 3 次 dog
-e
-
作用 :直接指定匹配模式,可多个
-e
-
示例:
[yuxb@controller lab 19:08:59]$ cat words | grep -e 'cat' -e 'dog'
cat
category
acat
concatenate
dog
hello cat
dogdog
dogdogdog
dogdogdogdog
等价写法:
[yuxb@controller lab 19:09:00]$ cat words | egrep 'cat|dog'
cat
category
acat
concatenate
dog
hello cat
dogdog
dogdogdog
dogdogdogdog
-f
-
作用:从文件读取模式,每行一个
-
示例:
[yuxb@controller lab 19:09:39]$ echo -e 'cat\ndog' > pattens_file
[yuxb@controller lab 19:09:40]$ cat pattens_file
cat
dog
[yuxb@controller lab 19:09:47]$ cat words | grep -f pattens_file
cat
category
acat
concatenate
dog
hello cat
dogdog
dogdogdog
dogdogdogdog
提示:模式文件就像你手写的"查找清单",每行一条要查的文本。
-i
忽略大小写
[yuxb@controller lab 19:09:47]$ cat words | grep -i 'cBt'
cbt
忽略大小写后,
CBt
、cBt
、cbt
都能匹配。
-w
完整单词匹配
[yuxb@controller lab 19:11:36]$ cat words | grep -w 'cat'
cat
hello cat
不会匹配
category
,因为cat
不是独立单词 ⚠️ 等价写法:grep '\bcat\b'
-x
整行匹配
[yuxb@controller lab 19:12:11]$ cat words | grep -x 'cat'
cat
# 等价写法
[yuxb@controller lab 19:12:13]$ cat words | grep '^cat$'
cat
只有整行完全和模式相同才匹配。
输出控制选项
-v
反转匹配
[yuxb@controller lab 19:15:45]$ cat words | egrep -v '^d|^c'
acat
hello cat
输出 不以 d 或 c 开头的行 ⚠️ 常用于过滤注释或空行:
[yuxb@controller lab 19:16:08]$ egrep -v '^ *#|^$' /etc/profile
# 只显示真正配置内容,去掉注释和空行
-m NUM
匹配次数限制
[yuxb@controller lab 19:17:01]$ cat words | grep -m2 'dog'
dog
dogdog
匹配到前 2 个就停止搜索,加快速度。
-c
显示行数
[yuxb@controller lab 19:17:21]$ cat words | grep -c 'dog'
4
只显示匹配行的数量,不显示内容。
-b
显示字节偏移
[yuxb@controller lab 19:17:42]$ cat words | grep -b 'cat'
0:cat
4:category
13:acat
18:concatenate
54:hello cat
左边数字是 匹配行第一个字符在文件中的字节位置,方便定位。
-n
显示行号
[yuxb@controller lab 19:17:43]$ cat words | grep -n 'cat'
1:cat
2:category
3:acat
4:concatenate
11:hello cat
左边数字是 行号,方便查找。
-o
只输出匹配内容
[yuxb@controller lab 19:18:36]$ cat words | egrep -o '(dog){3}'
dogdogdog
dogdogdog
每个匹配结果单独一行。
-q
静默模式
[yuxb@controller lab 19:21:16]$ cat words | egrep -q '(dog){3}'
[yuxb@controller lab 19:21:17]$ echo $?
0
[yuxb@controller lab 19:21:17]$ cat words | egrep -q '(dog){3}asdfasfdasf'
[yuxb@controller lab 19:21:17]$ echo $?
1
找到匹配返回
0
,没找到返回1
,不输出任何内容,适合脚本判断。
-s
抑制错误
[yuxb@controller lab 19:21:56]$ grep '^SELINUX=' /etc/shadow /etc/selinux/config
grep: /etc/shadow: 权限不够
/etc/selinux/config:SELINUX=disabled
[yuxb@controller lab 19:21:57]$ grep -s '^SELINUX=' /etc/shadow /etc/selinux/config
/etc/selinux/config:SELINUX=disabled
不想看到权限不足或文件不存在的报错,可以加
-s
。
set
sed 行寻址
sed
是一个流编辑器,支持通过 行号、范围、模式匹配 来选择要处理的行,这就是所谓的 行寻址。
sed 命令语法
基本格式
sed [选项] '地址 指令' 文件
-
地址:行寻址,用来指定作用的行(可以是行号、范围、正则匹配)
-
指令 :
sed
的操作命令(如p
、d
、s
) -
文件:要处理的文本文件,如果省略则默认从标准输入读取
sed 帮助
[yuxb@controller web 09:37:42]$ sed --help
用法: sed [选项]... {脚本(如果没有其他脚本)} [输入文件]...
-n, --quiet, --silent
取消自动打印模式空间
-e 脚本, --expression=脚本
添加“脚本”到程序的运行列表
-f 脚本文件, --file=脚本文件
添加“脚本文件”到程序的运行列表
--follow-symlinks
直接修改文件时跟随软链接
-i[SUFFIX], --in-place[=SUFFIX]
edit files in place (makes backup if SUFFIX supplied)
-c, --copy
use copy instead of rename when shuffling files in -i mode
-b, --binary
does nothing; for compatibility with WIN32/CYGWIN/MSDOS/EMX (
open files in binary mode (CR+LFs are not treated specially))
-l N, --line-length=N
指定“l”命令的换行期望长度
--posix
关闭所有 GNU 扩展
-r, --regexp-extended
在脚本中使用扩展正则表达式
-s, --separate
将输入文件视为各个独立的文件而不是一个长的连续输入
-u, --unbuffered
从输入文件读取最少的数据,更频繁的刷新输出
-z, --null-data
separate lines by NUL characters
--help
display this help and exit
--version
output version information and exit
如果没有 -e, --expression, -f 或 --file 选项,那么第一个非选项参数被视为
sed脚本。其他非选项参数被视为输入文件,如果没有输入文件,那么程序将从标准
输入读取数据。
GNU sed home page: <http://www.gnu.org/software/sed/>.
General help using GNU software: <http://www.gnu.org/gethelp/>.
E-mail bug reports to: <bug-sed@gnu.org>.
Be sure to include the word ``sed'' somewhere in the ``Subject:'' field.
示例1:模拟cat命令打印文件内容
[yuxb@controller lab 10:21:38]$ cat data.txt
I am studing sed
I am www.twle.cn
I am a no-work-men
I am so handsome
[yuxb@controller lab 10:22:07]$ sed '' data.txt
I am studing sed
I am www.twle.cn
I am a no-work-men
I am so handsome
示例2:从标准输入中读取数据
[yuxb@controller lab 10:22:12]$ sed ''
# 输入hello world,并回车
hello world
# 输出hello world
hello world
# 按ctrl+d退出
常用选项
选项 | 说明 |
---|---|
-n |
安静模式(默认 sed 会输出所有行,加 -n 后只输出匹配或指定操作的结果) |
-e |
多重编辑,允许指定多个编辑命令 |
-f |
从脚本文件读取 sed 命令 |
-i |
直接修改原文件(慎用,会覆盖原文件) |
-e ------ 多重编辑(expression)
-
允许在一条命令中指定多个编辑操作。
-
不加
-e
也能用,但多个命令必须分开写;有了-e
更清晰。
# 打印data.txt文件内容
[yuxb@controller lab 10:23:33]$ sed -e '' data.txt
I am studing sed
I am www.twle.cn
I am a no-work-men
I am so handsome
# 如果只有一个命令,-e选项可以省略
[yuxb@controller lab 10:24:07]$ sed '' data.txt
I am studing sed
I am www.twle.cn
I am a no-work-men
I am so handsome
# -e 选项可以多次使用,1d是作用是删除第一行
# 没有第五行,就没有删除效果
[yuxb@controller lab 10:24:08]$ sed -e '1d' -e '2d' -e '5d' data.txt
I am a no-work-men
I am so handsome
# 使用分号(;)分开多个命令
[yuxb@controller lab 10:24:27]$ sed -e '1d;2d;5d' data.txt
I am a no-work-men
I am so handsome
-f------ 从脚本文件读取命令(file)
- 把
sed
命令写在脚本文件里,方便复用。
[yuxb@controller lab 10:25:34]$ echo -e "1d\n2d\n5d" > scripts
[yuxb@controller lab 10:25:35]$ cat scripts
1d
2d
5d
[yuxb@controller lab 10:25:40]$ sed -f scripts data.txt
I am a no-work-men
I am so handsome
-n------安静模式(silent)
- 默认情况下,
sed
会把 所有行 输出到标准输出。
- 加上
-n
之后,只会输出符合条件的内容。
- 通常和
p
(print)命令一起用。
# 没有输出
[yuxb@controller lab 10:26:12]$ sed -n '' data.txt
# 打印第一行记录
[yuxb@controller lab 10:26:13]$ sed -n '1p' data.txt
I am studing sed
总结对比
选项 | 全称 | 作用 | 常见用途 |
---|---|---|---|
-n |
--quiet / --silent | 关闭默认输出,只显示需要的结果 | sed -n '1,3p' file |
-e |
--expression | 指定多个命令(行内) | sed -e '2d' -e 's/x/y/' file |
-f |
--file | 从外部文件读取命令 | sed -f cmds.sed file |
sed 行寻址
基本语法
sed '[地址] 命令' 文件
-
地址(line address) :用来指定
sed
命令作用的行,可以是行号、范围、正则匹配等。 -
命令 :如
p
、d
、s
等。
示例
[yuxb@controller lab 10:27:45]$ echo 'This is 1
> This is 2
> This is 3
> This is 4
> This is 5 ' > test
示例1: 打印所有行
[yuxb@controller lab 10:27:48]$ cat test | sed ''
This is 1
This is 2
This is 3
This is 4
This is 5
[yuxb@controller lab 10:29:29]$ cat test | sed -n 'p'
This is 1
This is 2
This is 3
This is 4
This is 5
# -n 关闭sed打印模式缓冲区中所有内容。
# p命令,明确打印输出模式缓冲区中所有内容。
示例2: 打印特定行
[yuxb@controller lab 10:29:53]$ cat test | sed -n '1p'
This is 1
[yuxb@controller lab 10:30:40]$ cat test | sed -n '$p'
This is 5
示例3: 打印第1行到3行
[yuxb@controller lab 10:30:47]$ cat test | sed -n '1,3p'
This is 1
This is 2
This is 3
示例4: 打印第3行到最后一行
[yuxb@controller lab 10:32:18]$ cat test | sed -n '3,$p'
This is 3
This is 4
This is 5
示例5: 连续输出,打印第2行以及后续两行
[yuxb@controller lab 10:32:40]$ cat test | sed -n '2,+2p'
This is 2
This is 3
This is 4
示例6: 隔行输出,打印第1行以及后续隔2行输出
[yuxb@controller lab 10:32:55]$ cat test | sed -n '1~2p'
This is 1
This is 3
This is 5
sed 模式寻址
基本语法
sed '/模式/ 命令' 文件
-
/模式/
:正则表达式,用来匹配行 -
命令
:对匹配到的行执行操作,如p
、d
、s
等
准备文件
[yuxb@controller lab 10:35:56]$ cat << 'EOF' > ~/test
> root:x:0:0:root:/root:/bin/bash
> bin:x:1:1:bin:/bin:/bin/false
> daemon:x:2:2:daemon:/sbin:/bin/false
> mail:x:8:12:mail:/var/spool/mail:/bin/false
> ftp:x:14:11:ftp:/home/ftp:/bin/false
> &nobody:$:99:99:nobody:/:/bin/false
> zhangy:x:1000:100:,,,:/home/zhangy:/bin/bash
> http:x:33:33::/srv/http:/bin/false
> dbus:x:81:81:System message bus:/:/bin/false
> hal:x:82:82:HAL daemon:/:/bin/false
> mysql:x:89:89::/var/lib/mysql:/bin/false
> aaa:x:1001:1001::/home/aaa:/bin/bash
> ba:x:1002:1002::/home/zhangy:/bin/bash
> test:x:1003:1003::/home/test:/bin/bash
> @zhangying:*:1004:1004::/home/test:/bin/bash
> policykit:x:102:1005:Po
> EOF
示例1: 打印含有字符串zhang的行
[yuxb@controller ~ 10:36:36]$ cat test | sed -n '/zhang/p'
zhangy:x:1000:100:,,,:/home/zhangy:/bin/bash
ba:x:1002:1002::/home/zhangy:/bin/bash
@zhangying:*:1004:1004::/home/test:/bin/bash
# -n
# 作用:关闭 sed 的默认输出。
# 默认情况下,sed 会把每一行都输出一次,加 -n 后 只输出显式指定打印的行。
# /zhang/
# 作用:模式匹配,匹配所有 包含 zhang 的行。
# /模式/ 的形式就是 模式寻址。
# p
# 作用:打印匹配到的行。
# 因为我们加了 -n,所以只有匹配的行会被打印出来。
示例2: 打印root开头的行到zhang开头的行
[yuxb@controller ~ 10:36:41]$ cat test | sed -n '/^root/,/^mail/p'
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/bin/false
daemon:x:2:2:daemon:/sbin:/bin/false
mail:x:8:12:mail:/var/spool/mail:/bin/false
# /^mail/
# 作用:匹配 以 mail 开头的行。
# 这是 结束行模式。
示例3: 打印root开头的行到第三行
[yuxb@controller ~ 10:36:54]$ cat test | sed -n '/^root/,3p'
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/bin/false
daemon:x:2:2:daemon:/sbin:/bin/false
# /^root/
# 作用:匹配所有 以 root 开头的行。
# ^ 表示行首。
# 这是 模式寻址 的起始位置。
# ,3
# 作用:范围结束在 第 3 行。
# 表示从匹配到 /^root/ 的行开始,到 第 3 行 结束。
示例4: 打印root开头的行到最后一行
[yuxb@controller ~ 10:37:08]$ cat test | sed -n '/^root/,$p'
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/bin/false
daemon:x:2:2:daemon:/sbin:/bin/false
mail:x:8:12:mail:/var/spool/mail:/bin/false
ftp:x:14:11:ftp:/home/ftp:/bin/false
&nobody:$:99:99:nobody:/:/bin/false
zhangy:x:1000:100:,,,:/home/zhangy:/bin/bash
http:x:33:33::/srv/http:/bin/false
dbus:x:81:81:System message bus:/:/bin/false
hal:x:82:82:HAL daemon:/:/bin/false
mysql:x:89:89::/var/lib/mysql:/bin/false
aaa:x:1001:1001::/home/aaa:/bin/bash
ba:x:1002:1002::/home/zhangy:/bin/bash
test:x:1003:1003::/home/test:/bin/bash
@zhangying:*:1004:1004::/home/test:/bin/bash
policykit:x:102:1005:Po
# ,$
# 作用:指定范围的结束行为 最后一行。
# $ 是 sed 中的特殊符号,表示 文件的最后一行。
sed 子命令
打印
作用
-
p,打印模式空间所有记录。
-
P,打印模式空间第一行记录。
语法
sed -n '地址p' 文件
sed -n '地址P' 文件
地址
:行寻址(可以是行号、范围、模式),用于指定要打印的行。
p
:打印匹配的整行。
P
:打印匹配的第一行内容(遇到换行符就停)。
示例
[yuxb@controller ~ 10:37:28]$ echo 'This is 1
> This is 2
> This is 3' | sed -n '1{N;p}'
This is 1
This is 2
# 1{...} → 只对第 1 行执行大括号里的命令。
# N → 把 下一行(第 2 行)也读进模式空间,此时模式空间包含:
# This is 1\nThis is 2
# p → 打印整个模式空间。
[yuxb@controller ~ 10:38:58]$ echo 'This is 1
> This is 2
> This is 3' | sed -n '1{N;P}'
This is 1
# N → 把第 1 行和第 2 行一起放到模式空间:This is 1\nThis is 2
# P → 只打印 模式空间的第一行(直到第一个换行符为止)。
# 可以记成:
# p = print all
# P = print first
读取下一行
[yuxb@controller ~ 10:39:00]$ echo 'This is 1
> This is 2
> This is 3
> This is 4
> This is 5' | sed -n 'n;p'
This is 2
This is 4
# n
# 读入下一行到模式空间,并覆盖当前内容。
# 默认会打印当前模式空间的内容(但因为用了 -n,默认输出被抑制了)。
# p
# 打印模式空间的内容。
# 逐步执行:
# 读入第 1 行 "This is 1" → 模式空间
# 执行 n → 丢弃第 1 行,读入第 2 行 "This is 2"
# 执行 p → 打印 "This is 2"
# 读入第 3 行 "This is 3" → 模式空间
# 执行 n → 丢弃第 3 行,读入第 4 行 "This is 4"
# 执行 p → 打印 "This is 4"
# 读入第 5 行 "This is 5" → 模式空间
# 执行 n → 试图读下一行,但没有了 → 结束,不打印
# sed -n 'n;p' 的逻辑是:
# 丢掉奇数行,只打印偶数行。
示例1: 成对合并行
[yuxb@controller ~ 10:43:35]$ cat test | sed 'N;s/\n/==/'
root:x:0:0:root:/root:/bin/bash==bin:x:1:1:bin:/bin:/bin/false
daemon:x:2:2:daemon:/sbin:/bin/false==mail:x:8:12:mail:/var/spool/mail:/bin/false
ftp:x:14:11:ftp:/home/ftp:/bin/false==&nobody:$:99:99:nobody:/:/bin/false
zhangy:x:1000:100:,,,:/home/zhangy:/bin/bash==http:x:33:33::/srv/http:/bin/false
dbus:x:81:81:System message bus:/:/bin/false==hal:x:82:82:HAL daemon:/:/bin/false
mysql:x:89:89::/var/lib/mysql:/bin/false==aaa:x:1001:1001::/home/aaa:/bin/bash
ba:x:1002:1002::/home/zhangy:/bin/bash==test:x:1003:1003::/home/test:/bin/bash
@zhangying:*:1004:1004::/home/test:/bin/bash==policykit:x:102:1005:Po
# N:把下一行读入模式空间,并在当前行后面加上一个换行符 \n。
# s/\n/==/:把换行符替换成 ==。
示例2: 打印前2行
[yuxb@controller ~ 10:44:15]$ echo 'This is 1
> This is 2
> This is 3
> This is 4
> This is 5' | sed -n '1{N;p}'
This is 1
This is 2
替换
**示例1:**把test文件中的root替换成tankzhang,只不过只替换一次即终止在这一行的操作,并转到下一行
[yuxb@controller ~ 10:45:06]$ sed 's/root/tankzhang/' test|grep tankzhang
tankzhang:x:0:0:root:/root:/bin/bash
# s:substitute(替换)
# root:匹配的模式
# tankzhang:替换后的字符串
# 没有加 g 标志 → 默认只替换 当前行的第一个匹配
示例2: 把test文件中的root全部替换成tankzhang。字母g是global的缩写。
[yuxb@controller ~ 10:45:07]$ sed 's/root/tankzhang/g' test |grep tankzhang
tankzhang:x:0:0:tankzhang:/tankzhang:/bin/bash
示例3: 加了-n
和p
后表示只打印那些发生替换的行(部分替换),下面的例子,不需要使用grep命令
[yuxb@controller ~ 10:45:23]$ sed -n 's/root/tankzhang/p' test
tankzhang:x:0:0:root:/root:/bin/bash
# -n:禁止默认输出(静默模式)。
# s/root/tankzhang/:替换命令,把 root 替换为 tankzhang(每行只替换第一个)。
# p:打印发生替换的行。
示例4: 加了-n
和pg
后表示只打印那些发生替换的行(全部替换)
[yuxb@controller ~ 10:47:10]$ sed -n 's/root/tankzhang/gp' test
tankzhang:x:0:0:tankzhang:/tankzhang:/bin/bash
**示例5:**在第二行到第八行之间,替换以zhang开头的行,用ying来替换,并显示替换的行
[yuxb@controller ~ 10:47:31]$ sed -ne '2,8s/^zhang/ying/gp' test
yingy:x:1000:100:,,,:/home/zhangy:/bin/bash
# -n:静默模式,不输出未被指定的行。
# 2,8:只对 第 2 行到第 8 行 之间的内容生效。
# s/^zhang/ying/g:把行首的 zhang 替换为 ying。
# ^zhang 表示以 zhang 开头。
# g 表示全局替换(虽然这里一行只有一个匹配点)。
# p:只打印替换成功的行。
示例6: 从以zhang开头的行开始,到匹配Po的行结束,在他们之间进行替换
[yuxb@controller ~ 10:47:32]$ sed -ne '/^zhang/,/Po/ s/zhang/ying/gp' test
yingy:x:1000:100:,,,:/home/yingy:/bin/bash
ba:x:1002:1002::/home/yingy:/bin/bash
@yingying:*:1004:1004::/home/test:/bin/bash
# -n
# 不自动打印,避免多余输出,只显示你显式指定的结果。
# -e 'script'
# 明确告诉 sed 执行的脚本(这里只有一个,可以省略 -e)。
# /^zhang/,/Po/
# 这是一个 区间寻址,起点是匹配 ^zhang 的行(以 zhang 开头的行),
# 终点是匹配 Po 的行(包含 Po 的行)。
# 这两者之间的所有行(包括起止行),都会执行后面跟的命令。
# s/zhang/ying/gp
# s/zhang/ying/ → 替换第一个匹配的 zhang 为 ying。
# g → 全局替换该行中所有 zhang。
# p → 打印发生替换的行。
替换中的分隔符可以自定义,默认是/。
示例7: 自定义替换分隔符为 #
。
sed
允许你自定义分隔符,比如 #
、@
、|
等。
[yuxb@controller ~ 10:47:49]$ sed -n 's#root#hello#gp' test
hello:x:0:0:hello:/hello:/bin/bash
分隔符;和-e选项
需要执行多个sed处理命令 时,用分号分开 ,或者使用 -e
选项。
示例:
-
在第2行到第8行之间,替换以zhang开头的行,用ying来替换
-
在第5行到第10行之间,用goodbay来替换dbus,并显示替换的行
[yuxb@controller ~ 10:48:57]$ cat test | sed -n
[yuxb@controller ~ 10:48:39]$ cat test | sed -ne '2,8s/zhang/ying/gp' -ne '5,10s#dbus#goodbay#gp'
yingy:x:1000:100:,,,:/home/yingy:/bin/bash
goodbay:x:81:81:System message bus:/:/bin/false
使用场景对比
写法 | 特点 | 适用场景 |
---|---|---|
; 分隔命令 |
简洁,但命令太多时可读性差 | 短小的一次性脚本 |
-e 多条命令 |
清晰,适合复杂脚本或多命令 | 推荐长期使用 |
插入
命令 | 作用 |
---|---|
a |
在匹配行 后 插入 |
i |
在匹配行 前 插入 |
c |
整行替换 |
a 在匹配行下面插入新行
将要插入的东西,插入到匹配行的下面
[yuxb@controller ~ 10:49:09]$ sed '/root/a====aaaa====' test
root:x:0:0:root:/root:/bin/bash
====aaaa====
bin:x:1:1:bin:/bin:/bin/false
daemon:x:2:2:daemon:/sbin:/bin/false
mail:x:8:12:mail:/var/spool/mail:/bin/false
ftp:x:14:11:ftp:/home/ftp:/bin/false
&nobody:$:99:99:nobody:/:/bin/false
zhangy:x:1000:100:,,,:/home/zhangy:/bin/bash
http:x:33:33::/srv/http:/bin/false
dbus:x:81:81:System message bus:/:/bin/false
hal:x:82:82:HAL daemon:/:/bin/false
mysql:x:89:89::/var/lib/mysql:/bin/false
aaa:x:1001:1001::/home/aaa:/bin/bash
ba:x:1002:1002::/home/zhangy:/bin/bash
test:x:1003:1003::/home/test:/bin/bash
@zhangying:*:1004:1004::/home/test:/bin/bash
policykit:x:102:1005:Po
i 在匹配行上面插入新行
将要插入的东西,插入到匹配行的上面
[yuxb@controller ~ 10:50:19]$ sed '/root/i====iiii====' test
====iiii====
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/bin/false
daemon:x:2:2:daemon:/sbin:/bin/false
mail:x:8:12:mail:/var/spool/mail:/bin/false
ftp:x:14:11:ftp:/home/ftp:/bin/false
&nobody:$:99:99:nobody:/:/bin/false
zhangy:x:1000:100:,,,:/home/zhangy:/bin/bash
http:x:33:33::/srv/http:/bin/false
dbus:x:81:81:System message bus:/:/bin/false
hal:x:82:82:HAL daemon:/:/bin/false
mysql:x:89:89::/var/lib/mysql:/bin/false
aaa:x:1001:1001::/home/aaa:/bin/bash
ba:x:1002:1002::/home/zhangy:/bin/bash
test:x:1003:1003::/home/test:/bin/bash
@zhangying:*:1004:1004::/home/test:/bin/bash
policykit:x:102:1005:Po
删除
作用
-
d,删除模式空间所有记录。
-
D,删除模式空间第一行记录。
语法
sed '地址范围d' 文件
sed '地址范围D' 文件
d 删除 示例
示例1: 删除1,14行
[yuxb@controller ~ 10:51:19]$ sed -e '1,14d' test
@zhangying:*:1004:1004::/home/test:/bin/bash
policykit:x:102:1005:Po
示例2: 删除4以后的行,包括第4行,把$当成最大行数就行了。
[yuxb@controller ~ 10:51:20]$ sed -e '4,$d' test
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/bin/false
daemon:x:2:2:daemon:/sbin:/bin/false
示例3: 删除包括false的行,或者包括bash的行,别忘了加\
[yuxb@controller ~ 10:52:33]$ sed -e '/\(false\|bash\)/d' test
policykit:x:102:1005:Po
示例4: 删除从匹配root的行,到匹配以test开头的行,中间的行
[yuxb@controller ~ 10:52:33]$ sed -e '/root/,/^test/d' test
@zhangying:*:1004:1004::/home/test:/bin/bash
policykit:x:102:1005:Po
D 删除 示例
删除当前模式空间开端至\n的内容,放弃之后的命令,对剩余模式空间继续执行sed。
示例1:读取最后一行内容
[yuxb@controller ~ 10:53:47]$ echo 'This is 1
> This is 2
> This is 3
> This is 4
> This is 5' | sed 'N;D'
This is 5
示例2:删除偶数行
[yuxb@controller ~ 10:53:49]$ echo 'This is 1
> This is 2
> This is 3
> This is 4
> This is 5' | sed 'n;D'
This is 1
This is 3
This is 5
打印行号
=
打印当前处理的 行号。
语法
sed '=' 文件名
示例1:行号与行内容交替显示
[yuxb@controller ~ 10:54:10]$ sed '=' test
1
root:x:0:0:root:/root:/bin/bash
2
bin:x:1:1:bin:/bin:/bin/false
3
daemon:x:2:2:daemon:/sbin:/bin/false
4
mail:x:8:12:mail:/var/spool/mail:/bin/false
5
ftp:x:14:11:ftp:/home/ftp:/bin/false
6
&nobody:$:99:99:nobody:/:/bin/false
7
zhangy:x:1000:100:,,,:/home/zhangy:/bin/bash
8
http:x:33:33::/srv/http:/bin/false
9
dbus:x:81:81:System message bus:/:/bin/false
10
hal:x:82:82:HAL daemon:/:/bin/false
11
mysql:x:89:89::/var/lib/mysql:/bin/false
12
aaa:x:1001:1001::/home/aaa:/bin/bash
13
ba:x:1002:1002::/home/zhangy:/bin/bash
14
test:x:1003:1003::/home/test:/bin/bash
15
@zhangying:*:1004:1004::/home/test:/bin/bash
16
policykit:x:102:1005:Po
示例2:行号与行内容并排显示
# 利用 sed 'N;s/\n/:/' 把行号和内容合并到同一行
[yuxb@controller ~ 10:54:52]$ sed '=' test| sed 'N;s/\n/:/'
1:root:x:0:0:root:/root:/bin/bash
2:bin:x:1:1:bin:/bin:/bin/false
3:daemon:x:2:2:daemon:/sbin:/bin/false
4:mail:x:8:12:mail:/var/spool/mail:/bin/false
5:ftp:x:14:11:ftp:/home/ftp:/bin/false
6:&nobody:$:99:99:nobody:/:/bin/false
7:zhangy:x:1000:100:,,,:/home/zhangy:/bin/bash
8:http:x:33:33::/srv/http:/bin/false
9:dbus:x:81:81:System message bus:/:/bin/false
10:hal:x:82:82:HAL daemon:/:/bin/false
11:mysql:x:89:89::/var/lib/mysql:/bin/false
12:aaa:x:1001:1001::/home/aaa:/bin/bash
13:ba:x:1002:1002::/home/zhangy:/bin/bash
14:test:x:1003:1003::/home/test:/bin/bash
15:@zhangying:*:1004:1004::/home/test:/bin/bash
16:policykit:x:102:1005:Po
写入
w 写入
将模式空间中记录写入到文件中。
示例1: 将root开头的行,写入test3中
[yuxb@controller ~ 10:55:51]$ sed -n '/^root/w test3' test
[yuxb@controller ~ 10:55:51]$ cat test3
root:x:0:0:root:/root:/bin/bash
# -n
# 表示 不自动打印模式空间的内容,避免把所有行都输出到终端。
# /^root/
# 表示 匹配以 root 开头的行。
# w test3
# 把模式空间中 匹配到的行写入文件 test3。
W 写入
将模式空间中第一条记录写入到文件中。
示例: 写入记录
[yuxb@controller ~ 10:55:56]$ vim scripts
[yuxb@controller ~ 10:56:38]$ cat scripts
1{
N
w write.log # w write.log → 把整个模式空间写入 write.log
}
# 小写w写入包含模式中所有行
[yuxb@controller ~ 10:57:04]$ cat write.log
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/bin/false
# 大写W写入只包含模式中第一行
[yuxb@controller ~ 10:57:05]$ vim scripts
[yuxb@controller ~ 10:58:01]$ cat scripts
1{
N
W write.log
}
[yuxb@controller ~ 10:58:11]$ sed -n -f scripts test
[yuxb@controller ~ 10:58:20]$ cat write.log
root:x:0:0:root:/root:/bin/bash
更改
整行替换。
作用
-
用新的内容替换匹配的整行。
-
在
sed
中有两种常用方式:
示例:root开头行替换出hello
# chello → 将匹配行整行替换为 hello
[yuxb@controller ~ 10:58:49]$ sed '/^root/chello' test
hello
bin:x:1:1:bin:/bin:/bin/false
daemon:x:2:2:daemon:/sbin:/bin/false
mail:x:8:12:mail:/var/spool/mail:/bin/false
ftp:x:14:11:ftp:/home/ftp:/bin/false
&nobody:$:99:99:nobody:/:/bin/false
zhangy:x:1000:100:,,,:/home/zhangy:/bin/bash
http:x:33:33::/srv/http:/bin/false
dbus:x:81:81:System message bus:/:/bin/false
hal:x:82:82:HAL daemon:/:/bin/false
mysql:x:89:89::/var/lib/mysql:/bin/false
aaa:x:1001:1001::/home/aaa:/bin/bash
ba:x:1002:1002::/home/zhangy:/bin/bash
test:x:1003:1003::/home/test:/bin/bash
@zhangying:*:1004:1004::/home/test:/bin/bash
policykit:x:102:1005:Po
# 等效下面命令
[yuxb@controller ~ 10:58:49]$ sed 's/^root.*/hello/' test
awk
awk
是一个功能强大的文本处理工具,主要用于按 字段 和 模式 对文本进行扫描、分析和处理。
awk 命令
基本语法
awk '模式 {动作}' 文件
-
模式(pattern):用来匹配行,可以是正则表达式,也可以是逻辑判断。
-
动作(action) :对匹配行执行的操作,用
{}
包裹。
如果省略模式,则作用于所有行。 如果省略动作,默认打印匹配行。
常用选项
选项 | 作用 |
---|---|
-F 分隔符 |
指定输入字段分隔符,默认空格或制表符 |
-v var=value |
定义 awk 变量 |
-f 脚本文件 |
从文件中读取 awk 脚本 |
awk 示例
示例文件
[yuxb@controller ~ 11:22:24]$ cat << 'EOF' > employee.txt
> 1) 张三 技术部 23
> 2) 李四 人力部 22
> 3) 王五 行政部 23
> 4) 赵六 技术部 24
> 5) 朱七 客服部 23
> EOF
示例1: 打印雇员信息。
# { print } → 打印模式空间的整行内容(等同于 print $0)。
[yuxb@controller ~ 11:22:52]$ awk '{ print }' employee.txt
1) 张三 技术部 23
2) 李四 人力部 22
3) 王五 行政部 23
4) 赵六 技术部 24
5) 朱七 客服部 23
示例2: 通过读取awk脚本,打印雇员信息。
[yuxb@controller ~ 11:25:31]$ cat commands.awk
{ print }
# -f commands.awk → 指定 awk 脚本文件执行。
[yuxb@controller ~ 11:25:34]$ awk -f commands.awk employee.txt
1) 张三 技术部 23
2) 李四 人力部 22
3) 王五 行政部 23
4) 赵六 技术部 24
5) 朱七 客服部 23
示例3: 输出特定隔行。
# /pattern/ 是 awk 最常用的 模式匹配方式
[yuxb@controller ~ 11:25:43]$ awk '/张三/ { print }' employee.txt
1) 张三 技术部 23
[yuxb@controller ~ 11:25:59]$ awk '/张三/' employee.txt
1) 张三 技术部 23
示例4: 统计满足特定条件的记录数。
[yuxb@controller ~ 11:26:05]$ awk '
> /术/ { count=count+1 }
> END { print "Count="count }' employee.txt
Count=2
# /术/ → 匹配包含“术”的行(如“技术部”)。
# { count = count + 1 } → 每匹配一行,计数器 count 加 1。
# END { print "Count=" count } → 在处理完所有行后打印结果。
示例5: 输出总长度大于 10 的行。
[yuxb@controller ~ 11:26:38]$ awk 'length($0)>10 { print $0 }' employee.txt
1) 张三 技术部 23
2) 李四 人力部 22
3) 王五 行政部 23
4) 赵六 技术部 24
5) 朱七 客服部 23
# length($0) → 获取当前行的长度(字符数)。
# length($0) > 10 → 条件判断,总长度大于 10 的行才执行 { print $0 }。
# $0 → 当前整行内容。
Shell 变量
Shell 变量用于在脚本中存储数据,可以是数字、字符串或命令输出。
Shell 中特殊变量
位置参数
变量 | 说明 |
---|---|
$0 |
脚本名称 |
$1 ~$9 |
第1~第9个参数 |
$# |
参数个数 |
$@ |
所有参数,逐个引用 |
$* |
所有参数,作为一个整体 |
$$ |
当前脚本的 PID |
$! |
上一个后台运行命令的 PID |
$? |
上一个命令的退出状态(0 成功,非0失败) |
Shell 位置参数变量
示例1:showargs.sh 内容如下
#!/bin/bash
echo $0
echo $1
echo $2
echo $10
echo ${10}
echo $#
echo $*
echo $@
echo "$@"
[yuxb@controller bin 15:01:59]$ bash showargs.sh {a..z}
showargs.sh
a
b
a0
j
26
a b c d e f g h i j k l m n o p q r s t u v w x y z
a b c d e f g h i j k l m n o p q r s t u v w x y z
a b c d e f g h i j k l m n o p q r s t u v w x y z
示例2:ssh_ctl 内容如下
#!/bin/bash
systemctl $1 sshd
[yuxb@controller bin 15:04:32]$ sudo ~/bin/ssh_ctl stop
[yuxb@controller bin 15:05:53]$ sudo ~/bin/ssh_ctl status
[yuxb@controller bin 15:05:59]$ sudo ~/bin/ssh_ctl start
Shell 进程中的特殊状态变量
$?
作用:获取执行上一个指令的执行状态返回值:0为成功,非0为失败,这个变量最常用。
只能获取 上一个命令 的状态,执行新命令后会被覆盖。
在脚本中,可以用它实现 错误处理和异常退出。
[yuxb@controller bin 14:57:08]$ echo $?
1
[yuxb@controller bin 14:57:20]$ ls /root
ls: 无法打开目录/root: 权限不够
[yuxb@controller bin 14:57:32]$ echo $?
2
[yuxb@controller bin 14:57:34]$
$$
作用:获取当前执行的 Shell 脚本的进程号(PID),这个变量不常用,了解即可。
[yuxb@controller bin 14:58:30]$ echo $$
1553
$!
作用:获取上一个在后台工作的进程的进程号(PID),这个变量不常用,了解即可。
[yuxb@controller bin 14:59:11]$ md5sum /dev/zero &
[1] 2696
[yuxb@controller bin 14:59:14]$ echo $!
2696
[yuxb@controller bin 14:59:23]$ ps o pid,%cpu,%mem,command $!
PID %CPU %MEM COMMAND
2696 100 0.0 md5sum /dev/zero
[yuxb@controller bin 14:59:29]$ kill $!
[1]+ 已终止 md5sum /dev/zero
$_
作用:获取在此之前执行的命令或脚本的最后一个参数,这个变量不常用,了解即可。
[yuxb@controller bin 14:59:36]$ ls /etc/hosts /etc/fstab /etc/hostname
/etc/fstab /etc/hostname /etc/hosts
[yuxb@controller bin 15:00:03]$ cat $_
controller.yuxb.cloud
[yuxb@controller bin 15:00:07]$ cat /etc/hostname
controller.yuxb.cloud
Shell 内置变量命令
echo
echo命令参数选项:
-
-n,不换行输出内容。
-
-e,解析转义字符(见下面的字符)
转义字符:
-
\n,换行。
-
\t,制表符(tab)。
-
\b,退格。
[yuxb@controller bin 15:06:03]$ echo -n "laowang";echo laoli
laowanglaoli
[yuxb@controller bin 15:07:24]$ echo -e "laowang\nlaoli"
laowang
laoli
[yuxb@controller bin 15:08:21]$ echo -e "laowang\tlaoli"
laowang laoli
[yuxb@controller bin 15:08:42]$ echo -e "1\b23"
23
[yuxb@controller bin 15:09:04]$ echo -e "123\b"
123
[yuxb@controller bin 15:09:08]$ echo -ne "123\b";echo haha
12haha
read
[yuxb@controller bin 15:16:59]$ vim read.sh
[yuxb@controller bin 15:17:13]$ cat read.sh
#!/bin/sh
read -p "输入你想要说的话:" str
echo "你想要说的话是:$str"
[yuxb@controller bin 15:17:25]$ bash read.sh
输入你想要说的话:666
你想要说的话是:666
[yuxb@controller bin 15:17:29]$ read -s -p "请设置用户密码: " password
请设置用户密码: [yuxb@controller bin 15:17:48]$ echo $password
123
Shell 变量子串知识
Shell 变量子串介绍
在 Shell 脚本中,可以对变量内容进行 截取、替换、删除等操作,方便处理字符串数据。
取子串(Substring)
${变量名:起始位置:长度}
-
起始位置 从
0
开始计数 -
长度可选,如果省略则取到字符串末尾
Shell 变量子串的实践
示例1: ${parameter}
[yuxb@controller bin 15:20:43]$ str="abc123abc123"
[yuxb@controller bin 15:29:52]$ echo ${str}
abc123abc123
示例2: ${#parameter}
# 获得字符串长度
[yuxb@controller bin 15:29:57]$ str="abc123abc123"
[yuxb@controller bin 15:30:11]$ echo ${#str}
12
# 其他方法
[yuxb@controller bin 15:30:46]$ echo ${str} | wc -L # wc -L 输出最长行的字符数
12
[yuxb@controller bin 15:30:47]$ expr length "${str}" # expr 是外部命令
12
[yuxb@controller bin 15:30:51]$ echo "$str" | awk '{ print length($0)}' # awk 的 length() 函数也能统计字符串长度
12
示例3: ${parameter:offset}
和 ${parameter:offset:length}
# 提取子串
[yuxb@controller bin 15:30:57]$ str="abc123abc123"
# 从下标 3 开始截取(从0开始计数)
[yuxb@controller bin 15:31:26]$ echo ${str:3}
123abc123
# 从下标 3 开始,截取 4 个字符
[yuxb@controller bin 15:31:30]$ echo ${str:3:4}
123a
示例4: ${parameter#word}
和 ${parameter##word}
[yuxb@controller bin 15:31:34]$ str="abc123abc123"
# 从左侧开始删除子串
[yuxb@controller bin 15:31:57]$ echo ${str#b*a}
abc123abc123
# 未匹配到,第一个字符必须与元字串第一个子符一致
[yuxb@controller bin 15:32:04]$ echo ${str#a*c}
123abc123
[yuxb@controller bin 15:32:17]$ echo ${str##a*c}
123
# # → 删除最短匹配前缀
# ## → 删除最长匹配前缀
# 类似操作也可以用于 后缀删除(% 和 %%)
示例5: ${parameter%word}
和 ${parameter%%word}
[yuxb@controller bin 15:32:21]$ str="abc123abc123"
# 从右侧开始删除子串
[yuxb@controller bin 15:33:12]$ echo ${str%a*c}
abc123abc123
# 未匹配到,最后一个字符必须与元字串最后一个子符一致
[yuxb@controller bin 15:33:18]$ echo ${str%c*3}
abc123ab
[yuxb@controller bin 15:33:28]$ echo ${str%%c*3}
ab
# % → 删除最短匹配后缀
# %% → 删除最长匹配后缀
# 前缀删除(#、##)和后缀删除(%、%%)常用于 路径、文件名、字符串处理
示例6: ${parameter/pattem/string}
[yuxb@controller bin 15:33:33]$ str="abc123abc123"
# 只替换第一个
[yuxb@controller bin 15:34:05]$ echo ${str/abc/def}
def123abc123
# 所有的全替换
[yuxb@controller bin 15:34:10]$ echo ${str//abc/def}
def123def123
# ${变量/旧/新} → 只替换第一个
# ${变量//旧/新} → 全部替换
Shell 脚本的条件测试
文件判断表达式
Bash 条件判断和目录操作
[yuxb@controller bin 16:50:54]$ [ -r /etc/shadow ] && echo i can read /etc/shadow
[yuxb@controller bin 16:51:25]$ ls -l /etc/shadow
---------- 1 root root 776 8月 19 14:31 /etc/shadow
[yuxb@controller bin 16:51:37]$ [ ! -r /etc/shadow ] && echo i can not read /etc/shadow
i can not read /etc/shadow
[yuxb@controller bin 16:51:57]$ [ -d $path ] && echo $path is a directory
is a directory
[yuxb@controller bin 16:52:36]$ ls $path
666.sh 999.sh eval.sh monitory_os noeval.sh read.sh showargs.sh ssh_ctl
[yuxb@controller bin 16:53:28]$ [ ! -d $path ] && echo $path is not a directory
[yuxb@controller bin 16:53:38]$ [ ! -d $path ] && mkdir $path && echo 创建 $path 成功
Try 'rmdir --help' for more information.
[yuxb@controller bin 16:56:22]$ ls $path
666.sh 999.sh eval.sh monitory_os noeval.sh read.sh showargs.sh ssh_ctl
[yuxb@controller bin 16:56:58]$ [ -d $path ] && echo $path is exist
is exist
[yuxb@controller bin 16:57:57]$ path=/tmp/data
[yuxb@controller bin 16:58:55]$ mkdir $path
[yuxb@controller bin 16:58:56]$ [ -d $path ] && echo $path is exist
/tmp/data is exist
[yuxb@controller bin 17:00:14]$ [ -d $path ] || mkdir $path
[yuxb@controller bin 17:00:46]$ ls $path
判断当前用户是否是root用户
[yuxb@controller bin 17:18:16]$ vim 123.sh
[yuxb@controller bin 17:19:11]$ cat 123.sh
#!/bin/bash
[ "$(whoami)" != "root" ] && echo pls run as root. && exit
[ "$USER" != "root" ] && echo pls run as root. && exit
[ "$UID" -ne 0 ] && echo pls run as root. && exit
((UID!=0)) && echo pls run as root. && exit
[yuxb@controller bin 17:19:51]$ chmod +x 123.sh
[yuxb@controller bin 17:19:57]$ 123.sh
pls run as root.