Como posso encontrar onde determinada função bash está definida?
28
Existem muitas funções que podem ser usadas no shell Bash. Suas definições podem ser listadas por set, mas como encontrar em quais arquivos determinadas funções definidas pelo usuário são definidas?
Se definido na chamada do shell ou em um arquivo de inicialização do shell, organize a execução do perfil do depurador antes do início do shell, idêntico à
--debuggeropção. Se definido após a invocação, o comportamento destinado ao uso por depuradores será ativado:
A -Fopção para o built-indeclare (consulte Bash Builtins ) exibe o nome do arquivo de origem e o número da linha correspondente a cada nome de função fornecido como argumento.
Esta é uma solução excelente e simples. Quanto ao seu exemplo, não é mesmo necessário para iniciar um novo shell: shopt -s extdebug; declare -Ff quote; shopt -u extdebug.
jarno
2
@jarno ah, bem, ao contrário do meu nome, eu uso o zsh. Por isso, comecei um novo shell. : D
bashity mcbashface
Existe um método semelhante para encontrar locais de declaração de alias ?
FedonKadifeli
@fedon minha abordagem complicada e complicada abaixo deve funcionar.
terdon
1
Você poderia fazer isso uma função como esta: find_function()( shopt -s extdebug; declare -F "$@"; ). Com os parênteses no corpo da função, ele é executado em um subshell e a alteração para shopts não afeta o chamador. E o -fnão parece ser necessário.
wjandrea
15
Isso é realmente mais complicado do que parece à primeira vista. Quais arquivos são lidos pelo seu shell depende do tipo de shell que você está executando no momento. Seja interativo ou não, seja um shell de logon ou não-logon e qual combinação dos itens acima. Para pesquisar em todos os arquivos padrão que podem ser lidos pelos diferentes shells, você pode fazer (mude $functionNamepara o nome real da função que está procurando):
Isso provavelmente precisa de alguma explicação. Ele -Phabilita PCRE (Perl Compatible Regular Expressions), que nos permite usar alguma sintaxe de regex mais sofisticada. Especificamente:
(^|\s): corresponde ao início de uma linha ( ^) ou espaço em branco ( \s).
(\.|source)\s+: corresponde a um .caractere literal ( \.) ou à palavra source, mas somente se forem seguidos por um ou mais caracteres de espaço em branco.
Como você pode ver, no entanto, isso imprimirá toda a linha correspondente. O que realmente interessa é a lista de nomes de arquivos chamados, não a linha que os chama. Você pode obter aqueles com este regex mais complicado:
O -hsinalizador suprime a impressão dos nomes dos arquivos onde uma correspondência foi encontrada, o que grepocorre por padrão quando solicitado a pesquisar em vários arquivos. Os -omeios "apenas imprimem a parte correspondente da linha". O material extra adicionado ao regex é:
\K: ignore qualquer coisa que corresponda a esse ponto. Esse é um truque do PCRE que permite usar um regex complexo para encontrar sua correspondência, mas não incluir a parte correspondente ao usar o -osinalizador grep .
Observe que, por acaso, uso um .seguido de um espaço que não é usado para fornecimento, mas é porque eu tenho um alias que está chamando outro idioma, não o bash. Isso é o que dá o estranho $a*100/$ce ")\n"'na saída acima. Mas isso pode ser ignorado.
Por fim, veja como reunir tudo isso e procurar um nome de função em todos os arquivos padrão e em todos os arquivos que seus arquivos padrão estão procurando:
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}
Adicione essas linhas ao seu ~/.bashrce você poderá executar (estou usando fooBarcomo exemplo o nome da função):
grep_function fooBar
Por exemplo, se eu tiver essa linha no meu ~/.bashrc:
Sua solução é um ótimo exemplo de script educacional, mas acho a solução do bashity mcbashface mais simples.
jarno
@jarno e é muito, muito mais simples! :)
terdon
Matrizes de suporte+=
D. Ben Knoble
@ D.BenKnoble eles fazem? Você quer dizer além de array+="foo"anexar a string fooao 1º elemento da matriz?
terdon
1
@ D.BenKnoble obrigado! Eu não sabia que os arrays do bash suportavam essa notação!
terdon
2
O habitual por usuário dotfile bashlê é ~/.bashrc. No entanto, pode muito bem fonte outros arquivos, por exemplo, eu gosto de manter aliases e funções em arquivos separados chamados ~/.bash_aliasese ~/.bash_functions, o que torna muito mais fácil encontrá-los. Você pode pesquisar o .bashrcpara sourcecomandos com:
Depois de ter a lista de arquivos criados pelo usuário, você pode procurá-los e o usuário .bashrccom uma única grepchamada, por exemplo, a função foopara minha configuração:
Respostas:
Ative a depuração. A partir do manual Bash :
Exemplo:
E realmente:
fonte
shopt -s extdebug; declare -Ff quote; shopt -u extdebug
.find_function()( shopt -s extdebug; declare -F "$@"; )
. Com os parênteses no corpo da função, ele é executado em um subshell e a alteração para shopts não afeta o chamador. E o-f
não parece ser necessário.Isso é realmente mais complicado do que parece à primeira vista. Quais arquivos são lidos pelo seu shell depende do tipo de shell que você está executando no momento. Seja interativo ou não, seja um shell de logon ou não-logon e qual combinação dos itens acima. Para pesquisar em todos os arquivos padrão que podem ser lidos pelos diferentes shells, você pode fazer (mude
$functionName
para o nome real da função que está procurando):Se isso não funcionar, você pode estar chamando um arquivo não padrão usando
.
ou seu aliassource
. Para encontrar esses casos, execute:Isso provavelmente precisa de alguma explicação. Ele
-P
habilita PCRE (Perl Compatible Regular Expressions), que nos permite usar alguma sintaxe de regex mais sofisticada. Especificamente:(^|\s)
: corresponde ao início de uma linha (^
) ou espaço em branco (\s
).(\.|source)\s+
: corresponde a um.
caractere literal (\.
) ou à palavrasource
, mas somente se forem seguidos por um ou mais caracteres de espaço em branco.Aqui está o que isso me dá no meu sistema:
Como você pode ver, no entanto, isso imprimirá toda a linha correspondente. O que realmente interessa é a lista de nomes de arquivos chamados, não a linha que os chama. Você pode obter aqueles com este regex mais complicado:
O
-h
sinalizador suprime a impressão dos nomes dos arquivos onde uma correspondência foi encontrada, o quegrep
ocorre por padrão quando solicitado a pesquisar em vários arquivos. Os-o
meios "apenas imprimem a parte correspondente da linha". O material extra adicionado ao regex é:\K
: ignore qualquer coisa que corresponda a esse ponto. Esse é um truque do PCRE que permite usar um regex complexo para encontrar sua correspondência, mas não incluir a parte correspondente ao usar o-o
sinalizador grep .No meu sistema, o comando acima retornará:
Observe que, por acaso, uso um
.
seguido de um espaço que não é usado para fornecimento, mas é porque eu tenho um alias que está chamando outro idioma, não o bash. Isso é o que dá o estranho$a*100/$c
e")\n"'
na saída acima. Mas isso pode ser ignorado.Por fim, veja como reunir tudo isso e procurar um nome de função em todos os arquivos padrão e em todos os arquivos que seus arquivos padrão estão procurando:
Adicione essas linhas ao seu
~/.bashrc
e você poderá executar (estou usandofooBar
como exemplo o nome da função):Por exemplo, se eu tiver essa linha no meu
~/.bashrc
:E o arquivo
~/a
é:Eu deveria encontrá-lo com:
fonte
+=
array+="foo"
anexar a stringfoo
ao 1º elemento da matriz?O habitual por usuário dotfile
bash
lê é~/.bashrc
. No entanto, pode muito bem fonte outros arquivos, por exemplo, eu gosto de manter aliases e funções em arquivos separados chamados~/.bash_aliases
e~/.bash_functions
, o que torna muito mais fácil encontrá-los. Você pode pesquisar o.bashrc
parasource
comandos com:Depois de ter a lista de arquivos criados pelo usuário, você pode procurá-los e o usuário
.bashrc
com uma únicagrep
chamada, por exemplo, a funçãofoo
para minha configuração:fonte