我与Shell的30天:从“命令小白“到“自动化高手“的成长手记

序章:那个被日志逼疯的夜晚

30天前,我在一家互联网公司做后端开发。某天深夜,运维同事丢给我一个任务:"服务器日志炸了,赶紧把最近7天的错误日志筛出来,按模块分类打包,明早9点前发我。"我盯着终端里密密麻麻的日志,手忙脚乱地用grep "ERROR"筛了两页,突然发现——我不会用Shell。

那晚我熬到凌晨三点,用最笨的方法:手动复制粘贴、用Excel筛选、甚至用Word做统计。第二天交差时,运维大哥扫了眼文件:"你这格式乱得,下次学学Shell吧,半小时能搞定。"这句话像根刺扎在我心里——原来,我离"高效"只差一个Shell的距离。

这30天,我从对着终端发抖的新手,到能写复杂脚本的"自动化狂魔"。今天,我想把这些血泪经验整理成一份"保姆级指南",帮你少走弯路,真正把Shell变成你的技术武器。

第一关:打破恐惧——Shell到底是个啥?

1.1 你和计算机的"方言"对话

想象你在教一个完全不懂中文的外国人做事:"把桌上的红色杯子拿到厨房"。他会问:"红色?杯子?厨房?"这时候你需要用他的语言(比如英语)解释:"Take the red cup on the table to the kitchen."

Shell就是你和计算机之间的"翻译官"。你用ls -l告诉它:"列出当前目录的所有文件,详细点";它翻译成计算机能听懂的系统调用(比如getdents),再把结果翻译回你能看懂的文字。

1.2 为什么必须学Shell?3个真实场景

场景1:上线后服务器CPU飙高,你需要快速定位哪个进程在搞鬼。用top -c看进程,ps -ef | grep 进程名找启动命令,kill -9 PID终止进程——这些操作,不用Shell你得翻遍图形化工具。

场景2:每周五要备份10个项目代码到云盘。手动cd、git pull、tar、scp?用Shell脚本写个循环,一键搞定10个项目。

场景3:日志里有10万条记录,你要找出所有包含"Timeout"且状态码504的条目。用grep "Timeout" access.log | grep "504",10秒出结果;手动翻?得翻到眼瞎。

结论:Shell不是"运维专属",而是每个开发者的"效率外挂"。它能让你和计算机对话更高效,把重复劳动变成"一键执行"。

第二关:基础语法——先搞定这些,你就能写90%的脚本

2.1 第一个脚本:从"hello world"到"我能解决实际问题"

很多人学Shell的第一步是写hello.sh,但我建议你直接挑战一个有实际价值的小任务:"输出当前目录下所有文件的大小(MB为单位),并按从大到小排序"。

试试看,你能写出这样的脚本吗?

代码:

 

#!/bin/bash

# 文件大小统计脚本(进阶版hello world)

echo "当前目录文件大小统计(MB):"

du -sm * | sort -hr | awk '{print $2 ": " $1 "MB"}'

 

du -sm *:统计每个文件/目录的大小(-s汇总,-m以MB为单位);

sort -hr:按数值逆序排序(-h兼容人类可读单位,-r降序);

awk:格式化输出,只保留文件名和大小。

运行结果:

代码:

 

当前目录文件大小统计(MB):

logs/: 1234MB

data.csv: 567MB

backup.tar.gz: 456MB

 

成就感爆棚! 这就是Shell的魅力——用几行代码解决原本需要手动操作半小时的问题。

2.2 变量:给数据起个"名字"

变量是Shell的"储物盒",用来存你需要的数据。比如,你想把日志路径存起来,方便后续修改:

代码:

 

LOG_DIR="/var/log/myapp"  # 定义变量(等号两边不能有空格!)

echo "日志路径:$LOG_DIR"  # 使用变量($符号是关键)

 

常见坑点:

变量赋值时,等号两边不能有空格(LOG_DIR = /var/log会报错);

变量名区分大小写(log_dir和LOG_DIR是两个不同的变量);

用${}明确变量边界(echo "文件:${LOG_DIR}/app.log"比echo "文件:$LOG_DIR/app.log"更安全,避免路径中有特殊字符时出错)。

2.3 输入输出:和用户"聊天"

想让脚本更灵活?试试让用户输入参数。比如,写一个"根据输入的数字计算平方"的脚本:

代码:

 

#!/bin/bash

echo "请输入一个数字:"

read num  # 读取用户输入到变量num

square=$((num * num))  # 计算平方($(( ))是算术运算)

echo "$num 的平方是:$square"

运行效果:

代码:

 

请输入一个数字:

5

5 的平方是:25

 

进阶玩法:用read一次性读取多个变量:

代码:

 

 

echo "请输入姓名和年龄(用空格分隔):"

read name age

echo "你好,$name!你今年$age岁了。"

 

2.4 控制流:让脚本"做决定"

条件判断:if-elif-else

比如,写一个"判断成绩等级"的脚本:

代码:

 

#!/bin/bash

score=85

 

if [ $score -ge 90 ]; then

    grade="优秀"

elif [ $score -ge 80 ]; then

    grade="良好"

elif [ $score -ge 60 ]; then

    grade="及格"

else

    grade="不及格"

fi

echo "分数:$score,等级:$grade"

关键语法:

[ ]是test命令的别名,里面每个条件都要用空格隔开(比如$score -ge 90);

-ge表示"大于等于"(其他常用运算符:-eq等于,-lt小于,-n字符串非空)。

循环:for/while

场景:批量重命名当前目录下所有.txt文件为.md,并加note_前缀。

用for循环轻松搞定:

代码:

 

for file in *.txt; do

    new_name="note_${file%.txt}.md"  # ${file%.txt}去掉.txt后缀

    mv "$file" "$new_name"

done

echo "重命名完成!共处理$(ls note_*.md | wc -l)个文件"

 

关键技巧:

*.txt会匹配所有以.txt结尾的文件;

${file%.txt}是变量替换,去掉变量值的.txt后缀(类似Python的str.rstrip(".txt"));

$(...)是命令替换,把括号里的命令结果插入到当前位置(比如ls note_*.md | wc -l统计文件数量)。

第三关:实战进阶——用Shell解决真实痛点

3.1 痛点1:日志分析——快速定位问题

场景:线上服务器日志app.log有10GB,你需要找出最近1小时内所有"ERROR"级别的日志,并按模块分类统计数量。

解决方案:

代码:

 

#!/bin/bash

LOG_FILE="/var/log/app.log"

ONE_HOUR_AGO=$(date -d "1 hour ago" +"%Y-%m-%d %H:%M:%S")  # 计算1小时前的时间

 

# 筛选最近1小时的ERROR日志,并按模块(假设模块名在日志的第三列)统计

grep "$ONE_HOUR_AGO" "$LOG_FILE" | grep "ERROR" | awk '{print $3}' | sort | uniq -c

 

效果:

代码:

 

 

   45 payment  # payment模块出现45次ERROR

   32 order    # order模块出现32次ERROR

    8 user     # user模块出现8次ERROR

 

关键技术:

date -d:动态计算时间(比硬编码时间更灵活);

grep:文本搜索(-E参数可启用正则表达式,比如grep -E "ERROR|WARN"同时搜索ERROR和WARN);

awk:按列处理($3表示第三列,sort | uniq -c统计频率)。

3.2 痛点2:文件批量处理——解放双手

场景:你下载了100张图片,命名混乱(img1.jpg、pic2.png、2023_photo.jpg),需要统一重命名为photo_001.jpg、photo_002.png等格式。

解决方案:

代码:

 

#!/bin/bash

COUNT=1  # 计数器

for file in *.{jpg,png,gif}; do  # 匹配所有jpg/png/gif文件

    ext="${file##*.}"  # 提取文件后缀(${var##*.}表示取最后一个点后的内容)

    new_name="photo_$(printf "%03d" $COUNT).$ext"  # 格式化计数器(03d表示3位,前面补0)

    mv -- "$file" "$new_name"  # 重命名(--防止文件名以-开头时报错)

    ((COUNT++))  # 计数器+1(等价于COUNT=$((COUNT+1)))

done

echo "重命名完成!共处理$((COUNT-1))个文件"

 

关键技术:

*.{jpg,png,gif}:通配符扩展(匹配所有指定后缀的文件);

${file##*.}:变量替换(提取文件后缀);

printf "%03d":格式化数字(补零,比如1→001);

((COUNT++)):算术运算(简化COUNT=$((COUNT+1)))。

3.3 痛点3:自动化运维——解放重复劳动

场景:你负责维护3台服务器,需要每天凌晨2点备份它们的/var/www目录到本地/backup,并删除7天前的旧备份。

解决方案:用Shell脚本+crontab实现定时任务。

步骤1:写备份脚本backup_servers.sh:

代码:

 

#!/bin/bash

BACKUP_DIR="/backup"

DATE=$(date +%Y%m%d)  # 当前日期(格式:20231025)

 

# 备份3台服务器

for ip in 192.168.1.101 192.168.1.102 192.168.1.103; do

    scp -r root@$ip:/var/www "$BACKUP_DIR/www_$DATE"  # 远程拷贝目录

    tar -czf "$BACKUP_DIR/www_$DATE.tar.gz" -C "$BACKUP_DIR" "www_$DATE"  # 打包压缩

    rm -rf "$BACKUP_DIR/www_$DATE"  # 删除临时目录

done

 

# 删除7天前的备份

find "$BACKUP_DIR" -name "www_*.tar.gz" -mtime +7 -delete  # -mtime +7表示7天前

 

步骤2:设置定时任务(每天2点执行):

 

crontab -e  # 打开crontab编辑器

# 添加以下内容(保存退出后生效):

0 2 * * * /bin/bash /path/to/backup_servers.sh >> /backup/backup.log 2>&1

 

关键技术:

scp:远程文件传输(root@$ip:/path表示远程服务器的路径);

tar:打包压缩(-czf创建gzip压缩包,-C切换目录);

find:查找文件并删除(-mtime +7匹配7天前的文件,-delete直接删除);

crontab:定时任务(0 2 * * *表示每天2:00)。

第四关:避坑指南——这些错误90%的新手会犯

4.1 常见错误1:路径问题——"文件找不到"

现象:脚本运行时报错No such file or directory,但文件明明存在。

原因:Shell脚本的工作目录(pwd)可能和你预期不同。比如,你在/home/user目录下运行./script.sh,但脚本里的/var/log/app.log是绝对路径,没问题;但如果脚本里用了logs/app.log(相对路径),实际路径会是/home/user/logs/app.log,如果不存在就会报错。

解决方案:

尽量用绝对路径(如/var/log/app.log);

如果必须用相对路径,在脚本开头打印当前工作目录:echo "当前工作目录:$(pwd)";

用realpath命令获取文件的绝对路径:FILE_PATH=$(realpath "logs/app.log")。

4.2 常见错误2:变量未定义——"结果不对"

现象:脚本输出的变量值为空,但你确定已经赋值了。

原因:Bash默认允许变量未定义(视为空),如果脚本依赖变量的值,可能导致逻辑错误。

解决方案:

用set -u开启"未定义变量报错"(脚本开头加set -u,遇到未定义变量直接终止);

初始化变量(即使赋默认值,如LOG_DIR=${LOG_DIR:-"/var/log"},表示如果LOG_DIR未定义,默认用/var/log)。

4.3 常见错误3:权限问题——"没有那个权限"

现象:脚本运行时报错Permission denied。

原因:

脚本本身没有执行权限(需要chmod +x script.sh);

脚本调用了没有权限的命令(比如往/root目录写文件,但当前用户不是root)。

解决方案:

给脚本添加执行权限:chmod +x script.sh;

用sudo运行需要权限的命令(如sudo mv file /root),或在脚本里检查权限(if [ ! -w "/root" ]; then echo "无写入权限"; exit 1; fi)。

终章:Shell的未来——从"脚本小子"到"自动化专家"

30天前,我对着终端发抖;今天,我能用Shell写复杂脚本,用cron定时执行,用ssh远程管理服务器。更重要的是,我学会了用Shell思维解决问题:遇到重复劳动,先想"能不能用一行命令搞定?"

Shell不是终点,而是起点。掌握它后,你可以:

学习sed/awk高级文本处理(比如用awk生成报表);

掌握ssh密钥登录(告别重复输入密码);

结合Python/Go写更强大的工具(Shell负责调用,Python负责复杂逻辑);

甚至学习Ansible/Puppet(它们底层也是Shell命令)。

最后送一句话:Shell的终极目标不是"写脚本",而是"解决问题"。现在就打开终端,输入echo "Hello, Shell!",迈出你的第一步吧!

延伸资源:

经典书籍:《Shell脚本学习指南(第3版)》《Linux命令行与shell脚本编程大全》;

在线工具:ShellCheck(语法检查)、ExplainShell(命令解析);

社区:Stack Overflow(搜Shell问题)、GitHub(找优秀的Shell脚本示例)。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值