Shell编程基础
Shell编程基础
复习定位
Shell脚本是将多条Shell命令写入一个文件并顺序执行——自动完成重复性任务。变量、条件判断、循环和函数让Shell从单纯的"命令解释器"变成一门脚本语言。熟悉Shell脚本对系统管理、日志分析、容器部署(CICD pipeline)都必不可少——多数编排工具(Ansible初步, Docker CMD, Kubernetes的init container)都使用Shell作为配置语言。
Shell脚本的结构
一个Shell脚本文件通常以#!(shebang)开头——后面跟解释器的路径——#!/bin/bash。#!告诉操作系统该由哪个程序来解释这个脚本。尽管shell也可以默认在子进程中直接运行——但不加shebang的脚本在./script.sh的方式运行时可能会被当前用户Shell不支持跨shell语法。执行脚本的方式——bash script.sh 或先加执行权限chmod +x script.sh后./script.sh。
#!/bin/bash
# 这是我的第一个脚本
echo "Hello, Shell!"变量
定义变量——name="value"——注意等号两侧不能有空格。引用变量——$name或${name}(加花括号便于边界识别)。变量的作用域——默认是全局的(对当前Shell和它的子进程可用)——但子进程对它的修改不影响父Shell的原始值。
export name——将变量提升为环境变量——随脚本启动的子进程都会继承它而无需逐个传参。
位置变量——$1,$2,...$9代表脚本的第1-9个参数——$@代表所有参数(视为独立字符串)——$*将所有参数视为一个字符串——$#参数个数——$0脚本名称——$?上一条命令的返回码(0=成功,非0=失败)。
条件判断
if [ condition ]; then ... elif ... else ... fi。注意[ 前后有空格——]前也有空格——这是调用test命令的等同形式。test -f /etc/passwd等价于[ -f /etc/passwd ]。
常用判断条件:
[-d path]:是否是目录[-f path]:是否是普通文件[-r/-w/-x path]:文件是否有读/写/执行权限[-z/-n str]:字符串是否为空/非空[str1 == str2]:字符串相等[num1 -gt/-lt/-eq/-ne num2]:数值比较
循环
for循环:for var in list; do ...; done。
for file in *.txt; do
echo "Processing $file"
donewhile循环:while [ condition ]; do ...; done。常用于读取文件或服务器不断输出日志流。
计数器while read line; do process "$line"; done < input.txt。
函数
function my_func {
echo "参数1: $1"
echo "参数2: $2"
return 0
}
my_func "hello" "world"函数在Shell脚本中必须先定义后调用——Shell本身是解释一行执行一行。在函数外的命令可以调用函数内的变量和全局变量——但通过local声明的变量仅在函数内部可见(不影响函数外部的同名变量)。
脚本调试
在脚本开头添加set -x——输出每一条要执行的命令(前面加+号)以追踪执行路径——这在复杂的条件判断和循环中非常有用以观察脚本变量具体在哪个判断被展开。set -e——有任何命令返回非0立即退出脚本——防止在命令失败后继续执行叠加更多的错误。set -u——使用未设置变量时报错——帮助发现变量名拼写错误。set -o pipefail——管道中的任何一条命令失败都算整个管道失败。这三者通常在脚本开头集中使用set -euo pipefail。
复习检查
Shell脚本的
$@和$*都表示所有参数——区别是什么?"$@"和$@——默认的引号效果差异的典型使用——思考args="$@"与args = $*`的区别?[ -f /path ]和[ ! -f /path ]的含义——!做什么?怎样判断一个文件不存在同时也不会是目录?for循环遍历
*.txt时——如果当前目录没有.txt文件——循环体执行几次?这时避免零结果的方法是什么(shopt -s nullglob的解释)?return和exit在函数中的差异——函数内return结束函数并将控制权返回给调用者——exit则终止整个脚本。set -e下——一个命令的返回码非0——但有时你希望容忍它的失败(比如防止一个dpkg查询不存在的包返回1时的中止)--在命令行后加上什么可以跳过set -e的限限检查?