第1章-Shell编程规范与变量-学习笔记_d35dd1bb
第1章 Shell编程规范与变量 —— 学习笔记
实验环境:OpenEuler 24.03,默认 Shell 为 /bin/bash(/bin/sh 是 /bin/bash 的软链接,保证向下兼容)
0. 本章框架(一图看懂)
Shell 脚本基础 ──► 编程规范 + 3种执行方式
I/O 控制 ──► 重定向(> >> < 2> &>) + 管道(|)
变量体系 ──► 自定义 / 环境 / 只读 / 位置 / 预定义
文本三剑客(入门) ► sort(排序) + uniq(去重计数) + tr(字符处理)
技能目标:①掌握脚本核心规范与执行逻辑;②熟练使用各类变量与数组;③精通重定向、管道原理与应用;④掌握 sort/uniq/tr 高级用法与组合场景。
1. Shell 脚本基础
1.1 脚本编程规范(四条核心规范)
| 规范要求 | 说明 | 示例 |
|---|---|---|
| 脚本声明(首行) | #!/bin/bash 指定解释器,必选 | #!/bin/bash |
| 注释说明 | # 开头,标注功能、作者、日期、参数 | # 功能:批量检测服务器端口状态 |
| 命令规范性 | 命令小写;路径用绝对路径(避免环境变量问题) | cd /boot && ls -lh |
| 可执行权限 | chmod +x 或直接用解释器执行 | chmod +x script.sh |
标准脚本示例(first.sh):
#!/bin/bash
# 脚本名称: first.sh
# 功能: 查看 /boot 目录下的内核文件信息
# 系统环境: OpenEuler 24.03
cd /boot
echo "【OpenEuler 24.03】当前目录为:"
pwd
echo "【OpenEuler 24.03】/boot 目录下以 vml 开头的内核文件:"
ls -lh vml*
1.2 三种执行方式(必考对比)
| 执行方式 | 命令 | 适用场景 | 权限要求 |
|---|---|---|---|
| 绝对/相对路径执行 | ./first.sh | 日常手动执行、脚本调度 | 需 x 权限 |
| 解释器直接执行 | bash first.sh / sh first.sh | 无 x 权限、跨 Shell 兼容 | 无需 x 权限 |
| source/点号加载 | source first.sh / . first.sh | 让脚本修改当前 Shell 环境(如定义全局变量) | 无需 x 权限 |
🛡️ 企业提示:禁止直接修改系统自带 Shell 脚本(如
/etc/rc.d/下),改前必须备份。
2. 重定向与管道
2.1 标准 I/O 与文件描述符(底层基础)
| 标准 I/O | 文件描述符 | 默认设备 | 作用 |
|---|---|---|---|
| 标准输入 STDIN | 0 | 键盘 | 命令读取输入 |
| 标准输出 STDOUT | 1 | 显示器 | 正常结果输出 |
| 标准错误 STDERR | 2 | 显示器 | 错误信息输出 |
本质:重定向与管道就是修改文件描述符的关联设备,实现数据定向传输。
2.2 重定向
| 符号 | 含义 | 说明 |
|---|---|---|
> (= 1>) | 覆盖输出重定向 | 文件不存在则新建,存在则覆盖 |
>>(= 1>>) | 追加输出重定向 | 保留原内容 |
< (= 0<) | 输入重定向 | 命令从文件读输入,实现非交互式执行 |
2> / 2>> | 错误(覆盖/追加)重定向 | 结果与错误分离 |
&> | 混合重定向 | 1 和 2 同时定向,等价 > file 2>&1;&> /dev/null = 丢弃全部输出 |
典型企业场景:
uname -p > kernel_arch.txt # 记录结果
uname -r >> kernel_arch.txt # 追加结果
echo "OpEuler@2024" > pass.txt
useradd jerry
passwd --stdin jerry < pass.txt # 输入重定向:非交互批量设密码
tar zcf /nonedir/etc.tgz /etc/ 2> backup_error.log # 采集错误日志
./configure --prefix=/usr/local/nginx &> /dev/null # 忽略无关输出
注:OpenEuler 中
passwd --stdin若失败,可能与 SELinux 策略有关,可临时setenforce 0。
2.3 管道 |
- 作用:将左侧命令的标准输出作为右侧命令的标准输入,链式协同处理。
- 关键点:只传递 STDOUT,STDERR 不经过管道(需先重定向)。
- 原理:数据暂存管道缓冲区,流式传输,不生成临时文件,效率高于重定向中转。
- 支持多级串联:
cmd1 | cmd2 | cmd3
高频组合示例:
# 提取用户名+登录Shell
grep "/bin/bash$" /etc/passwd | awk -F: '{print $1,$7}'
# 根分区使用率,如 11%
df -hT | grep "/$" | awk '{print $6}'
# 统计进程数(-v grep 排除grep自身)
ps -ef | grep nginx | grep -v grep | wc -l
3. Shell 变量
变量按类型分为:自定义变量、环境变量、只读变量、位置变量、预定义变量。
3.1 自定义变量(局部变量)
定义三条强制规则:
- 变量名以字母/下划线开头,后跟字母/数字/下划线;禁止特殊字符与空格;
=两边不能有空格(空格是命令分隔符);- 值含空格/特殊字符时用双/单引号括起。
#正确
Product=OpenEuler
Env="Production Server"
#错误
Product = OpenEuler
2Version=24.03
引用与边界界定:
echo $Product # OpenEuler
echo $Product24.03 # 输出 .03 —— 被识别为未定义变量 $Product24
echo ${Product}24.03 # OpenEuler24.03 —— 用 {} 明确变量边界
赋值特殊操作:
| 方式 | 特点 | 示例 |
|---|---|---|
双引号 ""弱引用 | 解析 $ 变量引用和 \ 转义 | Env="${Product} Production Env" |
单引号 '' 强引用 | 一切字符原样,禁止变量解析 | Env='${Product} $Version' |
| 命令替换 | 命令的 STDOUT 作为变量值 | p=$(which useradd)(推荐,支持嵌套)/ p=`which useradd` |
read 交互赋值 | 从键盘读入 | read -p "请输入备份目录:" Backup_Dir |
# 嵌套命令替换(只有 $() 支持):查 ls 命令所属包的配置文件
Ls_Conf=$(rpm -qc $(rpm -qf $(which ls)))
作用域:局部 → 全局(export)
Product=OpenEuler # 仅当前 Shell 有效,进子 Shell(bash) 后为空
export Product Version # 导出为全局变量,子 Shell 可用
export FQDN=www.xxx.com # 一步创建并导出
export只在当前会话有效;要永久生效须写入/etc/profile(全局)或~/.bash_profile(当前用户)。
3.2 数值运算(expr)
Bash 默认只支持整数运算(小数要用 bc/awk)。
X=35; Y=16
expr $X + $Y # 51 【注意运算符两侧必须有空格】
expr $X \* $Y # 560 【乘法必须转义 \*】
expr $X / $Y # 2 除法取整
expr $X % $Y # 3 取模
Ycube=$(expr $Y \* $Y \* $Y) # 结果赋值需命令替换 → 4096
3.3 环境变量(系统预定义的全局变量,名通常大写)
查看:env(仅环境变量)/ set(全部变量)。核心必记:
| 变量 | 作用 |
|---|---|
USER | 当前登录用户名 |
HOME | 当前用户宿主目录 |
PWD | 当前工作目录 |
PATH | 命令搜索路径(冒号分隔,按序查找,找不到报"未找到命令") |
LANG | 语言与字符集,如 zh_CN.UTF-8 |
SHELL | 登录 Shell |
HISTSIZE | 历史命令条数(默认 1000) |
PATH 实战——让脚本任意目录可执行(两种方法,推荐第2种):
# 方法1:把脚本放进 PATH 已含目录(如 /usr/local/bin)
# 方法2:把脚本目录追加进 PATH(临时生效)
PATH="$PATH:/root/scripts"
永久配置环境变量:
| 配置文件 | 作用域 | 场景 |
|---|---|---|
/etc/profile | 所有用户(全局) | 系统级 PATH、LANG |
~/.bash_profile | 仅当前用户 | 个人脚本目录、HISTSIZE |
vim ~/.bash_profile # 末尾加 export HISTSIZE=200
source ~/.bash_profile # 立即生效,无需重启终端
3.4 只读变量
Log_Path=/var/log/xx.log
readonly Log_Path # 设定后不可改、不可删(unset 也删不掉),关闭会话即失效
readonly -p # 查看只读变量
用途:固定脚本核心参数(路径、安装目录),防误改。
3.5 位置变量(脚本命令行传参)
$1~$9:脚本名后第 1~9 个参数;超过 9 个用${10}、${11};$0是脚本名(属预定义变量)。
#!/bin/bash # adder2num.sh
SUM=$(expr $1 + $2)
echo "【OpenEuler 加法运算】$1 + $2 = $SUM"
# ./adder2num.sh 12 34 → 【OpenEuler 加法运算】12 + 34 = 46
3.6 预定义变量(只读、专为脚本设计,必背)
| 变量 | 作用 | 应用场景 |
|---|---|---|
$# | 位置参数个数 | 参数个数校验 |
$* | 所有位置参数(作为一个整体) | 批量处理所有参数 |
$? | 前一条命令的返回状态,0=成功,非0=失败 | 脚本容错的核心(几乎每个企业脚本都用) |
$0 | 当前脚本名称 | 日志输出、帮助提示 |
综合实战(备份脚本 mybak.sh,串起 $*、$#、$0、时间戳、混合重定向):
#!/bin/bash
TARFILE=openeuler-bak-$(date +%s).tgz # UNIX 时间戳保证压缩包名唯一
tar zcf $TARFILE $* &> /dev/null # 批量备份所有传入参数
echo "已执行脚本:$0"
echo "共备份对象个数:$# 个"
echo "备份对象列表:$*"
echo "备份压缩包:$TARFILE"
4. 文本处理工具 sort / uniq / tr
4.1 sort —— 按行排序
语法:sort [选项] 文件 或 命令 | sort [选项]。默认按字符字典序(数字大小排序必须加 -n,否则 10 排在 2 前)。
| 选项 | 作用 |
|---|---|
-f | 忽略大小写 |
-b | 忽略行首空格 |
-n | 数值排序 |
-r | 反向(降序) |
-u | 排序并去重 |
-t | 指定分隔符(默认 Tab) |
-k | 指定排序列(配合 -t) |
-o 文件 | 结果输出到文件(替代 >) |
-M | 按月份排序(Jan–Dec) |
sort /etc/passwd # 基础字符排序
sort -t: -nrk 3 /etc/passwd # 按 UID(第3列·冒号分隔)降序
sort -t: -nk 3 /etc/passwd -o user_uid.txt
sort -nu num.txt # 数值升序 + 去重
4.2 uniq —— 去重(必须搭配 sort)
uniq 只对"连续"重复行生效 → 标准姿势:sort 文件 | uniq [选项]。
| 选项 | 作用 |
|---|---|
-c | 去重并计数(行首显示次数) |
-d | 仅显示重复的行 |
-u | 仅显示只出现一次的行 |
sort testfile | uniq # 去重
sort testfile | uniq -c # 3 CentOS 7 / 3 OpenEuler 24.03 / 2 Ubuntu 22.04
sort testfile | uniq -d # 找重复数据
sort testfile | uniq -u # 提取唯一数据(如单独出现1次的 SUSE 15)
4.3 tr —— 字符流处理(只读标准输入/管道,不能直接处理文件)
语法:命令 | tr [选项] 字符集1 [字符集2];字符集可用单字符、范围(a-z、0-9)、特殊字符(\n、\t)。
替换规则:两字符集长度建议一致;字符集1更长时,超出部分用字符集2的最后一个字符替换。
| 选项 | 作用 |
|---|---|
-d | 删除字符集1中所有字符 |
-s | 压缩连续重复字符,只留一个 |
-c | 取补集(匹配不属于字符集1的字符) |
-t | 截断字符集1至与字符集2等长(默认行为) |
echo "OPENEULER 24.03" | tr 'A-Z' 'a-z' # 大写→小写
echo "thissss is ..." | tr -s 'sn' # 压缩连续 s/n
echo "hello world" | tr -s ' ' # 压缩连续空格
echo 'hello world' | tr -d 'od' # 删除 o、d → hell wrl
echo "OpenEuler 24.03" | tr -d '0-9' # 删除所有数字
echo "..." | tr -c -d 'a-zA-Z ' # -c -d 组合:只保留字母和空格
cat testfile | tr '\n' ' ' # 多行→一行
cat data.tsv | tr '\t' ',' > data.csv # TSV→CSV
5. 高频考点 & 易错清单
考点自查(对应文档"本章核心考点")
- 脚本必备结构:
#!/bin/bash+ 注释 + 执行权限;3 种执行方式的区别(source 能改当前环境、bash 不需 x 权限)。 - 重定向五件套:
>、>>、<、2>、&>;文件描述符 0/1/2;管道只传 STDOUT。 - 变量赋值规则(= 无空格)、
${}边界、双/单引号区别、export、expr(空格、\*、仅整数)。 - 位置变量
$1~$9(超9个用${10})与预定义变量$#、$*、$?、$0的用途。 sort -n/-r/-t/-k/-u/-o、uniq -c/-d/-u(先 sort 后 uniq)、tr -s/-d/-c组合。
易错点速记
=两边有空格 → 报错;变量名数字开头 → 报错。$Product24会被当成一个整体变量名 → 用${Product}24。- 单引号完全不解析,双引号解析
$、\,反撇号/$()解析命令 → 优先级记忆。 - expr:运算符两侧必须空格,乘号写
\*,不支持小数。 - 数字排序忘加
-n→ “10 < 2” 的字典序陷阱。 - 直接
uniq file效果不对 → 必须sort file | uniq。 tr不接受文件名参数 →cat file | tr ...。$?紧跟被判断命令使用,中间插别的命令就被覆盖。export不写进配置文件,重启终端就丢。
6. 一页速查表
| 需求 | 命令/写法 |
|---|---|
| 查看系统支持的 Shell | cat /etc/shells |
| 脚本执行(无权限) | bash xx.sh |
| 脚本改变当前 Shell 环境 | source xx.sh 或 . xx.sh |
| 丢弃所有输出 | cmd &> /dev/null |
| 非交互设密码 | passwd --stdin user < pass.txt |
| 弱/强引用 | "含$变量" / '原样字符' |
| 取命令结果 | v=$(cmd)(可嵌套) |
| 全局变量 | export 名(本会话)/ 写入 profile(永久) |
| 整数运算 | expr $a \* $b(空格!) |
| 参数个数/全部/状态/脚本名 | $# / $* / $? / $0 |
| 按 UID 排序 | sort -t: -nk 3 /etc/passwd |
| 去重计数 | `sort f |
| 大小写转换 | tr 'A-Z' 'a-z' |
| 提取纯数字 | tr -c -d '0-9\n' |
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)