Wie finde ich heraus, wo bestimmte Bash-Funktionen definiert sind?

28

Es gibt viele Funktionen, die in der Bash-Shell verwendet werden können. Ihre Definitionen können aufgelistet werden set, aber wie kann man herausfinden, in welchen Dateien bestimmte benutzerdefinierte Funktionen definiert sind?

jarno
quelle
1
Schau mal
FedonKadifeli

Antworten:

32

Aktivieren Sie das Debuggen. Aus dem Bash-Handbuch :

extdebug

Wenn dies beim Aufruf der Shell oder in einer Shell-Startdatei festgelegt wurde, führen Sie das Debugger-Profil vor dem Start der Shell aus (identisch mit der --debuggerOption). Wenn nach dem Aufruf festgelegt, ist das für Debugger vorgesehene Verhalten aktiviert:

  • Die -FOption zum declareBuiltin (siehe Bash-Builtins ) zeigt den Namen der Quelldatei und die Zeilennummer an, die den Funktionsnamen entsprechen, die als Argument angegeben werden.

Beispiel:

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

Und in der Tat:

$ 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()
bashity mcbashface
quelle
Dies ist eine großartige und einfache Lösung. Wie für Ihr Beispiel ist es nicht einmal eine neue Shell starten benötigt: shopt -s extdebug; declare -Ff quote; shopt -u extdebug.
26.
2
@jarno ah, naja, entgegen meinem Namen benutze ich zsh. Deshalb habe ich eine neue Shell gestartet. : D
bashity mcbashface
Gibt es eine ähnliche Methode, um Alias- Deklarationspositionen zu finden ?
FedonKadifeli
@fedon mein gewundener und komplizierter Ansatz unten sollte funktionieren.
Terdon
1
Man könnte dies eine Funktion wie diese machen: find_function()( shopt -s extdebug; declare -F "$@"; ). Mit den Parens auf dem Funktionskörper wird es in einer Subshell ausgeführt, und die Änderung von shopts hat keine Auswirkungen auf den Aufrufer. Und das -fscheint nicht nötig zu sein.
wjandrea
15

Dies ist tatsächlich komplizierter als es zunächst erscheint. Welche Dateien von Ihrer Shell gelesen werden, hängt davon ab, welchen Shell-Typ Sie gerade ausführen. Ob es interaktiv ist oder nicht, ob es sich um eine Login- oder eine Nicht-Login-Shell handelt und welche Kombination der oben genannten. So durchsuchen Sie alle Standarddateien, die von den verschiedenen Shells gelesen werden können $functionName:

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

Wenn dies nicht funktioniert, rufen Sie möglicherweise eine nicht standardmäßige Datei mit .oder ihrem Alias ​​auf source. Um solche Fälle zu finden, führen Sie Folgendes aus:

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

Das bedarf wahrscheinlich einer Erklärung. Das -Paktiviert Perl-kompatible reguläre Ausdrücke (PCRE), mit denen wir eine ausgefeiltere Regex-Syntax verwenden können. Speziell:

  • (^|\s): Entspricht entweder dem Anfang einer Zeile ( ^) oder dem Leerzeichen ( \s).
  • (\.|source)\s+: Entspricht entweder einem Literalzeichen .( \.) oder dem Wort source, jedoch nur, wenn auf dieses ein oder mehrere Leerzeichen folgen.

Folgendes gibt mir auf meinem System:

$ 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

Wie Sie jedoch sehen können, wird die gesamte übereinstimmende Zeile gedruckt. Was uns wirklich interessiert, ist die Liste der aufgerufenen Dateinamen, nicht die Zeile, die sie aufruft. Sie können diese mit diesem, komplizierteren, regulären Ausdruck erhalten:

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

Das -hFlag unterdrückt das Drucken der Dateinamen, bei denen eine Übereinstimmung gefunden wurde. Dies grepgilt standardmäßig, wenn Sie aufgefordert werden, mehrere Dateien zu durchsuchen. Das -obedeutet "nur den passenden Teil der Zeile drucken". Die zusätzlichen Dinge, die zum regulären Ausdruck hinzugefügt werden, sind:

  • \K: Ignoriere alles, was bis zu diesem Punkt gepasst hat. Dies ist ein PCRE-Trick, mit dem Sie einen komplexen regulären Ausdruck verwenden können, um Ihre Übereinstimmung zu finden, aber diesen übereinstimmenden Teil nicht einschließen, wenn Sie das grep- -oFlag verwenden.

Auf meinem System wird der obige Befehl zurückgegeben:

$ 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

Beachten Sie, dass ich zufällig .gefolgt von einem Leerzeichen benutze, das nicht für das Sourcing verwendet wird, aber das liegt daran, dass ich einen Alias ​​habe, der eine andere Sprache aufruft, nicht Bash. Das ist es, was die seltsame $a*100/$cund ")\n"'in der Ausgabe oben gibt. Das kann man aber ignorieren.

Zum Schluss erfahren Sie, wie Sie all das zusammenfassen und in allen Standarddateien und allen Dateien, die Ihre Standarddateien beziehen, nach einem Funktionsnamen suchen :

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
}

Fügen Sie diese Zeilen zu Ihrer hinzu ~/.bashrcund Sie können dann ausführen (ich verwende fooBarals Beispiel einen Funktionsnamen):

grep_function fooBar

Zum Beispiel, wenn ich diese Zeile in meinem habe ~/.bashrc:

. ~/a

Und die Datei ~/aist:

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

Ich sollte es finden mit:

$ grep_function fooBar
/home/terdon/a:fooBar(){
terdon
quelle
Ihre Lösung ist ein großartiges Beispiel für pädagogisches Scripting, aber ich finde die Lösung von bashity mcbashface einfacher.
26.
@jarno und es ist viel, viel einfacher! :)
terdon
Array-Unterstützung+=
D. Ben Knoble
@ D.BenKnoble machen sie das? Sie meinen, außer array+="foo"die Zeichenfolge fooan das erste Element des Arrays anzuhängen?
terdon
1
@ D.BenKnoble danke! Ich wusste nicht, dass Bash-Arrays diese Notation unterstützen!
Terdon
2

Das üblichebash Lesen von Punktdateien pro Benutzer ist ~/.bashrc. Es kann jedoch durchaus sein, dass andere Dateien als Quelle vorliegen. Ich möchte beispielsweise Aliase und Funktionen in separaten Dateien namens ~/.bash_aliasesund aufbewahren ~/.bash_functions, was das Auffinden dieser Dateien erheblich erleichtert. Sie können die Suche .bashrcnach sourceBefehlen mit:

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

Sobald Sie die Liste der vom Benutzer erstellten Dateien haben, können Sie sie und den Benutzer .bashrcmit einem einzigen grepAufruf durchsuchen , z. B. nach der Funktion foofür mein Setup:

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