简化调试输出。在bash中替换现场命令文本的特定请求

发布于 2025-02-07 09:24:27 字数 777 浏览 1 评论 0原文

好的,所以,我有一个像这样的调试设置:

DVAR=(); 
function DBG() {
    if [[ $1 == -s ]]; then shift; stackTrace; fi;
    if [[ ! -z ${DVAR[@]} ]]; then
        for _v in ${!DVAR[@]}; do
            echo "${DVAR[$_v]}" >> $LOG;
            unset DVAR[$_v];
        done;
    fi
    local tmp=("$*")
    [[ ! -z $tmp ]]&&echo "$tmp" >> $LOG||continue;
}

每隔一段时间我直接称呼它,或者,我想通过反复将东西添加到数组中并稍后再调用,以便更多地采用此方法。具体来说,我想使用它:

DVAR+="${0##*/}${FUNCNAME[0]}:$LINENO === assorted local variables and stuff here ====="

第一部分是一个非常嘴巴的,并且真的很刻苦我的代码。我真的宁愿说出类似的话:

DBG === assorted local variables and stuff here=====

我尝试与别名甚至评估一样乱七八糟。 。避免。 ahem

Thoughts anyone?

OK, so, I have a debugging setup a little like this:

DVAR=(); 
function DBG() {
    if [[ $1 == -s ]]; then shift; stackTrace; fi;
    if [[ ! -z ${DVAR[@]} ]]; then
        for _v in ${!DVAR[@]}; do
            echo "${DVAR[$_v]}" >> $LOG;
            unset DVAR[$_v];
        done;
    fi
    local tmp=("$*")
    [[ ! -z $tmp ]]&&echo "$tmp" >> $LOG||continue;
}

every once in a while I call it either directly or, and I'd like to take this approach more, by repeatedly adding things to the array and calling it later. SPECIFICALLY, I'd like to be using this:

DVAR+="${0##*/}${FUNCNAME[0]}:$LINENO === assorted local variables and stuff here ====="

That first part is quite a mouthful and really clutters up my code. I'd REALLY rather be able to say something like:

DBG === assorted local variables and stuff here=====

I've tried messing around with alias and even eval, all to no. . evail. ahem

Thoughts anyone?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

暮凉 2025-02-14 09:24:27

就像@ufopilot所说的那样,您应该将行添加为阵列中的新条目,其中dvar+=(“ ....”),而不是通过将其串联到第一个元素来覆盖它作为长字符串。这是一个解释:

串联:

$  DVAR=()
$  DVAR+="foo"
$  DVAR+="bar"
$  declare -p DVAR
  declare -a DVAR=([0]="foobar")
$  echo ${DVAR[@]}
  foobar

附加新条目:

$  DVAR=()
$  DVAR+=(foo)
$  DVAR+=(bar)
$  declare -p DVAR
  declare -a DVAR=([0]="foo" [1]="bar")
$  echo "${DVAR[@]}"
  foo bar

以下是我为调试目的提供的函数的一个示例。该函数是get_stack,它将从谁打电话给它获得函数名称以及该调用函数的位置的文件名,以及跟踪,以便您可以看到呼叫历史记录。

文件test.sh:file

#!/usr/bin/env bash
foo() {
    get_stack
    echo -e "$stack_trace" # Note the quotation to get indentation
}

get_stack () {
   stack_trace=""
   local i stack_size=${#FUNCNAME[@]}
   local indent="  "
   local newline="" # newline only after first line
   # Offset to skip get_stack function
   for (( i=0; i<$stack_size; i++ )); do
      local func="${FUNCNAME[$i]}"
      [ x$func = x ] && func=MAIN
      local linen="${BASH_LINENO[$(( i - 1 ))]}"
      local src="${BASH_SOURCE[$i]}"
      [ x"$src" = x ] && src=non_file_source
      stack_trace+="${newline}${indent}-> $src [$func]: $linen"
      newline="\n"
      indent="$indent  "
   done
}

echo "stack from test.sh"
foo

test2.sh:

#!/usr/bin/env bash
source test.sh
echo "stack from test2.sh"
foo

输出:

stack from test.sh
  -> test.sh [foo]: 4
    -> test.sh [source]: 28
      -> ./test2.sh [main]: 3
stack from test2.sh
  -> test.sh [foo]: 4
    -> ./test2.sh [main]: 6

在我的脚本中,我有一个向下箭头ASCII字符,看起来比“ - &gt;”更好但是,无法弄清楚如何获得stackoverflow来显示它。 ASCII是\ u21b3。

正如您在堆栈跟踪中看到的那样,“ foo”在采购功能时就运行了,就像应该做的那样。但是现在很清楚为什么它运行并输出文本!

我认为这很好地证明了您如何可以在呼叫堆栈中向后走。 bash_soruce也是一个数组本身,具有匹配的索引,但是它将显示函数调用的文件。修改“ get_stack”函数以检查以下数组:

foo() {
  get_stack
  echo -e "$stack_trace"
  echo "${BASH_SOURCE[@]}"
  echo "${FUNCNAME[@]}"
}

产量:

stack from test.sh
  -> test.sh [foo]: 4
    -> test.sh [source]: 30
      -> ./test2.sh [main]: 3
test.sh test.sh ./test2.sh
foo source main
stack from test2.sh
  -> test.sh [foo]: 4
    -> ./test2.sh [main]: 6
test.sh ./test2.sh
foo main

如您所见,第一组输出属于test.sh,来自采购。您可以在bash_source中看到:“ foo source main”。 “主要”是主要范围,即运行但不在函数中,而是文件的主体。在这种情况下,test.sh有“ foo”调用,该调用在采购该文件后将运行。

希望您看到这些索引如何属于同一电话,但会产生不同的信息。

现在,您只想一次又一次地添加字符串,而不是一次又一次的Y信息。由于我不确定您是否想要堆栈跟踪,我是否只是将消息字符串添加到第一个调用功能实例中。我还在这里修复了一些旧代码,以使其变得更好。

带有消息的新改进函数:

get_stack () {
   local msg="$@"
   stack_trace=""
   local i stack_size=${#FUNCNAME[@]}
   local indent="  "
   # Offset to skip get_stack function
   for (( i=1; i<$stack_size; i++ )); do
      local func="${FUNCNAME[$i]}"
      [ x$func = x ] && func=MAIN
      local linen="${BASH_LINENO[$(( i - 1 ))]}"
      local src="${BASH_SOURCE[$i]}"
      [ x"$src" = x ] && src=non_file_source
      stack_trace+="${newline:=\n}${indent}-> $src [$func:$linen]${msg:+": $msg"}"
      msg=""
      indent="$indent  "
   done
}

输出:

 $  ./test2.sh
stack from test.sh

  -> test.sh [foo:4]: my message
    -> test.sh [source:28]
      -> ./test2.sh [main:3]
stack from test2.sh

  -> test.sh [foo:4]: my message
    -> ./test2.sh [main:6]

请注意,如果var是非初始化的或设置为空字符串,$ {var:+x}将替换为“ var”,则用“ var”将“ var”替换为“ var”,$ {var:= x}将以“ x”初始化“ x”初始化“ x”。 x“如果VAR被初始化/设置为不是空字符串的东西,最后作为奖励$ {var:-x}将替换为“ var”,则“ x”,如果var是非初始化/设置空字符串。这是bash中的可变替换,非常方便!

您可以将一些部分切入并经历到您的功能中,或者如果您需要基于堆栈的日志,则可以将我的功能用作基础。

希望这对您的努力有所帮助!

Like @ufopilot said, you should add your line as a new entry in the array with DVAR+=("...."), not overwriting it as a long string by concatenating it to the first element. Here's an explaining it:

Concatenating:

$  DVAR=()
$  DVAR+="foo"
$  DVAR+="bar"
$  declare -p DVAR
  declare -a DVAR=([0]="foobar")
$  echo ${DVAR[@]}
  foobar

Appending new entry:

$  DVAR=()
$  DVAR+=(foo)
$  DVAR+=(bar)
$  declare -p DVAR
  declare -a DVAR=([0]="foo" [1]="bar")
$  echo "${DVAR[@]}"
  foo bar

Here's an example of a function I put together for debugging purposes some time. The function is get_stack which will get the function name from whoever called it and the file name of where that calling function exists in, along with the trace so you can see the call history.

File test.sh:

#!/usr/bin/env bash
foo() {
    get_stack
    echo -e "$stack_trace" # Note the quotation to get indentation
}

get_stack () {
   stack_trace=""
   local i stack_size=${#FUNCNAME[@]}
   local indent="  "
   local newline="" # newline only after first line
   # Offset to skip get_stack function
   for (( i=0; i<$stack_size; i++ )); do
      local func="${FUNCNAME[$i]}"
      [ x$func = x ] && func=MAIN
      local linen="${BASH_LINENO[$(( i - 1 ))]}"
      local src="${BASH_SOURCE[$i]}"
      [ x"$src" = x ] && src=non_file_source
      stack_trace+="${newline}${indent}-> $src [$func]: $linen"
      newline="\n"
      indent="$indent  "
   done
}

echo "stack from test.sh"
foo

File test2.sh:

#!/usr/bin/env bash
source test.sh
echo "stack from test2.sh"
foo

Output:

stack from test.sh
  -> test.sh [foo]: 4
    -> test.sh [source]: 28
      -> ./test2.sh [main]: 3
stack from test2.sh
  -> test.sh [foo]: 4
    -> ./test2.sh [main]: 6

In my script I have a down-right arrow ascii character that looks better than "->" but can't figure out how to get stackoverflow to display it proberly. The ascii is \u21b3.

As you can see in the stack trace, "foo" ran upon sourcing the function, just as it's supposed to do. But now it is clear why it ran and outputed text!

I think this demonstrates well how you can the array FUNCNAME to walk backwards in the call stack. Also BASH_SORUCE is an array itself, with matching indices, however it will display what file the function call came from. Modifying the "get_stack" function to inspect these arrays:

foo() {
  get_stack
  echo -e "$stack_trace"
  echo "${BASH_SOURCE[@]}"
  echo "${FUNCNAME[@]}"
}

yields:

stack from test.sh
  -> test.sh [foo]: 4
    -> test.sh [source]: 30
      -> ./test2.sh [main]: 3
test.sh test.sh ./test2.sh
foo source main
stack from test2.sh
  -> test.sh [foo]: 4
    -> ./test2.sh [main]: 6
test.sh ./test2.sh
foo main

As you can see, the first set of outputs belong to test.sh, which came from the sourcing. This you can see in BASH_SOURCE: "foo source main". The "main" is the main scope, that is the stuff that runs but is not in a function but the main body of the file. In this case, test.sh had the "foo" call which upon sourcing this file will run.

I hope you see how the indices belong to the same call, but yield different info.

Now for your part, you wanted to add only the string and not the whole same-y info again and again. Since I'm not sure you want the stack trace or not I just added the message string to the first calling function instance. I also fixed up some old code here to make it a little better.

New improved function with message:

get_stack () {
   local msg="$@"
   stack_trace=""
   local i stack_size=${#FUNCNAME[@]}
   local indent="  "
   # Offset to skip get_stack function
   for (( i=1; i<$stack_size; i++ )); do
      local func="${FUNCNAME[$i]}"
      [ x$func = x ] && func=MAIN
      local linen="${BASH_LINENO[$(( i - 1 ))]}"
      local src="${BASH_SOURCE[$i]}"
      [ x"$src" = x ] && src=non_file_source
      stack_trace+="${newline:=\n}${indent}-> $src [$func:$linen]${msg:+": $msg"}"
      msg=""
      indent="$indent  "
   done
}

Output:

 $  ./test2.sh
stack from test.sh

  -> test.sh [foo:4]: my message
    -> test.sh [source:28]
      -> ./test2.sh [main:3]
stack from test2.sh

  -> test.sh [foo:4]: my message
    -> ./test2.sh [main:6]

Note that ${var:=X} will initialize "var" with "X" if var was uninitialized or set to empty string, ${var:+X} will replace "var" with "X" if var is initialized/set to something that is not the empty string, and finally as bonus ${var:-X} will replace "var" with "X" if var is uninitialized/set empty string. This is variable substitution in bash which is quite handy!

There are some pieces you can cut and past into your function, or if you need a stack based log you can use my function as a base.

Hope this helps you in your endeavors!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文