shell脚本变量记录
1.$0
表示当前脚本本身的路径(包含文件名),示例:如果脚本在 /home/user/projects/bin/start.sh,那么 $0 就是 /home/user/projects/bin/start.sh
在 Shell 脚本/函数中,参数列表是从 $1 开始的,$0 是特殊的,它不属于参数列表。
$0 vs $1 的核心区别
| 变量 | 含义 | 示例值 | 归属 |
|---|---|---|---|
$0 |
脚本/函数本身的名称(或路径) | ./deploy.sh 或 deploy |
永远不是参数 |
$1 |
第一个传入的参数 | v2.0.0 |
参数列表的第 1 个 |
$2 |
第二个传入的参数 | prod |
参数列表的第 2 个 |
$3 |
第三个传入的参数 | 依此类推 | 参数列表的第 3 个 |
$@ |
所有参数(用空格分隔) | v2.0.0 prod |
所有参数的集合 |
$# |
参数的个数 | 2 |
参数总数 |
在任何 Shell 脚本或函数中,参数列表永远是从 $1 开始计数的,$0 是保留给"当前脚本/函数名称"的特殊变量。
$0不属于参数列表,它不会被$#计入总个数。$1是第一个参数,$2是第二个参数,依此类推。
示例 1:脚本(文件名为 test.sh)
#!/bin/bash
echo "\$0 = $0"
echo "\$1 = $1"
echo "\$2 = $2"
echo "\$3 = $3"
echo "参数总数 = $#"
执行命令:
./test.sh v2.0.0 prod
输出结果:
$0 = ./test.sh # 脚本名称(不是参数!)
$1 = v2.0.0 # 第一个参数
$2 = prod # 第二个参数
$3 = # 没有第三个参数,为空
参数总数 = 2 # 只有 2 个参数($1 和 $2)
2.dirname $0
dirname 命令用于提取路径的目录部分(去掉文件名)
dirname /home/user/projects/bin/start.sh # 输出 /home/user/projects/bin
dirname ./bin/start.sh # 输出 ./bin
3.SCRIPTNAME=$(basename $0)
SCRIPTNAME= 定义一个变量,名为 SCRIPTNAME
$(...) 命令替换,先执行括号内的命令,将输出结果赋值给变量
basename 一个 Linux 命令,用于去除路径中的目录部分,只保留最后的文件名
$0 Shell 特殊变量,表示当前脚本的完整路径和名称(启动脚本时传入的第一个参数)
示例脚本:
#!/bin/bash
# 获取脚本名称
SCRIPTNAME=$(basename $0)
# 获取脚本所在目录的父级目录
BASEDIR=$(cd $(dirname $0)/..; pwd -P)
# 检查配置文件是否存在
CONFIG_FILE="$BASEDIR/config/application.yml"
if [ ! -f "$CONFIG_FILE" ]; then
echo "[$SCRIPTNAME] 错误:配置文件不存在 $CONFIG_FILE"
exit 1
fi
# 记录启动日志
echo "[$SCRIPTNAME] 启动时间: $(date)"
echo "[$SCRIPTNAME] 项目目录: $BASEDIR"
# 启动应用
echo "[$SCRIPTNAME] 正在启动服务..."
执行结果(假设脚本名为 start.sh):
假设你的脚本文件位于 /home/user/projects/bin/start.sh
[start.sh] 启动时间: Thu Sep 4 10:00:00 CST 2026
[start.sh] 项目目录: /home/user/myproject
[start.sh] 正在启动服务...
在实际脚本中,basename 和 dirname 通常配合使用:
# 获取脚本所在的目录
SCRIPT_DIR=$(dirname $0) # 例如:/home/user/projects/bin
# 获取脚本名称
SCRIPT_NAME=$(basename $0) # 例如:start.sh
# 获取项目根目录(父级目录)
BASEDIR=$(cd $SCRIPT_DIR/..; pwd -P)
4.if [[ -f ${BASEDIR}/bin/run.conf ]]; then
整体含义: 如果 ${BASEDIR}/bin/run.conf 这个普通文件存在,则执行 then 分支的代码。
[[ ... ]] Bash 的增强测试结构(比 [ ... ] 更强大,支持更多操作符)
-f 文件测试运算符,检查是否为"普通文件"(regular file)且存在
${BASEDIR}/bin/run.conf 被测试的文件路径,BASEDIR 是之前定义的变量
-z 检查字符串是否为空 -z:zero length → 空则真
VAR="hello"
[[ -z "$VAR" ]] # false
# 检查用户是否输入了参数
if [[ -z "$1" ]]; then
echo "错误:缺少参数!"
echo "用法: $0 <文件名>"
exit 1
fi
# 检查配置文件路径是否为空
if [[ -z "$CONFIG_FILE" ]]; then
CONFIG_FILE="/etc/default/app.conf"
fi
-n 测试运算符:字符串长度 > 0 时返回真(true) -n:not empty → 非空则真
-n 和 -z 刚好相反,是一对互补的运算符。
deploy "v2.0.0" # 调用函数时传入了参数 "v2.0.0"
if [ -n "$1" ]; then
echo "参数不为空,值为:$1" # ✅ 会执行这行
else
echo "参数为空"
fi
# 输出:参数不为空,值为:v2.0.0
相关文件测试运算符(补充)
| 运算符 | 含义 |
|---|---|
-d |
检查是否为目录 |
-e |
检查是否存在(文件或目录) |
-L |
检查是否为软链接 |
-r |
检查是否可读 |
-w |
检查是否可写 |
-x |
检查是否可执行 |
-s |
检查文件是否存在且大小大于 0 |
-nt |
文件1比文件2新(newer than) |
-ot |
文件1比文件2旧(older than) |
5.$?
$? 是一个特殊变量,保存最近一条命令的退出状态码:
0 → 命令执行成功
非 0(如 1、2、127)→ 命令执行失败,不同数字代表不同错误
典型用法
some_command
if [[ $? = 0 ]]; then
echo "执行成功"
else
echo "执行失败"
fi
注意:
$? 极易被覆盖。它只反映紧挨着的上一条命令。中间只要插入了任何命令(哪怕是 echo),$? 就变了:
some_command
echo "检查结果" # 这条会覆盖 $?
if [[ $? = 0 ]]; then # 此时 $? 是 echo 的退出码,几乎总是 0
正确做法是紧跟在命令后判断,或先保存:
some_command
result=$?
echo "检查结果"
if [[ $result = 0 ]]; then
6.until
local timer=0
until [[ $(get_app_pid 1>/dev/null 2>&1; echo $?) != 0 || $timer = ${MAX_WAIT_TIME} ]]; do
sleep 1
((timer=$timer+1))
done
| 部分 | 含义 |
|---|---|
| local timer=0 | 声明局部变量 timer,初始为 0(计时器) |
| until [[ 条件 ]] | 直到条件为真才退出循环(与 while 相反:条件为假时一直循环) |
| $( ... ) | 命令替换,取括号内命令的输出 |
| get_app_pid 1>/dev/null 2>&1 | 调用函数 get_app_pid,把它的 stdout 和 stderr 都丢弃 |
| ; echo $? | 上一条命令(get_app_pid)执行完后,输出它的退出码 |
| != 0 | 退出码不等于 0(即 get_app_pid 失败) |
| || | 逻辑或 |
| $timer = ${MAX_WAIT_TIME} | 计时器已达到最大等待时间 |
| sleep 1 | 暂停 1 秒 |
| ((timer=$timer+1)) | 计时器加 1(算术运算) |
until 的语义:条件为假时循环,条件为真时停止。
所以:
-
只要进程还在(get_app_pid成功,退出码 = 0)且 timer 还没到最大值 → 条件为假 → 继续循环
-
一旦进程消失(get_app_pid失败)或 timer 到达 MAX_WAIT_TIME → 条件为真 → 退出循环
7.sh deploy.sh install && sh restart.sh
&& 逻辑与运算符,只有前面命令成功,才会执行后面的命令
整体意思:先执行部署脚本(安装模式),如果部署成功了,再执行重启脚本。
&& 是"短路与"操作符,规则是:
-
前面的命令 成功(退出码为 0)→ 才执行后面的命令
-
前面的命令 失败(退出码非 0,比如 1)→ 不执行后面的命令
所以:如果 deploy.sh 返回 exit 1,那么 restart.sh 不会被执行。
因为在 Linux/Unix 里:
-
exit 0 = 成功
-
exit 1(或任何非 0 数字)= 失败
#!/bin/bash
echo "部署完成"
exit 0 # 明确告诉调用者:我成功了
#!/bin/bash
echo "部署失败"
exit 1 # 明确告诉调用者:我失败了
--
浙公网安备 33010602011771号