Come posso trovare dove è definita una determinata funzione bash?


Risposte:


32

Attiva il debug. Dal manuale di Bash :

extdebug

Se impostato su invocazione della shell o in un file di avvio della shell, provvedere all'esecuzione del profilo di debugger prima dell'avvio della shell, identico --debuggerall'opzione. Se impostato dopo l'invocazione, il comportamento destinato all'uso da parte dei debugger è abilitato:

  • L' -Fopzione per il declarebuiltin (vedere Bash Builtins ) visualizza il nome del file di origine e il numero di riga corrispondente a ciascun nome di funzione fornito come argomento.

Esempio:

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

E senza dubbio:

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

Questa è una soluzione grande e semplice. Per quanto riguarda il tuo esempio, non è nemmeno necessario per avviare una nuova shell: shopt -s extdebug; declare -Ff quote; shopt -u extdebug.
jarno,

2
@jarno ah, beh, contrariamente al mio nome, io uso zsh. Ecco perché ho iniziato una nuova shell. : D
bashity mcbashface

Esiste un metodo simile per trovare posizioni di dichiarazione alias ?
FedonKadifeli,

@fedon il mio approccio contorto e complicato di seguito dovrebbe funzionare.
terdon,

1
Si potrebbe fare questo una funzione come questa: find_function()( shopt -s extdebug; declare -F "$@"; ). Con le parentesi nel corpo della funzione, viene eseguita in una sottostruttura e la modifica a shopts non influisce sul chiamante. E il -fnon sembra essere necessario.
wjandrea,

15

Questo è in realtà più complicato di quanto sembri inizialmente. I file letti dalla shell dipendono dal tipo di shell attualmente in esecuzione. Che sia interattivo o no, che si tratti di una shell di accesso o non di accesso e quale combinazione di quanto sopra. Per cercare tra tutti i file predefiniti che possono essere letti dalle diverse shell, è possibile eseguire (passare $functionNameal nome effettivo della funzione che si sta cercando):

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

Se il problema persiste, è possibile che si stia chiamando un file non predefinito utilizzando .o il suo alias source. Per trovare tali casi, eseguire:

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

Questo probabilmente ha bisogno di qualche spiegazione. Il -Ppermette Perl Compatible Regular Expressions (PCRE), che cerchiamo di utilizzare una sintassi regex più elaborato. In particolare:

  • (^|\s): corrisponde all'inizio di una riga ( ^) o uno spazio bianco ( \s).
  • (\.|source)\s+: corrisponde a un .carattere letterale ( \.) o alla parola source, ma solo se sono seguiti da uno o più caratteri bianchi.

Ecco cosa mi dà sul mio sistema:

$ 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

Come puoi vedere, tuttavia, questo stamperà l'intera linea abbinata. Ciò a cui siamo veramente interessati è l'elenco dei nomi dei file chiamati, non la linea che li chiama. Puoi ottenere quelli con questo, più complicato, regex:

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

Il -hflag sopprime la stampa dei nomi dei file in cui è stata trovata una corrispondenza, che grepper impostazione predefinita viene eseguita quando viene richiesto di cercare tra più file. Il -omezzo "stampa solo la parte corrispondente della riga". Le cose extra aggiunte al regex sono:

  • \K: ignora tutto ciò che corrisponde a questo punto. Questo è un trucco di PCRE che ti consente di usare una regex complessa per trovare la tua corrispondenza ma non includere quella porzione corrispondente quando usi la -obandiera di grep .

Sul mio sistema, il comando sopra restituirà:

$ 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

Si noti che mi capita di avere un uso .seguito da uno spazio che non è utilizzato per l'approvvigionamento, ma è perché ho un alias che chiama un'altra lingua, non bash. Questo è ciò che dà lo strano $a*100/$ce ")\n"'nell'output sopra. Ma questo può essere ignorato.

Infine, ecco come mettere tutto insieme e cercare un nome di funzione in tutti i file predefiniti e in tutti i file che i tuoi file predefiniti provengono:

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
}

Aggiungi quelle righe al tuo ~/.bashrce puoi quindi eseguire (sto usando fooBarcome nome di funzione di esempio):

grep_function fooBar

Ad esempio, se ho questa riga nel mio ~/.bashrc:

. ~/a

E il file ~/aè:

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

Dovrei trovarlo con:

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

La tua soluzione è un ottimo esempio di scripting educativo, ma trovo la soluzione bashity di mcbashface più semplice.
jarno,

@jarno ed è molto, molto più semplice! :)
Terdon,

Supporto array+=
D. Ben Knoble

@ D.BenKnoble lo fanno? Intendi altro che array+="foo"aggiungere la stringa fooal primo elemento dell'array?
terdon,

1
@ D.BenKnoble grazie! Non sapevo che bash array supportasse quella notazione!
terdon,

2

Le solitebash letture dotfile per utente sono ~/.bashrc. Tuttavia, può benissimo reperire altri file, ad esempio mi piace mantenere alias e funzioni in file separati chiamati ~/.bash_aliasese ~/.bash_functions, il che rende molto più facile trovarli. È possibile cercare il .bashrcper sourcecomandi con:

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

Una volta che hai l'elenco dei file creati dall'utente puoi cercarli e l'utente .bashrccon una sola grepchiamata, ad es. Per la funzione fooper la mia configurazione:

grep foo /home/USERNAME/.bash{rc,_aliases,_functions}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.