¿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?
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.
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):
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.
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:
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 .
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"fidone}
Agregue esas líneas a su ~/.bashrcy luego puede ejecutar (estoy usando fooBarcomo un nombre de función de ejemplo):
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:
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:
Respuestas:
Activa la depuración. Del manual de Bash :
Ejemplo:
Y de hecho:
fuente
shopt -s extdebug; declare -Ff quote; shopt -u extdebug
.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-f
no parece ser necesario.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
$functionName
el nombre real de la función que está buscando):Si eso no funciona, puede estar llamando a un archivo no predeterminado usando
.
o su aliassource
. Para encontrar estos casos, ejecute:Eso probablemente necesita alguna explicación. El
-P
permite 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 palabrasource
, pero solo si van seguidos de uno o más espacios en blanco.Esto es lo que me da en mi sistema:
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:
La
-h
bandera suprime la impresión de los nombres de los archivos donde se encontró una coincidencia, lo quegrep
ocurre de manera predeterminada cuando se le pide que busque en varios archivos. Los-o
medios "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-o
bandera de grep .En mi sistema, el comando anterior devolverá:
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/$c
y")\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:
Agregue esas líneas a su
~/.bashrc
y luego puede ejecutar (estoy usandofooBar
como un nombre de función de ejemplo):Por ejemplo, si tengo esta línea en mi
~/.bashrc
:Y el archivo
~/a
es:Debería encontrarlo con:
fuente
+=
array+="foo"
agregar la cadenafoo
al primer elemento de la matriz?La lectura habitual de archivos de puntos por usuario
bash
es~/.bashrc
. Sin embargo, puede muy bien obtener otros archivos, por ejemplo, me gusta mantener los alias y las funciones en archivos separados llamados~/.bash_aliases
y~/.bash_functions
, lo que hace que sea mucho más fácil encontrarlos. Puede buscar en el.bashrc
desource
comandos con:Una vez que tenga la lista de archivos creados por el usuario, puede buscarlos y al usuario
.bashrc
con una solagrep
llamada, por ejemplo, para la funciónfoo
de mi configuración:fuente