当前位置: 首页 > news >正文

Shell脚本函数编程:从基础到高级实践

1. Shell函数基础概念

在Shell脚本编程中,函数是将一组命令封装起来以便重复使用的代码块。它就像是一个小型脚本中的脚本,能够接收参数、执行特定任务并返回结果。函数在Shell中的定义方式有两种基本语法:

第一种是传统Bourne shell风格:

function_name() { commands }

第二种是Korn shell风格(也兼容bash):

function function_name { commands }

这两种定义方式在大多数现代shell中都可以工作,但第一种更为通用和推荐。函数体内部可以包含任何有效的shell命令,包括条件判断、循环、甚至调用其他函数。

注意:函数名后面是否带括号()与shell版本有关。在bash中两种都可以,但在某些严格遵循POSIX标准的shell中,必须使用不带function关键字的定义方式。

函数的一个典型应用场景是避免代码重复。例如,当你在脚本中多次需要执行相同的日志记录操作时:

log_message() { local timestamp=$(date '+%Y-%m-%d %H:%M:%S') echo "[$timestamp] $1" >> /var/log/myscript.log } log_message "Starting processing..." # 一些操作... log_message "Processing completed."

2. 函数参数传递与返回值

2.1 参数传递机制

Shell函数处理参数的方式与脚本本身接收参数非常相似,但有几个关键区别需要注意。函数内部通过$1, $2, ..., $9来访问位置参数,$0仍然是脚本名称而不是函数名,$#表示参数个数,$@和$*表示所有参数。

一个常见的误区是认为函数参数需要在定义时声明。实际上,Shell函数不需要预先定义参数列表,任何函数都可以接受任意数量的参数。下面是一个处理参数的示例:

calculate() { if [ $# -ne 2 ]; then echo "Usage: calculate number1 number2" return 1 fi local sum=$(( $1 + $2 )) local diff=$(( $1 - $2 )) local product=$(( $1 * $2 )) echo "Sum: $sum, Difference: $diff, Product: $product" }

2.2 返回值处理

Shell函数通过return语句返回退出状态码(0表示成功,非0表示失败),这与大多数编程语言中的返回值概念不同。要返回实际数据,通常有以下几种方法:

  1. 使用echo输出结果,然后通过命令替换捕获:
get_timestamp() { echo $(date '+%Y%m%d_%H%M%S') } current_time=$(get_timestamp)
  1. 使用全局变量(不推荐,容易造成混乱):
result="" compute() { result=$(( $1 * $2 )) } compute 5 7 echo "Result is $result"
  1. 通过文件或命名管道传递数据(适用于复杂数据结构)

重要提示:return语句只能返回0-255之间的整数。如果你尝试返回大于255的值,它会被截断为模256的结果。例如,return 300实际上会返回44。

3. 函数作用域与变量处理

3.1 变量作用域规则

Shell函数中的变量默认是全局的,这意味着在函数内修改的变量会影响整个脚本环境。这是一个常见的陷阱来源。为了避免这种情况,应该使用local关键字声明函数局部变量:

count=0 # 全局变量 increment() { local count=0 # 局部变量,不影响全局 count=$((count + 1)) echo "Local count: $count" } increment increment echo "Global count: $count" # 仍然是0

3.2 数组参数传递

处理数组参数需要特别注意,因为Shell函数不能直接接收数组作为参数。常见的解决方案是:

  1. 将数组元素作为多个位置参数传递:
print_array() { for element in "$@"; do echo "$element" done } my_array=("apple" "banana" "cherry") print_array "${my_array[@]}"
  1. 使用全局数组(不推荐):
process_array() { for ((i=0; i<${#global_array[@]}; i++)); do global_array[i]=$(do_something "${global_array[i]}") done } global_array=("data1" "data2" "data3") process_array
  1. 通过引用传递(bash 4.3+):
modify_array() { local -n arr=$1 # 命名引用 arr[0]="modified" } my_array=("original" "values") modify_array my_array echo "${my_array[0]}" # 输出"modified"

4. 高级函数技巧与最佳实践

4.1 递归函数实现

虽然Shell脚本不是最适合递归编程的语言,但在某些情况下递归函数非常有用。下面是一个计算阶乘的递归示例:

factorial() { local n=$1 if [ $n -eq 0 ]; then echo 1 else local prev=$(factorial $((n - 1))) echo $((n * prev)) fi } echo "5! = $(factorial 5)"

需要注意的是,Shell对递归深度有限制,可以通过ulimit -s查看和修改栈大小。对于深度递归,最好改用迭代实现。

4.2 函数库的组织与导入

随着脚本复杂度增加,将函数组织到单独的文件中是个好主意。创建函数库文件(如myfunctions.sh):

#!/bin/bash # myfunctions.sh - 函数库示例 function greet { echo "Hello, $1!" } function calculate_square { echo $(( $1 * $1 )) }

然后在主脚本中导入:

source myfunctions.sh # 或者 . myfunctions.sh greet "World" result=$(calculate_square 5)

专业提示:在函数库文件中,最好包含一个main函数和if [ "$0" == "$BASH_SOURCE" ]检查,这样文件既可以作为库被导入,也可以独立执行进行测试。

4.3 错误处理与调试

健壮的Shell函数应该包含适当的错误检查和处理。以下是一些关键实践:

  1. 使用set -euo pipefail在脚本开头启用严格模式:

    • -e:命令失败时立即退出
    • -u:使用未定义变量时报错
    • -o pipefail:管道中任一命令失败则整个管道失败
  2. 在函数中添加参数验证:

safe_divide() { if [ $# -ne 2 ]; then echo "Usage: safe_divide dividend divisor" >&2 return 1 fi if [ $2 -eq 0 ]; then echo "Error: Division by zero" >&2 return 1 fi echo $(( $1 / $2 )) }
  1. 使用trap捕获信号并清理资源:
tempfile=$(mktemp) cleanup() { rm -f "$tempfile" echo "Cleanup complete" } trap cleanup EXIT INT TERM # 函数使用临时文件 process_data() { # ...使用tempfile... }

4.4 性能优化技巧

虽然Shell脚本不以性能著称,但对于频繁调用的函数,仍有优化空间:

  1. 避免在循环中调用外部命令:
# 不好 for file in *; do size=$(stat -c%s "$file") # ... done # 更好 for file in *; do size=$( < "$file" wc -c ) # ... done
  1. 使用shell内置命令替代外部命令:
# 使用shell内置字符串操作 ${var#pattern} # 替代sed ${var%pattern} # 替代sed ${var//pattern/repl} # 替代tr或sed
  1. 对于数据处理密集型任务,考虑将数据批量传递给外部工具,而不是每次调用:
# 不好:多次调用awk process_items() { for item in "$@"; do result=$(echo "$item" | awk '{print $1}') # ... done } # 更好:单次调用awk process_items() { printf "%s\n" "$@" | awk '{ # 批量处理所有项目 }' }

5. 实战案例:构建一个Shell脚本框架

让我们把这些概念整合到一个实用的脚本框架中,展示函数在实际项目中的应用:

#!/bin/bash set -euo pipefail # 导入函数库 SCRIPT_DIR=$(dirname "$(readlink -f "$0")") source "$SCRIPT_DIR/lib/common.sh" # 配置变量 CONFIG_FILE="/etc/app/config.cfg" LOG_FILE="/var/log/app.log" # 初始化函数 init() { load_config "$CONFIG_FILE" setup_logging "$LOG_FILE" check_dependencies awk sed curl } # 主处理函数 process_data() { local input_file=$1 local output_file=$2 validate_file "$input_file" transform_data "$input_file" | filter_results > "$output_file" if [ $? -ne 0 ]; then log_error "Data processing failed" return 1 fi } # 清理函数 cleanup() { release_resources log_info "Script completed" } # 主函数 main() { init trap cleanup EXIT local input="$1" local output="$2" log_info "Starting processing with input $input" process_data "$input" "$output" log_info "Output saved to $output" } # 脚本入口 if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then if [ $# -ne 2 ]; then echo "Usage: $0 input_file output_file" >&2 exit 1 fi main "$@" fi

这个框架展示了几个关键实践:

  1. 清晰的函数分工(初始化、处理、清理)
  2. 错误处理和日志记录
  3. 配置管理
  4. 模块化设计(通过source导入库)
  5. 既可作为独立脚本运行,也可作为库被导入

6. 常见Shell函数陷阱与解决方案

6.1 参数处理陷阱

  1. 忘记引用参数变量:
# 错误:如果参数包含空格会出问题 print_args() { for arg in $@; do echo "$arg" done } # 正确:始终引用参数扩展 print_args_correct() { for arg in "$@"; do echo "$arg" done }
  1. 修改位置参数:
# 错误方式:直接修改$1 change_args() { $1="new value" # 语法错误 } # 正确方式:使用set命令 change_args_correct() { set -- "new value" "${@:2}" }

6.2 返回值混淆

  1. 混淆return和echo:
# 混淆概念 bad_example() { return "Hello" # 错误:return只能用于状态码 } # 正确做法 good_example() { echo "Hello" # 输出结果 return 0 # 返回状态 }
  1. 忽略命令替换中的错误:
result=$(some_function) if [ $? -ne 0 ]; then # 处理错误 fi

6.3 性能陷阱

  1. 过度使用子shell:
# 创建不必要的子shell slow_function() { local var=$(echo "value") # 创建子shell # ... } # 更快的方式 fast_function() { local var="value" # 直接赋值 # ... }
  1. 在循环中重复计算:
# 低效 for file in *; do ext=${file##*.} # 每次循环都计算 # ... done # 更高效 for file in *; do ext=${file##*.} case $ext in txt) process_text "$file" ;; csv) process_csv "$file" ;; esac done

7. Shell函数调试技巧

7.1 基本调试方法

  1. 使用-x选项打印执行命令:
#!/bin/bash -x # 或者在脚本中启用 set -x my_function() { # ... } set +x
  1. 打印函数调用堆栈:
function debug_trace { echo "Call stack:" for ((i=1;i<${#FUNCNAME[@]};i++)); do echo " $i: ${BASH_SOURCE[$i]}:${BASH_LINENO[$i-1]} ${FUNCNAME[$i]}" done } my_function() { debug_trace }

7.2 高级调试工具

  1. 使用bashdb调试器:
# 安装 sudo apt-get install bashdb # 使用 bashdb myscript.sh
  1. 使用shellcheck进行静态分析:
# 安装 sudo apt-get install shellcheck # 使用 shellcheck myscript.sh
  1. 自定义调试函数:
# 在脚本开头设置 DEBUG=${DEBUG:-0} debug() { if [ "$DEBUG" -eq 1 ]; then echo "DEBUG: $@" >&2 fi } # 在函数中使用 my_function() { debug "Entering my_function with args: $@" # ... }

7.3 性能分析

  1. 使用time测量函数执行时间:
time my_function args...
  1. 使用特殊变量统计调用次数:
declare -A FUNC_CALLS count_calls() { local func=$1 FUNC_CALLS[$func]=$(( ${FUNC_CALLS[$func]:-0} + 1 )) } my_function() { count_calls "${FUNCNAME[0]}" # ... } # 结束时打印统计 trap 'echo "Function call statistics:"; declare -p FUNC_CALLS' EXIT

8. Shell函数在不同Shell中的兼容性

8.1 主要Shell实现的差异

  1. function关键字:

    • bash和ksh支持两种函数定义语法
    • dash/zsh只支持name()语法
  2. 局部变量:

    • bash/ksh/zsh支持local关键字
    • dash不支持,需要使用POSIX兼容的方式
  3. 数组支持:

    • bash 4.0+支持关联数组
    • ksh93也支持
    • 其他shell的数组功能有限

8.2 编写可移植函数

  1. 使用POSIX兼容语法:
# 可移植函数定义 portable_function() { # 使用$1, $2等访问参数 # 避免使用bash特有特性 }
  1. 特性检测:
# 检查shell特性 if [ -n "$BASH_VERSION" ]; then # bash特有代码 elif [ -n "$ZSH_VERSION" ]; then # zsh特有代码 else # POSIX兼容代码 fi
  1. 避免使用非标准扩展:
    • 进程替换<() >()
    • 数组操作${array[@]}
    • 正则表达式=~操作符
    • 子字符串${var:offset:length}

8.3 常见兼容性问题解决方案

  1. 局部变量模拟:
# 在dash中模拟局部变量 fn() { _old_var=$var # 保存原值 var=new_value # 设置新值 # ...函数体... var=$_old_var # 恢复原值 }
  1. 数组传递的兼容方法:
# 将数组序列化为字符串传递 serialize_array() { local IFS=$'\001' # 使用不可见字符作为分隔符 echo "$*" } deserialize_array() { local IFS=$'\001' local arr=($1) echo "${arr[@]}" } my_array=("item 1" "item 2") serialized=$(serialize_array "${my_array[@]}") new_array=($(deserialize_array "$serialized"))
  1. 测试函数是否存在的兼容方法:
# bash/zsh/ksh方式 if declare -f function_name > /dev/null; then # 函数存在 fi # POSIX兼容方式 if type function_name | grep -q 'function'; then # 函数存在 fi

9. Shell函数与系统集成

9.1 将常用函数添加到shell环境

  1. 在~/.bashrc或~/.zshrc中定义常用函数:
# 快速进入工作目录 workon() { cd ~/projects/"$1" || return source venv/bin/activate 2>/dev/null [[ -f .env ]] && source .env } # 查找并杀死进程 killproc() { pkill -f "$1" || { echo "No process found" >&2 return 1 } }
  1. 创建函数库文件并自动加载:
# 在~/.bashrc中添加 for funcfile in ~/.bash_functions/*; do source "$funcfile" done

9.2 系统管理中的实用函数

  1. 服务管理辅助函数:
service_status() { if systemctl is-active --quiet "$1"; then echo "$1 is running" else echo "$1 is not running" return 1 fi } restart_service() { sudo systemctl restart "$1" && \ echo "Successfully restarted $1" || \ echo "Failed to restart $1" >&2 }
  1. 日志监控函数:
# 实时监控多个日志文件 logwatch() { multitail \ -cS apache /var/log/apache2/access.log \ -cS mysql /var/log/mysql/error.log \ -cS app /var/log/myapp/app.log }
  1. 系统信息收集:
system_report() { echo "===== System Report =====" echo "Hostname: $(hostname)" echo "Uptime: $(uptime)" echo "Load: $(cat /proc/loadavg)" echo "Memory: $(free -h | awk '/Mem/{print $3"/"$2}') used" echo "Disk: $(df -h / | awk 'NR==2{print $4}') free on /" }

9.3 与外部工具的集成函数

  1. Git辅助函数:
# 快速提交所有更改并推送 gitall() { git add . && \ git commit -m "$1" && \ git push } # 创建并切换到新分支 gitnew() { git checkout -b "$1" && \ git push -u origin "$1" }
  1. Docker辅助函数:
# 清理所有停止的容器和悬空镜像 dockerclean() { docker system prune -af } # 进入容器shell dockerbash() { docker exec -it "$1" bash }
  1. 网络工具封装:
# 快速测试网站可用性 checkurl() { curl -sSL -w \ "HTTP Code: %{http_code}\nTotal Time: %{time_total}s\n" \ -o /dev/null "$1" } # 获取公网IP myip() { curl ifconfig.me }

10. Shell函数设计模式

10.1 命令模式封装

将复杂命令序列封装为语义化的函数:

# 复杂的find命令封装 find_large_files() { local dir=${1:-.} local size=${2:-100} find "$dir" -type f -size +${size}M -exec ls -lh {} + | sort -k5 -hr } # 使用 find_large_files /home 50 # 查找/home下大于50MB的文件

10.2 工厂函数模式

创建可配置的函数生成器:

# 创建日志函数工厂 make_logger() { local logfile=$1 local level=$2 echo " function log_${level} { echo \"[\$(date '+%Y-%m-%d %H:%M:%S')] ${level^^}: \$@\" >> \"$logfile\" } " } # 生成特定日志级别的函数 eval "$(make_logger /var/log/myapp.log info)" eval "$(make_logger /var/log/myapp.log error)" # 使用生成的函数 log_info "Application started" log_error "Something went wrong"

10.3 中间件模式

实现函数调用前后的钩子:

# 中间件包装器 with_middleware() { local func=$1 shift local middleware=("$@") for m in "${middleware[@]}"; do eval " function wrapped_${func} { $m pre \"\$@\" original_${func} \"\$@\" local ret=\$? $m post \"\$@\" \$ret return \$ret } " eval "original_${func}() { \$(declare -f $func | tail -n +2); }" func="wrapped_${func}" done alias $func=$func } # 示例中间件 log_middleware() { case $1 in pre) echo "Calling function with args: ${@:2}" >&2 ;; post) echo "Function returned $3" >&2 ;; esac } # 使用 my_function() { echo "Doing work with $1"; sleep 1; } with_middleware my_function log_middleware my_function "test"

10.4 状态机模式

使用函数实现简单状态机:

# 状态机实现 state_machine() { local state=$1 local event=$2 case "$state:$event" in "off:power") echo "on" ;; "on:power") echo "off" ;; "on:mode") echo "standby" ;; "standby:power") echo "off" ;; "standby:mode") echo "on" ;; *) echo "$state" ;; # 保持原状态 esac } # 使用 current_state="off" current_state=$(state_machine "$current_state" "power") # 切换到on current_state=$(state_machine "$current_state" "mode") # 切换到standby

11. Shell函数测试方法

11.1 手动测试函数

  1. 交互式测试方法:
# 在终端中直接定义和测试函数 $ myfunc() { echo "Hello $1"; } $ myfunc "World" Hello World
  1. 使用declare -f查看函数定义:
declare -f myfunc
  1. 使用type命令检查函数:
type myfunc

11.2 自动化测试框架

  1. 使用shunit2进行单元测试:
#!/bin/bash # test_myfunctions.sh . ./myfunctions.sh # 导入被测函数 testAdd() { assertEquals 3 "$(add 1 2)" assertEquals 0 "$(add -1 1)" } . shunit2 # 运行测试
  1. 使用bats-core测试框架:
#!/usr/bin/env bats # test.bats @test "addition using bc" { result="$(echo 2+2 | bc)" [ "$result" -eq 4 ] } @test "test my function" { run myfunc "input" [ "$status" -eq 0 ] [ "$output" = "expected output" ] }
  1. 自定义简单测试框架:
#!/bin/bash # test_runner.sh TEST_COUNT=0 PASS_COUNT=0 assert_equal() { ((TEST_COUNT++)) if [ "$1" = "$2" ]; then ((PASS_COUNT++)) echo "PASS: $3" else echo "FAIL: $3 (expected '$1', got '$2')" fi } # 测试用例 test_addition() { result=$(add 2 3) assert_equal 5 "$result" "addition of 2 and 3" } # 运行测试 test_addition # 报告结果 echo "Tests passed: $PASS_COUNT/$TEST_COUNT"

11.3 测试覆盖率分析

  1. 使用bashcov工具:
# 安装 gem install bashcov # 运行 bashcov myscript.sh
  1. 使用lcov生成HTML报告:
# 需要先安装lcov bashcov --lcov-output coverage.info genhtml coverage.info -o coverage_report
  1. 手动跟踪函数调用:
# 在脚本开头设置 declare -A FUNC_CALLS # 修改函数定义 original_myfunc=$(declare -f myfunc) eval " myfunc() { FUNC_CALLS[myfunc]=\$(( \${FUNC_CALLS[myfunc]:-0} + 1 )) ${original_myfunc#*\{} } " # 结束时报告 trap 'echo "Function call counts:"; declare -p FUNC_CALLS' EXIT

12. Shell函数性能优化

12.1 减少子shell创建

  1. 使用进程替换替代管道:
# 较慢:创建两个子shell result=$(echo "input" | process_stage1 | process_stage2) # 较快:使用进程替换 result=$(process_stage2 < <(process_stage1 <<< "input"))
  1. 使用here-string替代echo管道:
# 较慢 result=$(echo "$data" | process) # 较快 result=$(process <<< "$data")
  1. 避免不必要的命令替换:
# 不好 var=$(echo "value") # 好 var="value"

12.2 内置命令优化

  1. 使用shell内置字符串操作:
# 较慢 trimmed=$(echo "$str" | sed 's/^ *//;s/ *$//') # 较快 trimmed=${str#"${str%%[! ]*}"} trimmed=${trimmed%"${trimmed##*[! ]}"}
  1. 使用shell内置算术:
# 较慢 sum=$(expr $a + $b) # 较快 sum=$((a + b))
  1. 使用shell内置模式匹配:
# 较慢 if echo "$str" | grep -q 'pattern'; then # ... fi # 较快 if [[ "$str" == *pattern* ]]; then # ... fi

12.3 批量处理优化

  1. 减少外部命令调用次数:
# 不好:每次循环调用date for i in {1..100}; do echo "$(date +%s),$i" done > data.csv # 好:单次调用date timestamp=$(date +%s) for i in {1..100}; do echo "$timestamp,$i" done > data.csv
  1. 使用关联数组缓存结果:
declare -A cache expensive_operation() { local input=$1 if [[ -z "${cache[$input]}" ]]; then cache[$input]=$(do_expensive_calculation "$input") fi echo "${cache[$input]}" }
  1. 并行处理优化:
# 使用GNU parallel process_file() { # 处理单个文件的函数 } export -f process_file find . -name '*.log' | parallel -j 4 process_file

13. Shell函数安全实践

13.1 输入验证

  1. 验证参数数量:
safe_function() { if [ $# -lt 2 ]; then echo "Usage: safe_function arg1 arg2" >&2 return 1 fi # ... }
  1. 验证参数内容:
is_valid_filename() { [[ "$1" =~ ^[a-zA-Z0-9_.-]+$ ]] && [ ! -e "$1" ] } create_file() { local filename=$1 is_valid_filename "$filename" || { echo "Invalid or existing filename: $filename" >&2 return 1 } touch "$filename" }
  1. 清理用户输入:
clean_input() { # 移除所有非字母数字字符 echo "$1" | tr -cd '[:alnum:]' } process_input() { local clean=$(clean_input "$1") # 使用清理后的输入 }

13.2 权限控制

  1. 检查执行权限:
run_as_root() { [ "$(id -u)" -eq 0 ] || { echo "This function must be run as root" >&2 return 1 } # ... }
  1. 安全执行外部命令:
safe_exec() { local cmd=$1 case $cmd in /usr/bin/*|/bin/*|/sbin/*) "$cmd" "${@:2}" ;; *) echo "Command not allowed: $cmd" >&2 return 1 ;; esac }
  1. 安全临时文件创建:
create_tempfile() { local template=${1:-tempfile} local tmpdir=${2:-/tmp} mktemp "$tmpdir/${template}.XXXXXX" || { echo "Failed to create temp file" >&2 return 1 } } tempfile=$(create_tempfile "mydata") || exit 1 trap 'rm -f "$tempfile"' EXIT

13.3 敏感数据处理

  1. 安全密码输入:
get_password() { local prompt=${1:-"Password: "} unset password while IFS= read -p "$prompt" -r -s -n 1 char; do if [[ $char == $'\0' ]]; then break fi prompt='*' password+="$char" done echo echo "$password" } password=$(get_password)
  1. 安全环境变量处理:
# 在函数开始时保存敏感环境变量 protect_env() { local -a sensitive_vars=(API_KEY DB_PASSWORD) declare -A old_values for var in "${sensitive_vars[@]}"; do old_values[$var]=${!var} unset "$var" fi # 函数体... # 结束时恢复 for var in "${sensitive_vars[@]}"; do if [ -n "${old_values[$var]}" ]; then export "$var=${old_values[$var]}" else unset "$var" fi done }
  1. 安全日志记录:
sanitize_log() { local message=$1 # 移除敏感信息 message=${message//$API_KEY/***} message=${message//$DB_PASSWORD/***} echo "$message" } log_safe() { local level=$1 shift local message=$(sanitize_log "$*") echo "[$(date)] [$level] $message" >> "$LOG_FILE" }

14. Shell函数文档化

14.1 内联文档标准

  1. 使用特殊注释格式:
#!/bin/bash # 函数:calculate # 描述:执行基本算术运算 # 参数: # $1 - 第一个操作数 # $2 - 第二个操作数 # $3 - 运算符 (+, -, *, /) # 返回: # 成功时输出结果,失败时返回非零状态码 calculate() { # ... }
  1. 支持提取为帮助文档:
help() { local func=$1 if [ -z "$func" ]; then echo "Available functions:" declare -F | awk '{print $3}' return 0 fi # 提取函数前的注释 sed -n "/^# 函数:$func$/,/^$func/p" "$0" | head -n -1 }

14.2 生成外部文档

  1. 使用shdoc生成Markdown文档:
#!/bin/bash # 安装shdoc # npm install -g shdoc # 在脚本中添加特殊注释 # : <<'DOC' # ## calculate # 执行基本算术运算 # # **参数:** # - $1: 第一个操作数 # - $2: 第二个操作数 # - $3: 运算符 (+, -, *, /) # # **返回:** # 成功时输出结果,失败时返回非零状态码 # DOC calculate() { # ... } # 生成文档 # shdoc < script.sh > docs.md
  1. 自定义文档生成器:
generate_docs() { echo "# $0 Documentation" echo # 提取函数列表 local funcs=$(declare -F | awk '{print $3}') for func in $funcs; do # 提取函数定义行号 local def_line=$(declare -f "$func" | head -1) # 提取前面的注释 sed -n "/^# 函数:$func$/,/^$func/p" "$0" | head -n -1 echo "```bash" declare -f "$func" echo "```" echo done }

14.3 交互式帮助系统

  1. 函数帮助命令:
func_help() { local func=$1 if [ -z "$func" ]; then echo "Usage: func_help <function_name>" return 1 fi if ! declare -f "$func" > /dev/null; then echo "Function $func not found" >&2 return 1 fi echo "Help for $func:" echo declare -f "$func" | awk ' /^[[:space:]]*#/ {print substr($0, 3)} /^[[:space:]]*[^#]/ {exit} ' }
  1. 示例用法帮助:
with_examples() { local func=$1 if ! declare -f "$func" > /dev/null; then echo "Function $func not found" >&2 return 1 fi func_help "$func" echo "Examples:" grep -A2 "^# 示例:$func" "$0" | sed 's/^# //' } # 示例:calculate # calculate 10 5 + # =>
http://www.jsqmd.com/news/1299796/

相关文章:

  • STM32引脚复用与重定义:解决引脚冲突,释放芯片功能潜力
  • 免费离线OCR神器Umi-OCR:你的终极文字识别解决方案
  • 【单片机毕业设计】基于单片机的六路测温数据存储与查看装置设计 基于 GY906 传感器的智能温度预警系统实现(014701)
  • 2026黄浦区双相钢棒厂家哪家好,双相钢锻件厂家推荐怎么选不踩坑?厂家推荐指南 - mobible
  • 【20年CV架构师亲授】:扩散模型不是黑箱——用概率流ODE+朗之万动力学重构理解范式
  • 视频会议内网化部署如何提升企业信息安全等级?政企单位选型要点解析
  • Vue3视图渲染技术与JavaWeb开发实践
  • 变压器安装工程的关键技术参数中,接地电阻值与绝缘电阻值的合格范围分别是多少?
  • 无锡气体涡轮流量计厂家推荐
  • 突破网盘限速封锁:8大主流平台直链下载实战指南
  • 2026年江苏谷歌认证推广团队服务评测 - 起跑123
  • Nginx Rewrite规则详解与实战技巧
  • 2026 年广州钻尾螺丝、钢结构螺丝采购,工程采购避坑实测分享 - LYL仔仔
  • AI 批量生成电商商品图的效率瓶颈分析——从单线程到批处理的架构演进
  • 大学生到底考什么证书好
  • 如何查串口日志
  • 主流Agent框架详解
  • [Dify实战] Workflow 跑一半就失败?先补错误分支和兜底输出,应用上线才不慌
  • 光栅衍射原理与工程实践:从多光束干涉到光谱分析应用
  • Windows热键冲突终极解决方案:3分钟定位被占用快捷键的完整指南
  • CoreCycler:精准定位CPU单核稳定性的专业诊断工具
  • 合并报表与汇总表的高效便捷实操指南
  • 单片机毕设项目:嵌入式 STM32 激光测距与阈值调控装置设计 基于激光传感的单片机智能限位报警系统(014801)
  • 桐乡中央水处理公司哪家好?2026避坑指南:5个常见坑+5条硬标准,告别装完就后悔 - mobible
  • 2026北京西城黄金回收——检测全程大屏直播,客户亲眼见证读数结果 - 融媒生活
  • 商家门店人气评选制作教程,云众评选报名通道 + 奖品设置教学 - 微信投票小程序
  • 冰蝎v2.0.1 WebShell流量深度解析:从加密协议到攻击行为还原
  • 计算机网络高效复习指南:从OSI模型到TCP/IP核心协议实战解析
  • 抖音批量下载神器:5分钟掌握高效无水印视频获取技巧
  • diff-pdf:免费开源的PDF视觉差异检测工具终极指南