特定のbash関数が定義されている場所を見つけるにはどうすればよいですか?


回答:


32

デバッグをオンにします。Bashマニュアルから:

extdebug

シェルの呼び出し時、またはシェルの起動ファイルで設定されている場合は、--debuggerオプションと同じように、シェルが起動する前にデバッガープロファイルを実行するように調整し ます。呼び出し後に設定すると、デバッガーによる使用を目的とした動作が有効になります。

  • -Fオプションdeclare組み込みは、(参照バッシュ組み込みコマンド)の引数として供給される各関数名に対応するソースファイル名と行番号を表示します。

例:

$ 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()

これは優れたシンプルなソリューションです。あなたの例としては、それをしても新しいシェルを起動するために必要とされていません:shopt -s extdebug; declare -Ff quote; shopt -u extdebug
ジャーノ

2
@jarnoああ、まあ、私の名前に反して、私はzshを使用しています。それが私が新しいシェルを始めた理由です。:D
bashity mcbashface

エイリアス宣言の場所を見つけるための同様の方法はありますか?
FedonKadifeli

以下の私の複雑で複雑なアプローチが機能するはずです。
テルドン

1
これを次のような関数にすることができますfind_function()( shopt -s extdebug; declare -F "$@"; )。関数の本体に括弧があると、サブシェルで実行され、shoptsへの変更は呼び出し元に影響しません。そして、それ-fは必要ではないようです。
wjandrea

15

これは実際には最初に表示されるよりも複雑です。どのファイルがシェルによって読み取られるかは、現在実行しているシェルのタイプによって異なります。対話型かどうか、ログインシェルか非ログインシェルか、および上記の組み合わせ。さまざまなシェルで読み取れるすべてのデフォルトファイルを検索するには、次のようにします($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 Compatible Regular Expressions(PCRE)が有効になり、より洗練された正規表現構文を使用できます。具体的には:

  • (^|\s):行頭(^)または空白(\s)のいずれかに一致します。
  • (\.|source)\s+:リテラル.文字(\.)または単語のいずれかに一致しますが、sourceその後に1つ以上の空白文字が続く場合のみ。

私のシステムで得られるものは次のとおりです。

$ 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:この時点までに一致したものはすべて無視します。これは、複雑な正規表現を使用して一致を見つけることができますが、grepの-oフラグを使用するときに一致した部分を含めないPCREトリックです。

私のシステムでは、上記のコマンドは以下を返します:

$ 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(){

ソリューションは教育用の優れたスクリプト例ですが、bashity mcbashfaceのソリューションは簡単です。
ジャーノ

@jarno、それははるかに簡単です!:)
テルドン

配列はサポート+=
D.ベンKnobleを

@ D.BenKnoble彼らはしますか?あなたは、配列の最初の要素にarray+="foo"文字列fooを追加する以外のことを意味しますか?
テルドン

1
@ D.BenKnobleありがとう!bash配列がその表記法をサポートしていることを知りませんでした!
テルドン

2

通常のドットファイルは、ユーザーごとのbash読みがあります~/.bashrc。ただし、他のファイルを非常によくソースする可能性があります。たとえば、エイリアスと関数を~/.bash_aliasesandと呼ばれる別のファイルに保存しておくと~/.bash_functions、それらを簡単に見つけることができます。あなたは検索できる.bashrcためsourceでコマンド:

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

ユーザーが作成したファイルのリストを取得したら、たとえば、私のセットアップの機能のために、.bashrc1回のgrep呼び出しでそれらとユーザーのファイルを検索できfooます。

grep foo /home/USERNAME/.bash{rc,_aliases,_functions}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.