Z-Image-Turbo用户权限管理:多账号体系的设计思路
2026/1/8 14:53:54
PS4(Fourth Prompt String)是第四提示符,专门用于Shell 调试模式(set -x)的输出前缀。它控制在调试模式下每条命令执行前显示的提示信息。
# 开启调试模式 set -x # 或使用缩写 set -o xtrace # 关闭调试模式 set +x # 仅在脚本中调试 bash -x script.sh# 默认 PS4 值 + (一个加号和空格)示例:
$ set -x $ echo "hello" + echo hello # ← 这里的 "+ " 就是 PS4 helloecho $PS4 echo "$PS4"# 设置为箭头 export PS4="-> " # 设置为调试标记 export PS4="[DEBUG] "$ export PS4="[调试] " $ set -x $ echo "测试" [调试] echo 测试 测试 $ set +xPS4 支持一些特殊的转义序列来显示调试信息:
| 序列 | 含义 | 示例 |
|---|---|---|
\$ | 显示$或#(取决于用户) | $ |
\s | Shell 名称 | bash |
\v | Shell 版本 | 5.1.16 |
\\ | 反斜杠 | \ |
\! | 命令历史编号 | 1234 |
\# | 命令编号(在当前 Shell 中) | 45 |
\@ | 时间(12小时制 am/pm) | 02:30 PM |
\t | 时间(24小时制 HH:MM:SS) | 14:30:45 |
\u | 用户名 | alice |
\h | 主机名 | server |
\W | 当前目录的最后部分 | projects |
\w | 当前完整目录 | /home/alice/projects |
# 显示执行时间 export PS4="+ \t " # 格式: + 14:30:45 # 带日期时间 export PS4='+ $(date "+%Y-%m-%d %H:%M:%S") '# 显示脚本行号(需要 BASH_SOURCE 和 LINENO) export PS4='+ ${BASH_SOURCE}:${LINENO}: ' # 更详细的格式 export PS4='+ [${LINENO}] '# 显示函数名和行号 export PS4='+ ${FUNCNAME[0]}:${LINENO} ' # 显示调用栈 export PS4='+ ${FUNCNAME[0]}() [${BASH_SOURCE}:${LINENO}] '# 红色调试信息 export PS4=$'\033[31m+\033[0m ' # 黄色带方括号 export PS4=$'\033[33m[调试]\033[0m ' # 彩色分级(根据 BASH_SUBSHELL) export PS4='$(printf "%$((BASH_SUBSHELL*2))s")'$'\033[36m+\033[0m '#!/bin/bash # debug_script.sh # 设置详细的 PS4 export PS4='+ [${LINENO}] ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' set -x function test_func() { local var="内部变量" echo "函数内: $var" } echo "开始执行" test_func echo "结束" set +x输出:
+ [10] echo 开始执行 开始执行 + [11] test_func(): + [6] test_func(): local var=内部变量 + [7] test_func(): echo '函数内: 内部变量' 函数内: 内部变量 + [12] echo 结束 结束#!/bin/bash # 根据嵌套层级缩进 export PS4='$(printf "%$((BASH_SUBSHELL*2))s")'$'+ ' set -x echo "层级 0" ( echo "层级 1" ( echo "层级 2" ) ) set +x输出:
+ echo '层级 0' 层级 0 + ( echo '层级 1' ( echo '层级 2' ) ) + echo '层级 1' 层级 1 + ( echo '层级 2' ) + echo '层级 2' 层级 2#!/bin/bash # 不同层级不同颜色 export PS4='\ $(case $BASH_SUBSHELL in \ 0) echo -ne "\033[32m" ;; \ 1) echo -ne "\033[33m" ;; \ 2) echo -ne "\033[34m" ;; \ 3) echo -ne "\033[35m" ;; \ *) echo -ne "\033[36m" ;; \ esac)\ +$(printf "%$((BASH_SUBSHELL*2))s")\033[0m ' set -x echo "主进程" ( echo "子shell 1" ) ( ( echo "子shell 2" ) ) ( ( ( echo "子shell 3" ) ) ) set +x#!/bin/bash # 显示执行时间和内存使用 export PS4='+ [$(date "+%H:%M:%S.%3N")] [$$:$BASHPID] ' set -x # 测试命令 sleep 0.5 echo "测试" seq 1 3 set +x#!/bin/bash # debug_functions.sh export PS4='+ [${FUNCNAME[0]:-main}():${LINENO}] ' debug() { set -x echo "调试信息" local result=$(( $1 * 2 )) echo "结果: $result" set +x } set -x echo "开始" debug 5 echo "结束" set +x#!/bin/bash export PS4='+ [$$:${BASH_SUBSHELL}] ' set -x # 管道会在子shell中执行 cat /etc/passwd | grep "^root" | cut -d: -f1 set +x#!/bin/bash export PS4='+ [行${LINENO}] ' set -x if [[ -f /etc/passwd ]]; then echo "文件存在" wc -l /etc/passwd else echo "文件不存在" fi for i in {1..3}; do echo "循环: $i" done set +xBASH_SOURCE和LINENO# 显示文件名和行号 export PS4='+ ${BASH_SOURCE##*/}:${LINENO}: ' # 在脚本中: # + script.sh:10: echo "测试"FUNCNAME数组# 显示函数调用栈 export PS4='+ ${FUNCNAME[0]}()@${BASH_SOURCE##*/}:${LINENO} ' # 显示完整调用栈(最多3层) export PS4='+ ${FUNCNAME[0]:-main}()<-${FUNCNAME[1]:-}:<-${FUNCNAME[2]:-} 'BASH_SUBSHELL# 显示子shell层级 export PS4='$(printf "%.s " $(seq 1 $((BASH_SUBSHELL+1))))+ ' # 或使用缩进 export PS4='$(printf "%${BASH_SUBSHELL}s")+ '# 显示进程ID export PS4='+ [PID:$$] ' # 显示当前shell的PID export PS4='+ [BASHPID:$BASHPID] ' # 组合显示 export PS4='+ [$$:$BASHPID:$BASH_SUBSHELL] '#!/bin/bash # 只在 DEBUG_MODE 启用时开启调试 [[ "$DEBUG_MODE" == "1" ]] && set -x # 使用自定义 PS4 export PS4='[调试] ' # ... 脚本内容 ...#!/bin/bash debug_function() { local old_ps4="$PS4" export PS4='[函数调试] ' set -x # 函数体 echo "在函数中" set +x export PS4="$old_ps4" } # 正常执行 echo "正常模式" debug_function#!/bin/bash { # 临时修改 PS4 并开启调试 local old_ps4="$PS4" export PS4='[块调试] ' set -x echo "调试块开始" ls -la echo "调试块结束" set +x export PS4="$old_ps4" }trap结合#!/bin/bash # 在 EXIT 时显示调试信息 trap 'echo "退出状态: $?"' EXIT export PS4='+ [行${LINENO}] ' set -x # 脚本内容 false # 返回非零状态 echo "继续执行" set +xbashdb风格类似#!/bin/bash # 模拟调试器样式 export PS4='-> [${BASH_SOURCE##*/}:${LINENO}] [${FUNCNAME[0]:-main}] ' set -x function test() { echo "函数测试" } test echo "完成"# 添加到 ~/.bashrc echo 'export PS4="+ [\${LINENO}] "' >> ~/.bashrc # 或更详细的设置 cat >> ~/.bashrc << 'EOF' # 调试提示符设置 export PS4='+\t [${BASH_SOURCE##*/}:${LINENO}${FUNCNAME[0]:+ ${FUNCNAME[0]}()}] ' EOF source ~/.bashrc# 在 ~/.bashrc 中添加 if [[ "$DEBUG_MODE" == "1" ]]; then export PS4='[详细调试] ' set -x fi# 错误:单引号内的变量不会展开 export PS4='+ $LINENO ' # 显示字面量 "$LINENO" # 正确:使用双引号或特殊格式 export PS4="+ \$LINENO " # 需要转义 $ export PS4='+ ${LINENO} ' # 或使用 ${} 语法# 复杂的 PS4 可能影响性能 export PS4='+ $(date +%s.%N) ' # 每次执行都要调用 date # 更高效的替代 export PS4='+ ' # 简单格式# 使用 $'' 语法处理多行 export PS4=$'+\n ' # 或使用 printf export PS4='$(printf "\n+ ")' # 每次换行信息适量:显示必要信息,不要过多
# 好:显示行号和函数名 export PS4='+ ${LINENO}${FUNCNAME[0]:+:${FUNCNAME[0]}} ' # 过多:可能影响可读性 export PS4='+ [$(date)] [$$] [${BASH_SOURCE}] [${LINENO}] [${FUNCNAME[@]}] '使用颜色区分:但确保兼容性
# 只在支持颜色的终端使用 if [[ -t 1 ]]; then export PS4=$'\033[33m+\033[0m ' fi考虑可读性:清晰的格式
# 使用一致的格式 export PS4='+ [${LINENO}] '分层缩进:对于嵌套命令
export PS4='$(printf "%$((BASH_SUBSHELL*2))s")+ '临时调试:不需要时关闭
# 在脚本开头设置 [[ "$DEBUG" ]] && set -xexport PS4='+ [${LINENO}] 'export PS4='+\t [${BASH_SOURCE##*/}:${LINENO}] 'export PS4='+ ${FUNCNAME[0]:-main}()@${BASH_SOURCE##*/}:${LINENO} 'export PS4=$'\033[36m+\033[0m \033[33m${LINENO}\033[0m: '# 这些只在 Bash 中有效 export PS4='+ ${BASH_SOURCE}:${LINENO} ' export PS4='+ ${FUNCNAME[0]} '# 更通用的设置 export PS4='+ ' # 所有 Shell 都支持PS4 是 Shell 调试的强大工具,合理的配置可以大大提高调试效率!