Linux 文本处理与 Shell 编程笔记:正则表达式、sed、awk 与变量脚本

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

解释:

  • . → 任意字符

  • \. → 真正的小数点

  • ⚠️ 小白注意:转义符可以让符号按"原义"匹配。


|(必须用 egrepgrep -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 命令选项实例

模式选择和解释选项

-Eegrep
  • 作用 :支持扩展正则表达式,能用 + ? | {} 等高级语法

  • 示例

复制代码
 [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

忽略大小写后,CBtcBtcbt 都能匹配。


-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 的操作命令(如 pds

  • 文件:要处理的文本文件,如果省略则默认从标准输入读取

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 命令作用的行,可以是行号、范围、正则匹配等。

  • 命令 :如 pds 等。

示例
复制代码
 [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 '/模式/ 命令' 文件
  • /模式/:正则表达式,用来匹配行

  • 命令:对匹配到的行执行操作,如 pds

准备文件

复制代码
 [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: 加了-np后表示只打印那些发生替换的行(部分替换),下面的例子,不需要使用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: 加了-npg后表示只打印那些发生替换的行(全部替换)

复制代码
 [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 位置参数变量

示例1showargs.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.
 ​
相关推荐
G_H_S_3_14 分钟前
【网络运维】Linux:正则表达式
linux·运维·网络·正则表达式
敲上瘾15 分钟前
Linux I/O 多路复用实战:Select/Poll 编程指南
linux·服务器·c语言·c++·select·tcp·poll
huangyuchi.22 分钟前
【Linux系统】匿名管道以及进程池的简单实现
linux·运维·服务器·c++·管道·匿名管道·进程池简单实现
MrZhangBaby41 分钟前
SQL-leetcode—3374. 首字母大写 II
linux·sql·leetcode
czhc11400756631 小时前
LINUX 820 shell:shift,expect
linux·运维·excel
望获linux2 小时前
【实时Linux实战系列】基于实时Linux的物联网系统设计
linux·运维·服务器·chrome·php
翻滚的小@强2 小时前
数据挖掘笔记:点到线段的距离计算
人工智能·笔记·数据挖掘
会思考的猴子2 小时前
UE5 PCG 笔记(二) Difference 节点
笔记·ue5
刘一说2 小时前
CentOS 系统 Java 开发测试环境搭建手册
java·linux·运维·服务器·centos