Shell编程基础入门指南
计算机只能识别二进制机器语言(如11000000这样的序列),无法直接理解人类编写的源代码。为了让程序能够在计算机上运行,需要借助编译器或解释器将源代码转换为机器可执行的指令。
编程语言的两种执行方式
编译型语言
这类语言在执行前需要经过专门的编译过程,将源代码转换为目标机器码。编译完成后,运行时直接执行编译后的结果,无需再次翻译。因此执行效率较高,但依赖于特定的编译器,跨平台能力相对较弱。典型代表包括C、C++等语言。
解释型语言
解释型语言不需要预先编译,程序运行时由解释器逐行翻译成机器语言,每执行一次都需要翻译一次。由于翻译过程贯穿整个运行阶段,因此执行效率相对较低。Python、JavaScript、Ruby以及Shell脚本都属于解释型语言。
执行方式对比总结
编译型语言执行速度更快,但跨平台能力较弱;解释型语言虽然执行效率稍低,但具有良好的跨平台兼容性。通常情况下,底层开发、系统程序和大型应用会选择编译型语言;而服务器脚本、跨平台工具等场景则更适合使用解释型语言。
Shell的概念解析
Shell是Linux系统中位于内核之上的命令解释层,它充当用户与操作系统内核之间的桥梁。Shell接收用户输入的命令,将其转换为内核能够理解的形式传递给内核执行,同时将执行结果呈现给用户。这种交互方式既可以是交互式的(从键盘直接输入命令并立即获得响应),也可以是非交互式的(通过脚本文件批量执行命令)。
从功能角度来看,Shell命令行解释器的主要作用是实现用户与内核的沟通。用户输入指令后,Shell将其转换为内核可识别的二进制格式,内核分析指令后向CPU发出进程调度和资源分配指令,最终由硬件设备展示执行结果。
Shell脚本的定义与用途
Linux环境下的Shell脚本是一个具有执行权限的文本文件,通过它可以自动化完成特定功能任务。简单来说,Shell脚本就是将需要执行的命令保存到文本文件中,然后按照预定顺序依次执行。由于Shell是解释型语言,因此无需进行编译过程。
Shell脚本的本质可以概括为:命令集合 + 脚本规范格式 + 特定语法结构 + 编程思想 = Shell脚本
Shell脚本的典型应用场景
日常运维中,Shell脚本常用于以下方面:
- 自动化软件部署(如LAMP、LNMP、Tomcat等环境的一键搭建)
- 系统初始化和批量管理(批量修改主机密码、推送公钥等)
- 日志分析处理(统计网站访问量、生成报表)
- 数据备份(数据库定期备份、日志转储)
- 系统监控(资源使用情况监控、告警触发)
Shell脚本学习路径
- 熟悉常用命令的功能和使用场景
- 掌握脚本的标准格式规范(魔术字节声明、正确的执行方式)
- 精通脚本基本语法(变量、条件判断、循环等核心知识)
学习的核心秘诀在于:先看懂模仿练习,最后独立思考编写。
脚本示例演示
示例一:基础日志清理脚本
cd /var/log
cat /dev/null > messages
echo "日志清理完成"
这个脚本存在的问题是没有权限检查和流程控制逻辑,不够完善。
示例二:带错误处理的日志清理脚本
#!/bin/bash
# 日志清理脚本增强版
LOG_DIR=/var/log
ADMIN_UID=0
# 检查是否以管理员权限运行
if [ "$UID" -ne "$ADMIN_UID" ]; then
echo "需要管理员权限才能执行此脚本"
exit 1
fi
cd $LOG_DIR || {
echo "无法切换到目标目录" >&2
exit 1
}
cat /dev/null > messages && echo "日志清理完成"
exit 0
该版本增加了用户身份验证、目录切换错误处理等机制,使脚本更加健壮。
清空日志文件的几种方式:
echo > test.log
> test.log
cat /dev/null > test.log
脚本执行原理
执行Shell脚本时会启动子Shell进程。从命令行启动的脚本会继承当前Shell的环境变量;而系统自动执行的脚本(非交互式启动)则需要自行定义所需的环境变量。
进程与Shell的关系
每个进程只能感知到内核和自身,无法直接了解其他进程的存在。进程是程序的一次执行实例,不同的进程即使名称相同,进程号也必然不同。多个用户可以同时登录系统,每个用户的Shell会话相互独立;同一用户也可以从不同终端登录,每个会话对应不同的Shell进程。父Shell中设置的变量不会自动传递给子Shell,反之亦然。
Shell与其他脚本语言的区别
Shell的优势在于处理操作系统底层任务,这得益于其丰富的命令生态(如grep、sed、awk等工具的配合)。对于一键安装、监控告警、日常运维任务等场景,Shell脚本开发更为高效简洁。
PHP、Python则更适合开发运维管理工具和Web界面应用。
Shell脚本特别擅长处理纯文本类型的数据,而Linux系统中几乎所有配置文件和日志文件(如Nginx、Apache、Rsync等服务的配置和日志)都是纯文本格式。掌握Shell脚本能够充分发挥Linux系统的潜力。
Linux系统支持的Shell类型
/bin/sh - Bourne Shell的符号链接
/bin/bash - Linux默认Shell,功能最为完善
/sbin/nologin - 非交互式Shell,禁止登录
/bin/dash - 轻量级Shell,执行效率高但功能较少
/bin/tcsh - csh的增强版本,完全兼容csh
/bin/csh - C语言风格的Shell
Shell脚本语言属于弱类型语言,常用的标准Shell包括Bourne Shell(sh)和C Shell(csh),其中Bourne Shell已被bash取代。
查看当前系统默认Shell:
echo $SHELL
# 输出:/bin/bash
grep root /etc/passwd
# 输出:root:x:0:0:root:/root:/bin/bash
Bash Shell的基本特性
Bash提供了丰富的功能特性,包括命令补全、快捷键操作、命令历史、别名定义、标准输入输出重定向和管道操作等。
命令补全功能
- 命令补全:在PATH环境变量指定的路径中搜索以输入字符串开头的可执行文件。如果找到多个匹配项,按两次Tab键显示完整列表;否则直接补全。
- 路径补全:根据给定的路径前缀自动补全文件名。
常用快捷键
| 快捷键 | 功能说明 |
|---|---|
| Ctrl+a | 跳至命令行首 |
| Ctrl+e | 跳至命令行尾 |
| Ctrl+u | 删除光标至行首的内容 |
| Ctrl+k | 删除光标至行尾的内容 |
| Ctrl+l | 清屏 |
| Ctrl+c | 终止前台进程 |
| Ctrl+z | 暂停前台进程并放入后台 |
| Ctrl+d | 退出Shell |
| Ctrl+r | 搜索历史命令 |
通配符使用
*- 匹配任意数量的字符?- 匹配单个任意字符[list]- 匹配指定列表中的任意单个字符[!list]- 匹配除指定列表外的任意单个字符{str1,str2,...}- 匹配多个指定字符串之一
示例演示:
touch file{1..3} # 创建file1, file2, file3
touch file{1..13}.jpg # 创建file1.jpg到file13.jpg
ls file* # 列出所有file开头的文件
ls *.jpg # 列出所有jpg文件
ls file? # 列出file后跟单个字符的文件
ls file[0-9].jpg # 列出file0.jpg到file9.jpg
引号的区别与用法
- 双引号(""):保留引号内内容的整体性,但允许通过$符号引用变量
- 单引号(''):将内容作为整体看待,禁止变量引用,特殊字符均视为普通字符
- 反引号(``):与$()功能相同,用于执行命令并获取输出,嵌套使用时反引号可能出现问题
示例对比:
echo "$(hostname)"
# 输出:server
echo '$(hostname)'
# 输出:$(hostname)
echo $(date +%F)
# 输出:2024-01-15
echo `echo $(date +%F)`
# 输出:2024-01-15
命令历史机制
系统默认保存最近执行的1000条命令历史,记录位置为~/.bash_history。
相关环境变量:
- PATH:命令搜索路径
- HISTSIZE:历史记录缓冲区大小
history命令常用选项:
-c:清空命令历史-d OFFSET [n]:删除指定位置的命令-w:保存历史记录到文件
历史命令调用技巧:
!n:执行第n条历史命令!-n:执行倒数第n条历史命令!!:执行上一条命令!string:执行最近以指定字符串开头的命令!$:引用上一条命令的最后一个参数
命令别名机制
定义语法:
alias CMDALIAS='COMMAND [options] [arguments]'
特性说明:在Shell中定义的别名仅在当前Shell会话中有效,别名作用范围仅限于当前进程。
相关操作:
alias # 查看已定义的别名
unalias 别名 # 删除指定别名
unalias -a # 清除所有别名
alias 新别名='实际命令' # 定义新别名
别名持久化配置:
vim ~/.bashrc # 仅对当前用户生效
vim /etc/bashrc # 对所有用户生效
标准输入输出与重定向
三大标准流
| 类型 | 设备文件 | 文件描述符 | 默认设备 |
|---|---|---|---|
| 标准输入 | /dev/stdin | 0 | 键盘 |
| 标准输出 | /dev/stdout | 1 | 显示器 |
| 标准错误 | /dev/stderr | 2 | 显示器 |
重定向操作
防止意外覆盖:
set -C # 禁止对已存在文件使用覆盖重定向
>| # 强制覆盖输出
set +C # 关闭上述保护功能
重定向类型说明:
| 类型 | 操作符 | 用途 |
|---|---|---|
| 输入重定向 | < | 将输入来源从键盘改为指定文件 |
| 输出重定向 | > | 将正常输出保存到文件(覆盖) |
| 输出重定向 | >> | 将正常输出追加到文件 |
| 错误重定向 | 2> | 将错误信息保存到文件(覆盖) |
| 错误重定向 | 2>> | 将错误信息追加到文件 |
| 混合重定向 | &> | 同时重定向正确和错误输出到同一文件 |
| 混合重定向 | &>> | 追加方式重定向正确和错误输出 |
管道操作
管道将前一个命令的输出作为后一个命令的输入:
命令1 | 命令2 [| 命令n]
典型应用示例:
pgrep -l "." | wc -l # 统计运行中的进程总数
ifconfig | grep "HWaddr" # 获取本机MAC地址
命令组合逻辑
命令1 ; 命令2 # 顺序执行,无逻辑关联
命令1 && 命令2 # 仅当命令1成功时执行命令2
命令1 || 命令2 # 仅当命令1失败时执行命令2
Shell脚本的创建与执行
Shell脚本由Linux命令、Bash内置命令、流程控制语句和注释组成。
脚本开头的解释器声明
规范脚本第一行需指定解释器路径:
#!/bin/bash
# 或
#!/bin/sh
#!称为魔术数字(shebang),内核根据它来确定使用哪个程序解释执行脚本内容。此行必须位于脚本最前端,否则会被视为普通注释。
为提高兼容性,也可使用env命令:
#!/bin/env bash
不同脚本语言的标准开头:
#!/bin/sh
#!/bin/bash
#!/usr/bin/awk
#!/bin/sed
#!/usr/bin/perl
#!/usr/bin/env python
CentOS和RedHat系统默认Shell为bash。建议即使使用默认bash,也养成添加shebang的习惯。若未指定解释器,则需手动使用对应解释器执行:
bash test.sh
python test.py
脚本注释规范
#符号后的内容为注释,注释不会被执行,仅供阅读理解。良好的注释习惯有助于团队协作和个人后续维护。
常用注释格式:
# 脚本名称
# 功能描述
# 存放路径
# 使用方法
# 更新记录
三种脚本执行方式
方式一:使用解释器直接运行
bash script-name.sh
# 或
sh script-name.sh
适用于脚本文件无执行权限或未指定解释器的情况。
方式二:直接执行脚本文件
/path/script-name.sh
# 或
./script-name.sh
需要先添加执行权限:
chmod u+x script-name.sh
chmod 755 script-name.sh
方式三:source或点号加载执行
source script-name.sh
# 或
. script-name.sh
此方式不需要执行权限,且脚本中的变量和函数会在当前Shell中生效,而非创建子Shell执行。
三种执行方式的差异演示
创建测试脚本:
cat > test.sh
echo '当前目录:' `pwd`
使用方式一执行:
sh test.sh
echo $userdir
# 输出:空(变量未传递到当前Shell)
使用方式三执行:
source test.sh
echo $userdir
# 输出:/home/user(变量成功传递到当前Shell)
结论:source和点号方式执行脚本时,脚本中定义的变量和函数会在当前Shell中保持有效;而bash/sh方式执行则会启动子Shell,脚本结束后变量无法保留。
系统服务脚本中常见这种用法:
. /etc/rc.d/init.d/functions
Shell脚本开发规范
- 指定解释器:脚本开头必须声明shebang
- 添加版权信息:包含日期、作者、功能说明、版本号等元数据
- 使用英文注释:避免中文乱码问题
- 统一文件扩展名:建议使用.sh作为脚本文件后缀
- 成对书写语法元素:如
[]、{}、''、""、$()等需一次性成对写出,防止遗漏 - 保持适当缩进:通过缩进提高代码可读性
- 流程控制语句整体构建:先写完if/for/while等结构框架,再填充具体逻辑
示例规范写法:
if 条件判断
then
执行内容
fi
for 变量 in 列表
do
循环体
done
遵循这些规范能够显著减少编程错误,提高脚本质量和工作效率。
