¿Cómo puedo encontrar dónde se define cierta función bash?

28

Hay muchas funciones que se pueden usar en Bash Shell. Sus definiciones se pueden enumerar por set, pero ¿cómo encontrar en qué archivos se definen ciertas funciones definidas por el usuario?

jarno
fuente
1
Mire: askubuntu.com/questions/463462/…
FedonKadifeli

Respuestas:

32

Activa la depuración. Del manual de Bash :

extdebug

Si se establece en la invocación del shell, o en un archivo de inicio del shell, organice la ejecución del perfil del depurador antes de que se inicie el shell, idéntico a la --debuggeropción. Si se establece después de la invocación, se habilita el comportamiento destinado a ser utilizado por los depuradores:

  • La -Fopción para el declarebuiltin (ver Bash Builtins ) muestra el nombre del archivo fuente y el número de línea correspondiente a cada nombre de función proporcionado como argumento.

Ejemplo:

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

Y de hecho:

$ 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
fuente
Esta es una excelente y simple solución. En cuanto a su ejemplo, no es incluso necesario para iniciar un nuevo shell: shopt -s extdebug; declare -Ff quote; shopt -u extdebug.
jarno
2
@jarno ah, bueno, al contrario de mi nombre, uso zsh. Es por eso que comencé un nuevo shell. : D
bashity mcbashface
¿Existe un método similar para encontrar ubicaciones de declaración de alias ?
FedonKadifeli
@fedon mi enfoque complicado y complicado a continuación debería funcionar.
terdon
1
Se podría hacer de esto una función como esta: find_function()( shopt -s extdebug; declare -F "$@"; ). Con los parens en el cuerpo de la función, se ejecuta en una subshell y el cambio a shopts no afecta a la persona que llama. Y el -fno parece ser necesario.
wjandrea
15

Esto es realmente más complicado de lo que parece al principio. Los archivos que lee su shell dependen del tipo de shell que esté ejecutando actualmente. Si es interactivo o no, si se trata de un shell de inicio de sesión o no, y qué combinación de lo anterior. Para buscar en todos los archivos predeterminados que pueden leer los diferentes shells, puede hacer (cambiar $functionNameel nombre real de la función que está buscando):

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

Si eso no funciona, puede estar llamando a un archivo no predeterminado usando .o su alias source. Para encontrar estos casos, ejecute:

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

Eso probablemente necesita alguna explicación. El -Ppermite Perl Compatible Regular Expressions (PCRE), que vamos a utilizar una sintaxis de expresiones regulares más elegante. Específicamente:

  • (^|\s): coincide con el comienzo de una línea ( ^) o un espacio en blanco ( \s).
  • (\.|source)\s+: coincide con un .carácter literal ( \.) o la palabra source, pero solo si van seguidos de uno o más espacios en blanco.

Esto es lo que me da en mi 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

Como puede ver, sin embargo, esto imprimirá toda la línea coincidente. Lo que realmente nos interesa es la lista de nombres de archivos llamados, no la línea que los llama. Puede obtener aquellos con esto, más complicado, 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

La -hbandera suprime la impresión de los nombres de los archivos donde se encontró una coincidencia, lo que grepocurre de manera predeterminada cuando se le pide que busque en varios archivos. Los -omedios "solo imprimen la parte correspondiente de la línea". Las cosas adicionales agregadas a la expresión regular son:

  • \K: ignora todo lo que coincida hasta este punto. Este es un truco PCRE que le permite usar una expresión regular compleja para encontrar su coincidencia, pero no incluye esa parte coincidente cuando se usa la -obandera de grep .

En mi sistema, el comando anterior devolverá:

$ 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

Tenga en cuenta que tengo un uso .seguido de un espacio que no se usa para el abastecimiento, pero eso es porque tengo un alias que llama a otro idioma, no bash. Eso es lo que da lo extraño $a*100/$cy ")\n"'en la salida anterior. Pero eso puede ser ignorado.

Finalmente, aquí se explica cómo juntar todo eso y buscar el nombre de una función en todos los archivos predeterminados y todos los archivos de los que provienen sus archivos predeterminados:

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
}

Agregue esas líneas a su ~/.bashrcy luego puede ejecutar (estoy usando fooBarcomo un nombre de función de ejemplo):

grep_function fooBar

Por ejemplo, si tengo esta línea en mi ~/.bashrc:

. ~/a

Y el archivo ~/aes:

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

Debería encontrarlo con:

$ grep_function fooBar
/home/terdon/a:fooBar(){
terdon
fuente
Su solución es un gran ejemplo educativo de secuencias de comandos, pero creo que la solución de bashity mcbashface es más simple.
jarno
@jarno y es mucho, mucho más simple! :)
terdon
Soporte de matrices+=
D. Ben Knoble
@ D.BenKnoble que hacen? ¿Te refieres a que no sea array+="foo"agregar la cadena fooal primer elemento de la matriz?
Terdon
1
@ D.BenKnoble gracias! ¡No sabía que las matrices bash admitían esa notación!
terdon
2

La lectura habitual de archivos de puntos por usuario bashes ~/.bashrc. Sin embargo, puede muy bien obtener otros archivos, por ejemplo, me gusta mantener los alias y las funciones en archivos separados llamados ~/.bash_aliasesy ~/.bash_functions, lo que hace que sea mucho más fácil encontrarlos. Puede buscar en el .bashrcde sourcecomandos con:

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

Una vez que tenga la lista de archivos creados por el usuario, puede buscarlos y al usuario .bashrccon una sola grepllamada, por ejemplo, para la función foode mi configuración:

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