首页 > 解决方案 > 如何在 bash 脚本中打印所有函数调用命令

问题描述

有没有办法在 bash 脚本中打印所有函数调用顺序,就像 IDEA Intellij 中的调用层次结构?

提前致谢。

标签: bashshell

解决方案


有一些预定义的 Bash 变量会在运行时公开其中的一些信息,但是如果您想要一个静态确定的调用层次结构,我不知道有任何工具可以为 Bash 做到这一点。

FUNCNAME以下是使用预定义数组和BASH_LINENO文档)从脚本本身中找到它的方法BASH_SOURCE

调用者.sh
#!/usr/bin/env bash

print_callers() {
  local func file line

  echo 'CALLERS:'

  for i in "${!FUNCNAME[@]}"; do
    # Uncomment next line to skip the current function, i.e., `print_callers`.
    # if [[ $i == 0 ]]; then continue; fi
    func=${FUNCNAME[$i]}
    file=${BASH_SOURCE[$i]}
    line=${BASH_LINENO[$i - 1]}
    echo " - $func ($file:$line)"
  done

  echo
} >&2

baz() {
  print_callers
  echo 'this is a leaf function'
}

bar() {
  baz
}

foo() {
  bar
}

foo

以上将输出:

$ ./callers.sh
CALLERS:
 - print_callers (./callers.sh:0)
 - baz (./callers.sh:21)
 - bar (./callers.sh:26)
 - foo (./callers.sh:30)
 - main (./callers.sh:33)

this is a leaf function

我实际上是结合使用这种方法,trap以便在抛出错误时打印脚本执行的堆栈跟踪。我有这个prelude.sh文件,可以在不同的 Bash 脚本中重复使用:

前奏曲.sh
#!/usr/bin/env bash

shopt -so errexit
shopt -so errtrace
shopt -so noglob
shopt -so nounset
shopt -so pipefail
shopt -s extglob

bold() {
  tput bold; echo -n "$@"; tput sgr0
}

trap ERR ERR; ERR() {
  local code=$?
  local cmnd=$BASH_COMMAND
  local func file line level

  echo "Exit status $(bold "$code") for command: $(bold "$cmnd")"

  for i in "${!FUNCNAME[@]}"; do
    if [[ $i == 0 ]]; then continue; fi
    func=${FUNCNAME[$i]}
    file=${BASH_SOURCE[$i]}
    line=${BASH_LINENO[$i - 1]}

    if (( i == ${#FUNCNAME[@]} - 1 )); then
      level='└─'
      func="<$func>"
    else
      level='├─'
    fi

    echo " $level $(bold "$func") ($file:$line)"
  done

  exit 1
} >&2

然后我可以在我的其他脚本中像这样使用它(prelude.shis on PATH):

失败.sh
#!/usr/bin/env bash

source prelude.sh

foo() {
  echo '' | grep 'nope'
}

foo

以上将导致这个堆栈跟踪被打印出来:

Exit status 1 for command: grep 'nope'
 ├─ foo (./fails.sh:6)
 └─ <main> (./fails.sh:9)

推荐阅读