Jak mogę znaleźć, gdzie zdefiniowano określoną funkcję bash?


Odpowiedzi:


32

Włącz debugowanie. Z podręcznika Bash :

extdebug

Jeśli ustawione przy wywołaniu powłoki lub w pliku startowym powłoki, należy ustawić profil debuggera przed uruchomieniem powłoki, identycznie jak w --debuggeropcji. Jeśli ustawione po wywołaniu, zachowanie przeznaczone do użycia przez debuggery jest włączone:

  • -FOpcja do declarewbudowanego polecenia (zobacz Bash builtins ) wyświetla nazwę pliku źródłowego i linia numer odpowiadający każdej nazwy funkcji dostarczanych jako argument.

Przykład:

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

I rzeczywiście:

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

To świetne i proste rozwiązanie. Jak dla przykładu, nie jest nawet konieczne, aby rozpocząć nową powłokę: shopt -s extdebug; declare -Ff quote; shopt -u extdebug.
jarno

2
@jarno ah, cóż, w przeciwieństwie do mojego imienia, używam zsh. Dlatego zacząłem nową powłokę. : D
bashity mcbashface

Czy istnieje podobna metoda znajdowania lokalizacji deklaracji aliasów ?
FedonKadifeli

@fedon moje zawiłe i skomplikowane podejście poniżej powinno zadziałać.
terdon

1
Można zrobić to funkcja tak: find_function()( shopt -s extdebug; declare -F "$@"; ). Dzięki parens na ciele funkcji, jest wykonywany w podpowłoce, a zmiana na shopts nie wpływa na wywołującego. I -fnie wydaje się to potrzebne.
wjandrea

15

Jest to w rzeczywistości bardziej skomplikowane, niż się wydaje. To, które pliki są odczytywane przez powłokę, zależy od typu aktualnie uruchomionej powłoki . Niezależnie od tego, czy jest to interaktywna, czy nie, czy jest to powłoka do logowania, czy bez logowania, oraz jaka kombinacja powyższych. Aby przeszukać wszystkie domyślne pliki, które mogą być odczytane przez różne powłoki, możesz to zrobić (zmień $functionNamena rzeczywistą nazwę szukanej funkcji):

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

Jeśli to nie zadziała, być może dzwonisz do pliku innego niż domyślny przy użyciu .lub jego aliasu source. Aby znaleźć takie przypadki, uruchom:

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

To chyba wymaga wyjaśnienia. -PUmożliwia pcre (PCRE), które pozwalają nam używać trochę bardziej wyszukane składni regex. Konkretnie:

  • (^|\s): dopasuj początek linii ( ^) lub białego znaku ( \s).
  • (\.|source)\s+: dopasuj literał .( \.) lub słowo source, ale tylko wtedy, gdy po nich następuje jeden lub więcej białych znaków.

Oto, co daje mi mój 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

Jak widać, spowoduje to wydrukowanie całej dopasowanej linii. Najbardziej interesuje nas lista nazw plików, a nie linia, która je wywołuje. Możesz uzyskać te za pomocą tego, bardziej skomplikowanego wyrażenia regularnego:

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

-hFlaga hamuje drukowanie nazwy pliku, w którym mecz został znaleziony, który greprobi domyślnie kiedy powiedział do przeszukiwania wielu plików. Te -ośrodki „tylko wydrukować część dopasowanie linii”. Dodatkowe rzeczy dodane do wyrażenia regularnego to:

  • \K: zignoruj ​​wszystko dopasowane do tego momentu. Jest to sztuczka PCRE, która pozwala użyć złożonego wyrażenia regularnego, aby znaleźć dopasowanie, ale nie obejmuje tej dopasowanej części, gdy używasz -oflagi grep .

W moim systemie powyższe polecenie zwróci:

$ 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

Zauważ, że zdarza mi się używać znaku .spacji, który nie jest używany do pozyskiwania, ale to dlatego, że mam alias, który woła inny język, a nie bash. To daje dziwne $a*100/$ci ")\n"'powyższe wyniki. Ale można to zignorować.

Na koniec, oto jak to wszystko połączyć i wyszukać nazwę funkcji we wszystkich domyślnych plikach i we wszystkich plikach, które pozyskują Twoje domyślne pliki:

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
}

Dodaj te wiersze do swojego ~/.bashrci możesz następnie uruchomić (używam fooBarjako przykładowej nazwy funkcji):

grep_function fooBar

Na przykład, jeśli mam tę linię w moim ~/.bashrc:

. ~/a

Plik ~/ato:

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

Powinienem to znaleźć z:

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

Twoje rozwiązanie jest świetnym przykładem skryptu edukacyjnego, ale uważam, że rozwiązanie bashity mcbashface jest prostsze.
jarno

@jarno i jest o wiele, wiele prostsze! :)
terdon

Obsługa tablic+=
D. Ben Knoble

@ D.BenKnoble robią? Masz na myśli inne niż array+="foo"dołączenie ciągu foodo pierwszego elementu tablicy?
terdon

1
@ D.BenKnoble dzięki! Nie wiedziałem, że tablice bash obsługują ten zapis!
terdon

2

Zwykle per-user dotfile bashczyta to ~/.bashrc. Może jednak bardzo dobrze pozyskiwać inne pliki, na przykład lubię przechowywać aliasy i funkcje w osobnych plikach o nazwie ~/.bash_aliasesi ~/.bash_functions, co znacznie ułatwia ich znalezienie. Możesz wyszukiwać polecenia .bashrcza sourcepomocą:

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

Gdy masz już listę plików utworzonych przez użytkownika, możesz je wyszukać i użytkownika .bashrcza pomocą jednego greppołączenia, np. W celu uzyskania funkcji foodla mojej konfiguracji:

grep foo /home/USERNAME/.bash{rc,_aliases,_functions}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.