Shell 进阶

1. 正则表达式

正则表达式

2. Shell 文本处理工具

2.1. grep

grep (Global Regular Expression Print) 是 Linux/Unix 系统中一个强大的文本搜索工具,用于在文件中查找包含特定模式的行。

基本语法

grep [选项] 模式 [文件...]

# 示例
grep "pattern" file.txt
Bash

常用选项

-i, --ignore-case           # 忽略大小写
-v, --invert-match          # 反向查找,只显示不匹配的行
-n, --line-number           # 显示匹配行的行号
-c, --count                 # 如果匹配成功,则输出匹配到多少行
-l, --files-with-matches    # 如果匹配成功,则只将文件名打印出来,失败则不打印。通常 -r 一起用,grep -rl 'root' /etc

-R, -r, --recursive         # 递归搜索目录
-w                          # 全词匹配,当你想搜索一个单词,但不想匹配到包含该单词的其他更长单词时使用。
-o, --only-matching         # 只显示匹配的内容

-q, --quiet, --silent       # 静默模式,不会向终端输出内容,得用$?来判断执行成功没有,即有没有过滤到想要的内容

-A, --after-context=NUM     # 如果匹配成功,则将匹配行及其后n行一起打印出来
-B, --before-context=NUM    # 如果匹配成功,则将匹配行及其前n行一起打印出来
-C, --context=NUM           # 如果匹配成功,则将匹配行及其前后n行一起打印出来

-E                          # 使用扩展正则表达式 (等同于 egrep)
Bash

高级用法示例

# 使用扩展正则表达式
grep -E "pattern1|pattern2" filename

# 搜索多个文件
grep "pattern" file1 file2 file3

# 将输出重定向到文件
grep "pattern" filename > output.txt

# 结合其他命令使用
ps aux | grep "process_name"
Bash

实际应用示例

# 查找所有包含"error"的日志条目
grep -i "error" /var/log/syslog

# 统计Java代码中"public"关键字出现的次数
grep -c "public" *.java

# 查找当前目录及其子目录中所有包含"TODO"注释的文件
grep -rn "TODO" .

# 示配置文件中的非注释行
grep -v "^#" /etc/nginx/nginx.conf

# 查找包含IP地址的行
grep -E "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b" file
Bash

2.2. sed

sed (Stream EDitor) 是一个强大的流式文本编辑器,用于对输入流(文件或管道)进行基本的文本转换。它以行为单位进行处理,可以将结果输出到标准输出或文件。

通俗来讲,sed 是一种在线的非交互式的编辑器,它一次只处理一行内容。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用 sed 命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出,或者使用 sed -i 选项,-i 选项就是将本该输出到屏幕上的内容输出/流入文件中。

模式空间与保持空间

模式空间 (Pattern Space):sed 的主要工作区 sed 每次读取一行文本到模式空间进行处理,大多数 sed 命令(如 s///pd 等)都是对模式空间进行操作。每处理完一行后自动清空,再读入下一行。

保持空间 (Hold Space):用于临时保存文本内容的额外缓冲区,用辅助模式空间处理文本。开始时为空,保存的内容会一直保留,直到被显式修改。

模式空间与保持空间使用案例:交换文件的行

cat test.txt 
1111
2222
3333
 

# 实现步骤:
# 1、读取文件第一行内容到模式空间,进行的操作如下  
# 将模式空间内容覆盖到保持空间
# 删除模式空间内容
 
# 2、读取文件第二行内容到模式空间,进行的操作如下  
# 将保持内容追加到模式空间
# 将模式空间内容覆盖到保持空间
# 删除模式空间内容 
 
# 3、读取文件第三行内容到模式空间,进行的操作如下  
# 将保持空间内容追加到模式空间
 
# 命令
sed -r '1h;1d;2G;2h;2d;3G' test.txt
# 或者
sed '1!G;h;$!d' test.txt
Bash

基本语法

sed [选项] '命令' 输入文件
sed [选项] -e '命令1' -e '命令2' 输入文件
sed [选项] -f 脚本文件 输入文件
Bash

常用选项

-e              # 允许多项编辑
-n              # 禁止自动打印模式空间内容
-i              # 直接修改文件内容
-r              # 使用扩展正则表达式
-f              # 指定sed脚本文件名
 
# 示例
# 演示文件
cat > test.txt <<EOF
1111111
2222222
3333333
4444444
5555555
EOF

# '' 空命令表示不执行任何编辑操作,仅打印模式空间内容
sed '' test.txt 
1111111
2222222
3333333
4444444
5555555
# 对比添加 -n 参数
sed -n '' test.txt   # 未输出任何内容


# 同时执行多条命令,d 表示删除,数字表示行数,命令详解见下一小结。
sed -e '3d' -e '1d' test.txt  
2222222
4444444
5555555
# 查看源文件,发现没有修改文件
cat test.txt
1111111
2222222
3333333
4444444
5555555


# p 表示打印
sed -rn -e '1,3d' -e 'p' test.txt
4444444
5555555
# 对比不加 -n
sed -r -e '1,3d' -e 'p' test.txt
4444444
4444444
5555555
5555555    # 会自动打印模式空间内容,同时使用打印命令所以,会输出两次内容。
 
# 也可以将命令写入一个文件中
cat sed.txt 
1,3d
p
sed -rn -f sed.txt test.txt
4444444
5555555


# 直接修改文件
sed -i -e '3d' -e '1d' test.txt
cat test.txt 
2222222
4444444
5555555
Bash

常用命令

命令的作用是告诉 sed 对指定行进行何种操作,包括打印、删除、修改等。

sed 命令由“地址+命令”两部分组成,命令如:1,3d,1,3 表示要处理的行数,d 表示执行什么操作。如果没有指定地址,sed 将处理所有行。

地址部分可以是数字也可以使用正则表达式,示例:

sed '3d' file.txt        # 删除第3行
sed '2,5d' file.txt      # 删除2-5行
sed '/^#/d' file.txt     # 删除所有注释行(以#开头)
Bash

基本操作命令:

s          # 替换命令
sed 's/原字符串/新字符串/[flags]' 文件名
# flags 可以是:
# g:全局替换(一行中所有字符串都匹配,不加则匹配一行中的第一个)
# 数字:每行替换到第n个匹配
# p:打印替换的行
# w 文件:将替换的行写入文件
# i:忽略大小写
# 示例:只处理以数字开头的行,在这些行中,将所有 Apple 替换为 Orange,将修改后的这些行保存到 orange_lines.txt 文件中
sed '/^[0-9]/s/Apple/Orange/gw orange_lines.txt' test.txt



d          # 删除命令
sed '/pattern/d' file.txt



p          # 打印命令
# 通常与 -n 选项一起使用:
sed -n '/pattern/p' file.txt



q          # 退出命令
sed '5q' file.txt  # 处理前5行后退出



i          # 在当前行之前添加一行或多行
sed '3i\插入内容' file.txt



a          # 追加命令,在当前行后添加一行或多行
sed '/pattern/a\追加内容' file.txt



c          # 用新文本修改(替换)当前行中的文本
sed '/pattern/c\新行内容' file.txt



r          # 从文件读取内容并追加到匹配行后
sed '/pattern/r otherfile' file.txt



w          # 将匹配行写入文件
sed '/pattern/w output.txt' file.txt



n          # 把下一行内容读入模式空间,后续的处理命令处理的都是刚读入的新内容
sed '/pattern/{n;d}' file.txt  # 删除匹配行的下一行



N          # 将下一行追加到模式空间(两行合并)
sed 'N;s/\n/ /' file.txt  # 合并连续两行



x          # 交换模式空间和保持空间内容
h          # 用模式空间覆盖保持空间
H          # 将模式空间追加到保持空间

g          # 用保持空间覆盖模式空间
G          # 将保持空间追加到模式空间



l          # 会用 $ 符号标识出文件中看不到的字符的位置



!          # 对所选行以外的所有行应用命令



=          # 打印行号
sed -n '/pattern/=' file.txt



y          # 将字符转换为另一字符(字符一对一转换)
sed 'y/abc/ABC/' file.txt
Bash

2.3. awk

awk 是一种强大的文本处理工具,主要用于模式扫描和处理,适用于结构化文本数据(如日志、CSV、表格数据等)。它支持变量、条件判断、循环、数组等编程特性,可以高效地提取、转换和格式化数据。

基本语法

awk [选项] '模式 {动作}' 输入文件

# 通过管道传递
command | awk '模式 {动作}'
Bash

常用选项

-F          # 指定字段分隔符(默认是空格或制表符)
-v          # 定义变量(-v var=value)
-f          # 从脚本文件读取 awk 命令
Bash

awk 工作原理

# 示例
awk -F: '{print $1,$3}' /etc/passwd
 
# 1.awk 会接收一行作为输入,并将这一行赋给awk的内部变量$0,每一行也可称为一个记录,行的边界是以换行符作为结束。
 
# 2.然后,刚刚读入的行被以:为分隔符分解成若干字段(或域),每个字段存储在已编号的变量中,编号从$1开始,最多达100个字段。
# 注意:如果未指定行分隔符,awk将使用内置变量FS的值作为默认的行分隔符,FS默认值为空格
 
# 3.使用print函数打印,如果$1$3之间没有逗号,它俩在输出时将贴在一起,应该在$1,$3之间加逗号,该逗号与awk的内置变量OFS保持一致,OFS默认为空格,于是以空格为分隔符输出$1和$3
# 我们可以指定OFS的值:awk -F: 'BEGIN{OFS="-"}{print $1,$3}' /etc/passwd

# 4.输出之后,将从文件中获取另一行,然后覆盖给$0,继续重复上述步骤一直持续到所有行处理完毕。
Bash

内置变量

$0                # 当前整行内容
$1, $2, ..., $NF  # 以分隔符分隔的第 1、2、...、最后一个字段内容
NF                # 当前行以分隔符分隔后的字段数
NR                # 当前行号
FNR               # 当前文件的行号(多文件处理时有用)
FS                # 输入字段分隔符(默认空格)
OFS               # 输出字段分隔符(默认空格)
RS                # 输入记录(行)分隔符(默认 \n)
ORS               # 输出记录(行)分隔符(默认 \n)
FILENAME          # 当前处理的文件名
Bash

awk 用法示例

# 打印整行
awk '{print $0}' file.txt

# 打印特定列
awk '{print $1, $3}' file.txt  # 打印第1和第3列

# 指定分隔符
awk -F',' '{print $2}' data.csv  # 以逗号分隔,打印第2列

# 条件过滤
awk '$3 > 100 {print $1, $3}' file.txt  # 第3列大于100的行
awk '/error/ {print}' log.txt           # 包含 "error" 的行

# 计算行数
awk 'END {print NR}' file.txt  # 统计总行数

# 计算列总和
awk '{sum += $1} END {print sum}' file.txt

# BEGIN 和 END 块使用
awk 'BEGIN {FS=":"; OFS="|"} {print $1, $6} END {print "Total:", NR}' /etc/passwd

# 条件判断(if-else)
awk '{if ($3 > 50) print $1 " High"; else print $1 " Low"}' data.txt

# 循环(for/while)
awk '{for (i=1; i<=NF; i++) print $i}' file.txt  # 逐字段打印

# 数组(统计词频)
awk '{count[$1]++} END {for (word in count) print word, count[word]}' file.txt

# 字符串操作
awk '{print length($1), substr($1, 2, 3), toupper($1)}' file.txt

# 多文件处理
awk '{print FILENAME, NR, $0}' file1.txt file2.txt
Bash

3. 进程锁

进程锁(文件锁)在 Shell 脚本中用于确保同一时间只有一个脚本实例运行,防止资源竞争和数据损坏。以下是一种推荐实现方法:

#!/bin/bash

LOCK_FILE="/tmp/my_script.lock"

# 尝试获取锁,如果获取不到则退出
exec 200>"$LOCK_FILE"
flock -n 200 || {
    echo "另一个实例正在运行,退出..."
    exit 1
}

# 你的脚本主体代码
echo "脚本正在运行..."
sleep 10

# 自动释放锁(脚本结束时文件描述符关闭)
Bash
上一篇
下一篇