shell脚本中常用指令数据处理

shell脚本的介绍

shell脚本的介绍

Shell脚本的核心确实是 “命令的组合” + “逻辑控制(判断/循环)” + “熟练的操作经验”。为了让之前的复习更扎实,把整体的结构做成结构化脑图。

🧠 Shell 脚本核心知识图谱

1. 基石:指令的熟练操作(你提到的重点)

脚本的本质是“批处理”,所以对单个指令的熟悉程度决定脚本质量。

  • 文件操作:ls、cp、mv、rm、mkdir、touch。

  • 文本处理(重中之重):grep(过滤)、sed(流编辑)、awk(列处理)、wc(统计)、sort(排序)。

  • 系统状态:ps(进程)、netstat/ss(端口)、df/du(磁盘)。

  • 神器:xargs(参数传递)、find(查找文件)。

2. 骨架:变量与参数

脚本不能写死,需要靠变量传递值。

  • 自定义变量:a=10(注意=两边不能有空格)。

  • 系统变量:$HOME、$PATH、$PWD。

  • 位置参数:$0(脚本名)、$1~$9(第1~9个参数)、$#(参数个数)、$?(上一条命令执行结果,0为成功)。

  • 命令替换:date 赋值给变量 => d=$(date) 或 d=date。

3. 灵魂:判断与循环(逻辑控制)

这是脚本从“顺序执行”变为“智能执行”的关键。

(1)条件判断(if 和 case) 判断的核心是 [ 条件 ](注意空格),本质上执行的是 test 命令。

  • 文件判断:-f(是否存在且为文件)、-d(目录)、-e(是否存在)、-x(可执行)。

  • 数值比较(只能用整数):-eq(等于)、-ne(不等于)、-gt(大于)、-lt(小于)、-ge(≥)、-le(≤)。

    • 易错点:> 和 < 是字符串比较,别用在数字上!

  • 字符串比较:=(相等)、!=(不等)、-z(为空)。

匹配关键词自动显示为彩色

 临时设置:
 # alias grep='grep --color=auto' //只针对当前终端和当前用户生效
 ​
 永久设置:
 1)全局(针对所有用户生效)
 vim /etc/bashrc
 alias grep='grep --color=auto'
 source /etc/bashrc
 ​
 2)局部(针对具体的某个用户)
 vim ~/.bashrc
 alias grep='grep --color=auto'
 ​
 注意:如果希望你对环境变量的设置立刻生效,可以使用以下命令而不需要重启计算机
 source ~/.bashrc

数据处理方式

 shell脚本数据的处理
 ​
 1)数据检索:grep tr cut
 ​
 2)数据处理:uniq sort tee paste xargs
 ​
 awk的应用场景
 ​
 1)字符串截取
 ​
 2)数据运算
 ​
 3)比如内存使用率脚本

行检索:grep / egrep

grep 基本检索(案例演示及参数介绍)

 # 在日志中查找包含 "error" 的行(不区分大小写)
 grep -i "error" /var/log/syslog
 ​
 # 查找不包含 "debug" 的行
 grep -v "debug" app.log
 ​
 # 递归查找当前目录下所有 .conf 文件中包含 "AllowOverride" 的行
 grep -r "AllowOverride" /etc/apache2/*.conf
 ​
 # 显示匹配行及其上下各2行
 grep -C 2 "timeout" nginx.log
 ​
 # 忽略大小写匹配包含root的行
 grep -i "root" /etc/passwd
 ​
 # 精确匹配ftp单词
 grep -w "ftp" /etc/passwd
 ​
 # 打印匹配到的关键字ftp
 grep -wo "ftp" /etc/passwd
 ​
 # 打印匹配到root关键字的行号
 grep -n "root" /etc/passwd
 ​
 # 忽略大小写匹配并显示包含关键字root的行
 grep -ni "root" /etc/passwd
 ​
 # 忽略大小写匹配并统计包含关键字root的行数
 grep -nic "root" /etc/passwd
 ​
 # 忽略大小写匹配以root开头的行
 grep -i "^root" /etc/passwd
 ​
 # 匹配以bash结尾的行
 grep "bash$" /etc/passwd
 ​
 # 匹配空行并打印行号
 grep -n "^$" /etc/passwd
 ​
 # 匹配以#号开头的行
 grep "^#" /etc/vsftpd/vsftpd.conf
 ​
 # 匹配不以#号开头的行
 grep -v "^#" /etc/vsftpd/vsftpd.conf
 ​
 # 匹配包含mail关键字及其后5行
 grep -A 5 "mail" /etc/passwd
 ​
 # 匹配包含mail关键字及其前5行
 grep -B 5 "mail" /etc/passwd
 ​
 # 匹配包含mail关键字及其前后各5行
 grep -C 5 "mail" /etc/passwd
 ​
 ​
 # 参数介绍
     -i: 不区分大小写
     -v: 查找不包含指定内容的行,反向选择
     -w: 按单词搜索
     -n: 显示行号
     -A: 显示匹配行及后面多少行 -A 5
     -B: 显示匹配行及前面多少行
 ​
     -o: 打印匹配关键字
     -c: 统计匹配到的次数
     -r: 逐层遍历目录查找
     -C: 显示匹配行前后多少行
     -l:只列出匹配的文件名
     -L:列出不匹配的文件名
     -e: 使用正则匹配
     -E:使用扩展正则匹配
     ^key:以关键字开头
     key$:以关键字结尾
     ^$:匹配空行
     --color=auto :可以将找到的关键词部分加上颜色的显示

egrep(扩展正则)

 # 查找包含 error 或 warning 的行
 egrep "error|warning" /var/log/nginx/access.log
 ​
 # 查找以数字开头的行
 egrep "^[0-9]" data.txt
 ​
 # 查找包含连续3个以上数字的行
 egrep "[0-9]{3,}" user_list.txt
 ​
 # 查找以 error 或 warning 开头的行
 egrep "^(error|warning)" /var/log/syslog
 ​
 # 查找包含邮箱地址的行
 egrep "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}" contacts.txt
 ​
 # 查找包含 IP 地址的行
 egrep "([0-9]{1,3}\.){3}[0-9]{1,3}" access.log
 ​
 # 查找空行或只有空格的行
 egrep "^[[:space:]]*$" config.conf
 ​
 # 查找以 a、b 或 c 开头的行
 egrep "^[abc]" wordlist.txt
 ​
 ​
 # 参数介绍
     egrep: 等同于 grep -E,支持扩展正则表达式
     |: 或,匹配多个模式之一
     ^: 匹配行首
     $: 匹配行尾
     [0-9]: 匹配任意数字
     [a-zA-Z]: 匹配任意字母
     {n,}: 匹配前面字符至少 n 次
     {n,m}: 匹配前面字符 n 到 m 次
     +: 匹配前面字符至少 1 次
     *: 匹配前面字符 0 次或多次
     ?: 匹配前面字符 0 次或 1 次
     \.: 转义,匹配点号
     [[:space:]]: 匹配空白字符(空格、制表符等)
     (): 分组,将多个模式视为一个整体

字符串检索:cut / tr

cut — 按列/字符裁剪

 # 提取 /etc/passwd 的用户名(第1列,以:分隔)
 cut -d: -f1 /etc/passwd | head -5
 ​
 # 提取文件的第3到第5个字符
 cut -c3-5 file.txt
 ​
 # 提取第1列和第3列,以逗号分隔输出
 cut -d, -f1,3 data.csv
 ​
 # 以:冒号分割,截取第1列内容
 cut -d: -f1 1.txt
 ​
 # 以:冒号分割,截取第1,6,7列内容
 cut -d: -f1,6,7 1.txt
 ​
 # 截取文件中每行第4个字符
 cut -c4 1.txt
 ​
 # 截取文件中每行的1-4个字符
 cut -c1-4 1.txt
 ​
 # 截取文件中每行的4-10个字符
 cut -c4-10 1.txt
 ​
 # 从第5个字符开始截取后面所有字符
 cut -c5- 1.txt
 ​
 ​
 # 参数介绍
     cut: 用于列截取
     -c: 以字符为单位进行分割
     -d: 自定义分隔符,默认为制表符 \t
     -f: 与 -d 一起使用,指定显示哪个区域

tr — 字符替换/删除

 # 将所有小写字母转为大写
 echo "hello world" | tr 'a-z' 'A-Z'
 ​
 # 删除所有数字
 echo "abc123def456" | tr -d '0-9'
 ​
 # 压缩连续的换行符为一个换行符
 cat messy.txt | tr -s '\n'
 ​
 # 将文件中的逗号全部替换为制表符
 cat data.csv | tr ',' '\t' > data.tsv
 ​
 # 删除所有空格
 echo "hello world" | tr -d ' '
 ​
 # 将冒号替换为制表符
 echo "root:x:0:0" | tr ':' '\t'
 ​
 # 将大写字母转为小写
 echo "HELLO WORLD" | tr 'A-Z' 'a-z'
 ​
 # 删除所有标点符号
 echo "hello,world!@#" | tr -d '[:punct:]'
 ​
 # 压缩连续的空格为一个空格
 echo "hello    world" | tr -s ' '
 ​
 ​
 # 参数介绍
     tr: 用于字符转换、删除和压缩
     -d: 删除指定字符
     -s: 压缩连续重复字符为一个字符
     'a-z' 'A-Z': 将小写字母转换为大写字母
     '0-9': 匹配所有数字
     '\n': 换行符
     '\t': 制表符
     ' ': 空格
     '[:punct:]': 匹配所有标点符号
     

数据处理命令

sort — 数据排序

 # 按数字排序(默认是字典序)
 sort -n numbers.txt
 ​
 # 按第2列(数值)排序,:分隔
 sort -t: -k2 -n /etc/passwd | head -5
 ​
 # 逆序排序
 sort -r -n scores.txt
 ​
 # 按第3列排序,且忽略前导空格
 sort -k3 -b data.txt
 ​
 # 按第2列数值排序,然后按第1列字母排序
 sort -t, -k2,2n -k1,1 data.csv
 ​
 # 对文件去重排序
 sort -u data.txt
 ​
 # 按第二列字母逆序排序
 sort -t: -k2 -r /etc/passwd
 ​
 # 按月份排序(Jan, Feb, Mar...)
 sort -M month.txt
 ​
 # 按第三列数值排序并忽略大小写
 sort -t: -k3 -n -f /etc/passwd
 ​
 # 检查文件是否已排序
 sort -c data.txt
 ​
 ​
 # 参数介绍
     sort: 文本排序
     -n: 按数值排序(默认是字典序)
     -r: 逆序排序
     -t: 指定字段分隔符(默认是空白)
     -k: 指定排序字段(列),如 -k2 按第2列排序
     -u: 去重排序(等同于 sort | uniq)
     -b: 忽略前导空格
     -M: 按月份排序(Jan, Feb, Mar...)
     -f: 忽略大小写
     -c: 检查文件是否已排序,未排序则报错
     -k2,2n: 指定第2列为起始结束,按数值排序
     

uniq — 数据去重(通常与 sort 配合)

 # 去除连续重复行
 uniq 2.txt
 ​
 # 只显示重复的行
 uniq -d 2.txt
 ​
 # 统计重复行出现的次数
 uniq -dc 2.txt
 ​
 # 统计重复行出现的次数
 sort access.log | uniq -c
 ​
 # 只显示重复的行
 sort list.txt | uniq -d
 ​
 # 只显示不重复的行
 sort list.txt | uniq -u
 ​
 # 统计每个IP的访问次数(去重统计并降序排列)
 awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10
 ​
 ​
 # 参数介绍
     uniq: 用于去除连续重复行(通常与 sort 配合使用)
     -i: 忽略大小写
     -c: 统计重复行出现的次数
     -d: 只显示重复的行
     -u: 只显示不重复的行

paste — 文本数据合并

 # 横向合并两个文件(按行并列)
 paste file1.txt file2.txt
 ​
 # 使用指定分隔符合并
 paste -d: names.txt emails.txt
 ​
 # 将文件的所有行合并成一行(类似转置)
 paste -s -d, scores.txt
 ​
 # 合并多个列,输出为CSV
 paste -d, col1.txt col2.txt col3.txt > merged.csv

tee — 数据输出分流

 # 同时显示并保存到文件(覆盖)
 echo "System info" | tee info.txt
 ​
 # 追加到文件
 date | tee -a log.txt
 ​
 # 管道中间使用:统计行数并保存原始数据
 grep "ERROR" app.log | tee error.log | wc -l
 ​
 # 同时写入两个文件
 ls -l | tee file1.txt file2.txt

xargs — 参数传递与批量处理

 # 查找所有 .log 文件并删除
 find /var/log -name "*.log" -type f | xargs rm -f
 ​
 # 批量创建用户(从 user.txt 读用户名)
 cat users.txt | xargs -I {} useradd -m {}
 ​
 # 并行执行(4个进程)
 cat urls.txt | xargs -P 4 -I {} curl -s {} > /dev/null
 ​
 # 将每5个参数作为一组执行
 echo 1 2 3 4 5 6 7 8 | xargs -n 3 echo "组:"
 ​
 # 交互式确认删除
 find . -name "*.tmp" | xargs -p rm

shell字符

 有基础的同学不要和正则表达式中的符号含义搞混淆了。    
     !:                执行历史命令   !! 执行上一条命令
     $:                变量中取内容符
     + - * / %:        对应数学运算  加 减 乘 除 取余数  
     &:                后台执行
     ;:               分号可以在shell中一行执行多个命令,命令之间用分号分割    
     \:                转义字符
     ``:               反引号 命令中执行命令    echo "today is `date +%F`"
     ' ':              单引号,脚本中字符串要用单引号引起来,但是不同于双引号的是,单引号不解释变量
     " ":              双引号,脚本中出现的字符串可以用双引号引起来
 ​
 通配符    
     ~:                家目录    # cd ~ 代表进入用户家目录
     *:                星号是shell中的通配符  匹配所有
     ?:                问号是shell中的通配符  匹配除回车以外的一个字符
     [list]: 匹配[list]中的任意单个字符
 [!list]: 匹配除list中的任意单个字符
 {string1,string2,...}: 匹配string1,string2或更多字符串
 ​
 ​
 重定向
 >           覆盖输入 
 >>          追加输入
 <           输出
 <<          追加输出
 ​
 管道命令
 |:               管道符 上一个命令的输出作为下一个命令的输入   cat filename | grep "abc"