Linux基础与Bash 5.2环境:WSL2与云原生适配

聊到Linux入门,咱们得先搭个顺手的环境。现在都2024年了,Linux内核最新已经到6.8版本(今年3月刚发布的),你要是还在纠结装双系统或者搞虚拟机,那真的有点“复古”了。简单来说,对于绝大多数开发者和运维新手,我首推WSL2(Windows Subsystem for Linux 2)。它不仅能让你在Windows上无缝跑一个完整的Linux内核,而且性能损耗极低,文件互访也方便。

咱们现在的Bash(也就是你打开终端时默认的那个Shell)主流版本是5.2(2022年9月发布的),虽然Shell脚本有个通用的POSIX标准,但Bash 5.2在交互体验和脚本安全性上确实做了不少优化。

为什么推荐WSL2而不是传统虚拟机?

以前咱们用VMware或者VirtualBox,不仅占内存,而且网络配置、文件共享能把人折腾死。WSL2基于Hyper-V技术,轻量得不像话。

快速上手你的第一个脚本

既然环境提到了Bash 5.2,咱们直接上手写个最简单的脚本,感受一下。别怕,这东西比你想的简单。

打开你的终端(WSL2或者MacOS的Terminal都行),输入 bash --version,确认一下版本。然后咱们创建一个文件:

# 创建一个名为 hello.sh 的文件,用 nano 或者 vim 都行 nano hello.sh

在文件里写入以下内容:

#!/bin/bash # 这是一个简单的测试脚本 # 这里的 #!/bin/bash 叫做 Shebang,告诉系统用哪个解释器来执行 echo "Hello, Linux World!" echo "当前Bash版本是: $BASH_VERSION" echo "当前时间是: $(date)"

保存退出后,记得给这个脚本执行权限(这是新手必踩的坑,忘了加权限就跑不起来):

chmod +x hello.sh ./hello.sh

你会看到输出。这里用到了$BASH_VERSION这个内置变量,这就是Bash 5.2环境给你提供的现成信息,不用你自己去查系统文件。

📖 学习建议

别在Windows的C盘根目录直接跑你的Shell脚本项目。 虽然WSL2能访问/mnt/c,但那边的文件权限(NTFS)和Linux的权限(ext4)处理逻辑不一样,容易导致脚本里的chmod失效或者Git显示一堆文件改动。建议把代码放在WSL2的家目录(~ 或者 /home/你的用户名)下,那里才是Linux的原生地盘,跑起来最稳。

---

Linux文件与权限管理:ls, chmod, sudo实战

搞定了环境,咱们来聊聊Linux里最核心的生存技能——“找文件”和“管权限”。其实,Linux里一切皆文件,你要是不会操作文件,那基本寸步难行。

文件查看神器 `ls`

ls 这个命令大家都认识,但很多人只会敲个 ls。这就好比你进了一家图书馆,只会在门口看一眼就走。咱们得学会看细节。

比如,查看当前目录下所有文件的详细信息(包括隐藏文件,即以.开头的文件):

ls -lah

输出大概长这样:

drwxr-xr-x 2 root root 4.0K Mar 20 10:00 . drwxr-xr-x 10 root root 4.0K Mar 19 12:00 .. -rw-r--r-- 1 1000 1000 123 Mar 20 09:30 config.txt

看那个 drwxr-xr-x 没?这就是权限位。第一个字符d代表是目录,-代表是文件。后面9个字符分三组:rwx(所有者)、r-x(组用户)、r-x(其他用户)。

权限修改与 `sudo`

权限控制是Linux的安全基石。咱们经常遇到“Permission denied”(权限拒绝),这时候新手容易直接 sudo rm -rf,这是大忌!

修改权限用 chmod。有两种写法,一种是数字,一种是字母。我推荐新手用字母,不容易出错。

假设你有一个脚本 deploy.sh,你想让所有者能读写执行,其他人只能读和执行:

# 给文件增加执行权限 chmod u+x deploy.sh # 或者更狠一点,直接设定权限 chmod 755 deploy.sh

避雷经验提醒chmod 777 是很多新手的噩梦。这相当于告诉系统:“谁来都能动这个文件”,非常不安全。在现在的安全强化趋势下,系统对权限校验越来越严格,咱们要养成最小权限原则。

当你需要干管理员才能干的事(比如装软件、改系统配置),就得用 sudo。它不是命令,而是个前缀,意思是“以超级管理员身份运行”。

# 更新软件源,必须加 sudo sudo apt update

实战:创建一个隔离的测试目录

光说不练假把式,咱们来个实战。假设你要给团队里的 dev 用户组创建一个共享目录,大家都能写,但别人看不见。

# 1. 创建目录 sudo mkdir /opt/shared_data # 2. 创建用户组(如果不存在) sudo groupadd dev # 3. 把目录的所有者设为 root,组设为 dev sudo chown root:dev /opt/shared_data # 4. 设置权限:所有者rwx,组rwx,其他用户无权限 # rwx = 7, rwx = 7, --- = 0 sudo chmod 770 /opt/shared_data # 5. 验证一下 ls -ld /opt/shared_data

你会看到类似 drwxrwx--- 的输出,这就对了。

💡 经验总结

没事别用 root 登录。 现在的Linux发行版(包括最新的内核趋势)都在弱化 root 直接登录,转而推崇 sudo。如果你平时操作都用 root,哪天手滑敲了个 rm -rf /,系统就真成砖了。用 sudo 能让你在执行危险命令时多思考一秒钟,这一秒可能救你的饭碗。

---

进程监控与网络操作:ps, top, curl, ss命令详解

服务器跑起来了,怎么知道它有没有偷懒?有没有被攻击?这就需要咱们掌握进程监控网络操作。这不仅是运维的活儿,开发调试的时候也离不开。

进程管理三板斧 `ps` 与 `top`

ps 是查看当前瞬间的进程快照。最经典的搭配是 ps aux

ps aux | grep nginx

这里有个面试考点梳理:ps aux | grep nginx 的执行逻辑是什么?

其实,就是先让 ps aux 把系统里所有进程列出来,然后通过管道 | 传给 grepgrep 再从中过滤出包含 nginx 字样的行。

避雷经验点:你会发现结果里往往有一行是 grep --color=auto nginx,这是 grep 命令本身。怎么去掉?加个正则技巧:

ps aux | grep [n]ginx

这样 grep 找的是 nginx,但它自己进程里显示的是 [n]ginx,就匹配不上自己了。

如果你想实时盯着进程,那就用 top 或者它的升级版 htoptop 就像Windows的任务管理器,默认按CPU占用排序。按 q 退出,按 M 按内存排序。

网络诊断利器 `curl` 与 `ss`

现在的应用离不开网络。curl 是命令行里的浏览器,用来发请求。

比如,测试一下你的后端接口是不是通的:

# 发送一个 GET 请求,并显示响应头 curl -i http://localhost:8080/api/health

以前我们查端口用 netstat,但现在它有点老了。根据现在的运维标准,推荐用 ss (Socket Statistics)。它更快,而且直接读取内核数据。

# 查看所有监听中的TCP端口 ss -tuln

如果你想看是哪个进程占用了8080端口,杀掉它:

# 找到占用8080的进程 ss -tulnp | grep 8080 # 假设看到 PID 是 1234 kill 1234 # 如果杀不掉,强制杀 kill -9 1234

实战:监控并重启挂掉的服务

咱们写个简单的逻辑,模拟监控Nginx是否运行,如果挂了就重启。

#!/bin/bash SERVICE_NAME="nginx" # 检查进程是否存在 if ! ps aux | grep -v grep | grep -q $SERVICE_NAME; then echo "$(date): $SERVICE_NAME is down. Attempting to restart..." # 这里用 systemctl,如果是容器环境可能只是启动命令 sudo systemctl start $SERVICE_NAME if [ $? -eq 0 ]; then echo "Restart successful." else echo "Restart failed." fi else echo "$(date): $SERVICE_NAME is running." fi

⚡ 效率提示

不要在生产环境随便用 kill -9 -9 信号(SIGKILL)是强制杀死,进程连清理现场(比如关闭文件句柄、释放锁)的机会都没有。这就好比你直接拔电源,而不是点“关机”。除非进程已经僵死(Zombie)或者完全不响应,否则先用普通的 kill (发送SIGTERM),给它个体面的死法。

---

管道与文本处理三剑客:grep, awk, sed实战案例

这一章是Shell脚本的精髓,也是最能体现你“技术含量”的地方。咱们常说的“文本处理三剑客”就是 grepawksed。配合管道 |,你能把一堆杂乱无章的日志玩出花来。

管道 `|` 的魔力

管道就像流水线上的传送带。左边命令的输出,直接变成右边命令的输入。

比如:我想知道当前目录下有多少个文件?

ls -l | wc -l

ls -l 列出文件,wc -l 数行数。这一串组合拳,比写个循环去数要快多了。

`grep`:过滤大师

grep 负责按条件过滤。

面试常考:如何用 grep 统计日志中 "ERROR" 关键词的出现次数?

# -i 忽略大小写,-c 统计次数 grep -i "ERROR" /var/log/app.log | wc -l # 或者直接用 -c grep -i -c "ERROR" /var/log/app.log

`awk`:格式化与统计

如果说 grep 是过滤器,awk 就是处理机。它特别擅长处理列状数据(比如CSV或者日志)。

假设有个日志文件 access.log,内容是 IP 时间 请求路径,我们要统计哪个IP访问最多:

# $1 代表第一列(IP),sort排序,uniq -c 去重计数 awk '{print $1}' access.log | sort | uniq -c | sort -nr | head -10

这行命令是经典中的经典,能列出访问量前10的IP。

`sed`:流编辑器

sed 主要用来替换文本,而且是不打开文件直接改(或者输出到新文件)。

比如,你想把配置文件里的 localhost 批量换成 127.0.0.1

# 这只是预览,不会真的改文件 sed 's/localhost/127.0.0.1/g' config.conf # 真的要改文件,加 -i 参数(in-place) sed -i 's/localhost/127.0.0.1/g' config.conf

关键点:sed -i 很危险,改错了就找不回来了。建议先备份,或者先用不带 -i 的命令看看输出对不对。

实战案例:分析Nginx日志

咱们来个综合实战。假设我们要从Nginx日志里找出今天访问量最高的5个页面(URL),并且排除掉静态资源(图片、css、js)。

#!/bin/bash LOG_FILE="/var/log/nginx/access.log" echo "Top 5 visited pages (excluding static files):" # 1. 用 awk 打印出 URL ($7 通常是请求路径) # 2. 用 grep -v 排除静态文件后缀 # 3. 排序去重统计 awk '{print $7}' $LOG_FILE | \ grep -v -E '\.(jpg|png|gif|css|js|ico)$' | \ sort | uniq -c | sort -nr | head -5

这个脚本里的 \ 是换行符,为了让命令看起来清晰。这就是Linux的魅力,把简单的工具组合起来,解决复杂的问题。

📖 学习建议

脚本里尽量用绝对路径。 比如写 grep 的时候,虽然系统能找到,但在Cron定时任务里跑的时候,环境变量可能没加载,导致 command not found。写 /bin/grep 或者 /usr/bin/awk 虽然麻烦点,但更稳。另外,处理文本时,注意文件编码,Windows下编辑的脚本传到Linux里经常因为换行符(\r\n vs \n)报错,用 dos2unix 工具修一下就好。

Shell脚本编程入门:变量、条件判断与循环语法

简单来说,Shell脚本就是把你平时在终端敲的一堆命令,按顺序写到一个文件里,让系统自动帮你跑。咱们现在主流的服务器环境,用的Shell基本都是 Bash 5.2(2022年9月发布的稳定版),虽然Linux内核都更新到6.8了(2024年3月发布),但Shell脚本的语法标准还是遵循 POSIX Shell 规范,所以你学会了这些,在绝大多数Linux发行版上都能跑得通,不用担心兼容性问题。

很多新手一上来就被Shell的语法劝退,其实核心就三块:变量、判断、循环

变量:别乱加空格!

这是新手最容易踩的坑。在Shell里,赋值的时候等号两边绝对不能有空格

# 错误示范 name = "Tom" # 这会报错,Shell会把name当成命令执行 # 正确示范 name="Tom" echo $name

除了自己定义的变量,系统还内置了一些特殊变量,面试特别爱问,记一下:

条件判断:中括号两边要留空格

Shell里的条件判断用 if 语句,但那个中括号 [ ] 其实是一个命令,所以两边必须留空格,不然Shell会懵逼。

#!/bin/bash # 这是一个简单的判断脚本 file_path="/tmp/test.txt" # 注意 [ 后面和 ] 前面都有空格 if [ -f "$file_path" ]; then echo "文件存在,准备读取..." else echo "文件不存在,正在创建..." touch "$file_path" fi

常用的判断条件还有 -d(是不是目录)、-eq(数字相等)、-ne(数字不等)。

循环:批量干活的神器

当你需要重复做一件事,比如批量重命名文件,循环就派上用场了。最常用的是 for 循环。

#!/bin/bash # 批量创建10个测试文件 echo "开始批量创建文件..." # 这里的 {1..10} 是Bash的序列生成语法 for i in {1..10}; do touch "file_$i.txt" echo "创建了 file_$i.txt" done echo "搞定!"

💡 经验总结:写脚本的时候,养成在开头加上 #!/bin/bash 的习惯,这叫Shebang,它告诉系统用哪个解释器来执行。另外,如果你打算把脚本给别人用,或者跑在严格的环境里,建议加上 set -e,这样一旦脚本里某条命令报错了,脚本会立刻停止,防止错误像滚雪球一样越滚越大。

---

自动化运维实战:日志清理与cron定时备份脚本

作为全栈工程师,服务器运维是躲不掉的。最烦人的就是磁盘被日志塞满,或者数据没备份导致背锅。咱们用Shell脚本结合 cron 定时任务,就能把这些脏活累活全自动化了。

场景一:自动清理过期日志

Nginx或者应用跑久了,日志文件动不动几个G。咱们写个脚本,自动删掉7天前的 .log 文件。

#!/bin/bash # 脚本名:clean_logs.sh # 功能:清理 /var/log/myapp 目录下7天前的日志 LOG_DIR="/var/log/myapp" # 这里用到了 find 命令,-mtime +7 表示修改时间超过7天,-type f 表示只找文件 # -name "*.log" 是匹配规则,-delete 是直接删除 echo "$(date): 开始清理日志..." >> /var/log/cleanup.log find "$LOG_DIR" -type f -name "*.log" -mtime +7 -delete if [ $? -eq 0 ]; then echo "$(date): 日志清理完成。" >> /var/log/cleanup.log else echo "$(date): 日志清理失败,请检查权限。" >> /var/log/cleanup.log fi

场景二:定时备份数据目录

光清理不行,重要的数据还得备份。咱们写一个备份 /data 目录的脚本,并且加上日期戳,防止覆盖。

#!/bin/bash # 脚本名:backup_data.sh # 功能:将 /data 目录打包备份到 /backup SRC_DIR="/data" BACKUP_DIR="/backup" # 获取当前日期,格式如 2024-05-21 DATE=$(date +%Y-%m-%d) BACKUP_FILE="data_backup_$DATE.tar.gz" # 检查备份目录是否存在,不存在就创建 if [ ! -d "$BACKUP_DIR" ]; then mkdir -p "$BACKUP_DIR" fi echo "开始备份 $SRC_DIR ..." # 使用 tar 命令打包压缩 tar -czf "$BACKUP_DIR/$BACKUP_FILE" "$SRC_DIR" if [ $? -eq 0 ]; then echo "备份成功!文件保存在:$BACKUP_DIR/$BACKUP_FILE" # 顺便删掉30天前的旧备份,省空间 find "$BACKUP_DIR" -name "data_backup_*.tar.gz" -mtime +30 -delete else echo "备份失败!快去看看咋回事!" exit 1 fi

结合 cron 定时跑起来

脚本写好了,怎么让它每天自动跑?这就得用到 cron 了。在终端输入 crontab -e 编辑定时任务。

# 每天凌晨2点执行备份脚本 0 2 * * * /bin/bash /path/to/backup_data.sh # 每天凌晨3点执行日志清理脚本 0 3 * * * /bin/bash /path/to/clean_logs.sh

📖 学习建议:千万别直接把命令扔进 cron 就不管了。cron 执行的时候环境变量和你在终端里不一样,所以脚本里尽量用绝对路径(比如写 /bin/tar 而不是 tar)。还有,一定要测试脚本在终端能跑通了,再放进 cron,不然到时候报错你都不知道,因为 cron 默认是不给你发邮件提示的(除非你配置了)。

---

面试中常被问到:Shell陷阱与容器化轻量改造

写了这么多年脚本,我见过太多人因为一些细节翻车。这一章咱们聊聊那些让你加班到半夜的“坑”,以及现在云原生时代(K8s、Docker)下,Shell脚本该怎么写才显得专业。

那些年我们踩过的坑

`bash

file="my document.txt"

# 错误:rm 会试图删除 my 和 document.txt 两个文件

# rm $file

# 正确:

rm "$file"

`

`bash

# 方法一:加个 grep -v grep 过滤掉自己

ps aux | grep nginx | grep -v grep

# 方法二:用正则的中括号技巧(更优雅)

ps aux | grep [n]ginx

# 原理是 grep 在搜 [n]ginx 时,进程名是 grep [n]ginx,正则匹配不上自己

`

面试核心:统计日志里的ERROR

面试官特别喜欢问:“怎么统计日志里ERROR出现了多少次?”

直接用 grep-c 参数就行,简单粗暴。

# -c 参数直接输出匹配行的数量 grep -c "ERROR" /var/log/app.log # 如果要统计不区分大小写,加 -i grep -i -c "error" /var/log/app.log

容器化时代的轻量改造

现在大家都用Docker和K8s了,在容器里跑Shell脚本,跟在物理机上不一样。现在的趋势是 轻量化减少依赖

以前我们喜欢在脚本里用 awksed 做复杂的文本处理,但在容器里,为了追求镜像小,基础镜像(比如 alpine)可能连这些工具都没有,或者版本很老。

改造建议

#!/bin/bash # 容器友好的脚本示例 # 定义一个清理函数 cleanup() { echo "收到停止信号,正在优雅退出..." # 这里放停止服务、保存状态的代码 exit 0 } # 捕获 SIGTERM 和 SIGINT 信号 trap cleanup SIGTERM SIGINT echo "服务启动中..." # 模拟一个前台进程,比如启动一个Web服务 # 这里用 tail -f 模拟,实际可能是 java -jar xxx.jar tail -f /dev/null & # 等待信号 wait $!

🔧 实战技巧:在写自动化运维脚本时,如果逻辑特别复杂(比如涉及到复杂的API调用、JSON解析),别硬用Shell死磕。虽然Shell能写,但代码会很难看且难维护。这时候划清界限,用Python或者Go写工具,Shell只负责调用和串联,这才是2024年最主流的做法。