我如何找到某个 Bash 函数的定义位置?

我如何找到某个 Bash 函数的定义位置?

Bash shell 中可以使用许多函数。它们的定义可以通过 列出set,但如何找到某些用户定义函数在哪些文件中定义?

答案1

开启调试。从Bash 手册

扩展调试

如果在 shell 调用时设置,或者在 shell 启动文件中设置,则安排在 shell 启动之前执行调试器配置文件,与选项相同 --debugger。如果在调用后设置,则启用供调试器使用的行为:

  • -F内置选项(declare参见Bash 内置函数) 显示与作为参数提供的每个函数名称相对应的源文件名和行号。

例子:

$ bash --debugger
$ declare -Ff quote
quote 143 /usr/share/bash-completion/bash_completion

确实如此:

$ nl -ba /usr/share/bash-completion/bash_completion | sed -n 143,150p
   143  quote()
   144  {
   145      local quoted=${1//\'/\'\\\'\'}
   146      printf "'%s'" "$quoted"
   147  }
   148
   149  # @see _quote_readline_by_ref()
   150  quote_readline()

答案2

这实际上比乍一看要复杂得多。你的 shell 读取哪些文件取决于什么类型的壳您当前正在运行的程序。它是否是交互式的、是登录 shell 还是非登录 shell,以及上述内容的组合。要搜索不同 shell 可以读取的所有默认文件,您可以执行以下操作(更改$functionName为您要查找的函数的实际名称):

grep "$functionName" ~/.bashrc ~/.profile ~/.bash_profile ~/.bash.login \
                     ~/.bash_aliases /etc/bash.bashrc /etc/profile \
                     /etc/profile.d/* /etc/environment 2> /dev/null

如果这不起作用,您可能正在使用.或其别名调用非默认文件source。要查找此类情况,请运行:

grep -P '(^|\s)(\.|source)\s+' ~/.bashrc ~/.profile ~/.bash_profile \
                               ~/.bash.login ~/.bash_aliases /etc/bash.bashrc \
                               /etc/profile /etc/profile.d/* /etc/environment 2> /dev/null

这可能需要一些解释。-P启用 Perl 兼容正则表达式 (PCRE),让我们可以使用一些更高级的正则表达式语法。具体来说:

  • (^|\s): 匹配行首 ( ^) 或空格 ( \s)。
  • (\.|source)\s+: 匹配文字.字符(\.)或单词source,但前提是它们后面必须跟着一个或多个空格字符。

以下是我的系统上显示的内容:

$ grep -P '(^|\s)(\.|source)\s+' ~/.bashrc ~/.profile ~/.bash_profile \
>                                ~/.bash.login ~/.bash_aliases /etc/bash.bashrc \
>                                /etc/profile /etc/profile.d/* /etc/environment 2> /dev/null
/home/terdon/.bashrc:   . /etc/bashrc
/home/terdon/.bashrc:   . /etc/bash_completion
/home/terdon/.bashrc:. $HOME/scripts/git-prompt.sh
/home/terdon/.bashrc:#  echo -n "$n : "; grep "^CA"  $n |perl -e 'my ($a,$c)=0; while(<>){$c++;next if /cellular_component_unknown/; next if /biological_process/; $a++} print "$a Classes of $c annotated (" . $a*100/$c . ")\n"' 
/etc/bash.bashrc:[ -r /usr/share/bash-completion/bash_completion   ] && . /usr/share/bash-completion/bash_completion
/etc/profile:       test -r "$profile" && . "$profile"
/etc/profile:   . /etc/bash.bashrc
/etc/profile.d/locale.sh:    . "$XDG_CONFIG_HOME/locale.conf"
/etc/profile.d/locale.sh:    . "$HOME/.config/locale.conf"
/etc/profile.d/locale.sh:    . /etc/locale.conf
/etc/profile.d/Z97-byobu.sh:        . /usr/bin/byobu-launch
/etc/profile.d/Z97-byobu.sh:        . /usr/bin/byobu-launch
/etc/profile.d/Z97-byobu.sh:        . /usr/bin/byobu-launch
/etc/profile.d/Z97-byobu.sh:        . /usr/bin/byobu-launch
/etc/profile.d/Z97-byobu.sh:        . /usr/bin/byobu-launch

但是,如您所见,这将打印整个匹配的行。我们真正感兴趣的是调用的文件名列表,而不是调用它们的行。您可以使用这个更复杂的正则表达式获取它们:

grep -hPo '(^|\s)(\.|source)\s+\K\S+' ~/.bashrc ~/.profile ~/.bash_profile \
                                      ~/.bash.login ~/.bash_aliases \
                                      /etc/bash.bashrc /etc/profile \
                                      /etc/profile.d/* /etc/environment 2> /dev/null

-h标志禁止打印找到匹配项的文件名,grep默认情况下,当被告知搜索多个文件时会执行此操作。-o意思是“仅打印行的匹配部分”。添加到正则表达式的额外内容是:

  • \K:忽略到目前为止匹配的任何内容。这是一个 PCRE 技巧,可让您使用复杂的正则表达式来查找匹配项,但在使用 grep 的-o标志时不包括匹配的部分。

在我的系统上,上述命令将返回:

$ grep -hPo '(^|\s)(\.|source)\s+\K\S+' ~/.bashrc ~/.profile ~/.bash_profile \
>                                       ~/.bash.login ~/.bash_aliases \
>                                       /etc/bash.bashrc /etc/profile \
>                                       /etc/profile.d/* /etc/environment 2> /dev/null
/etc/bashrc
/etc/bash_completion
$HOME/scripts/git-prompt.sh
$a*100/$c
")\n"'
/usr/share/bash-completion/bash_completion
"$profile"
/etc/bash.bashrc
"$XDG_CONFIG_HOME/locale.conf"
"$HOME/.config/locale.conf"
/etc/locale.conf
/usr/bin/byobu-launch
/usr/bin/byobu-launch
/usr/bin/byobu-launch
/usr/bin/byobu-launch
/usr/bin/byobu-launch

请注意,我恰好使用了后面.跟着一个空格的不是用于采购,但那是因为我有一个调用另一种语言的别名,而不是 bash。这就是上面输出中奇怪的结果。但可以忽略它$a*100/$c")\n"'

最后,下面介绍如何将所有这些放在一起并在所有默认文件中搜索函数名称您的默认文件所来源的所有文件:

grep_function(){
  target="$@"
  files=( ~/.bashrc ~/.profile ~/.bash_profile ~/.bash.login 
         ~/.bash_aliases /etc/bash.bashrc /etc/profile 
         /etc/profile.d/* /etc/environment)
    while IFS= read -r file; do
      files+=( "$file" )
    done < <(grep -hPo '(^|\s)(\.|source)\s+\K\S+'  "${files[@]}" 2>/dev/null)
    for file in "${files[@]}"; do
      ## The tilde of ~/ can break this
      file=$(sed 's|~/|'"$HOME"'/|g' <<<"$file")
      if [[ -e $file ]]; then
        grep -H "$target" -- "$file"
      fi
    done
}

将这些行添加到您的~/.bashrc然后您可以运行(我将其用作fooBar示例函数名称):

grep_function fooBar

例如,如果我的中有此行~/.bashrc

. ~/a

该文件~/a是:

$ cat ~/a
fooBar(){
  echo foo
}

我应该用以下方法找到它:

$ grep_function fooBar
/home/terdon/a:fooBar(){

答案3

通常每个用户的 dotfilebash读取的是~/.bashrc。但是,它也可能读取其他文件,例如,我喜欢将别名和函数保存在名为~/.bash_aliases和 的单独文件中~/.bash_functions,这样可以更轻松地找到它们。您可以使用以下命令搜索.bashrcforsource命令:

grep -E '(^\s*|\s)(\.|source)\s' /home/USERNAME/.bashrc

一旦您有了用户创建的文件列表,您就可以.bashrc通过一次grep调用来搜索这些文件和用户的文件,例如foo针对我的设置的功能:

grep foo /home/USERNAME/.bash{rc,_aliases,_functions}

相关内容