There are many functions that can be used in Bash shell. Their definitions can be listed by set
, but how to find in which files certain user defined functions are defined?

- 5,600
-
1Look at: https://askubuntu.com/questions/463462/sequence-of-scripts-sourced-upon-login – FedKad May 26 '19 at 09:49
3 Answers
Turn on debugging. From the Bash manual:
extdebug
If set at shell invocation, or in a shell startup file, arrange to execute the debugger profile before the shell starts, identical to the
--debugger
option. If set after invocation, behavior intended for use by debuggers is enabled:
- The
-F
option to thedeclare
builtin (see Bash Builtins) displays the source file name and line number corresponding to each function name supplied as an argument.
Example:
$ bash --debugger
$ declare -Ff quote
quote 143 /usr/share/bash-completion/bash_completion
And indeed:
$ 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()

- 386
- 2
- 2
-
1This is a great and simple solution. As for your example, it is not even needed to start a new shell:
shopt -s extdebug; declare -Ff quote; shopt -u extdebug
. – jarno May 26 '19 at 13:36 -
2@jarno ah, well, contrary to my name, I use zsh. That's why I started a new shell. :D – bashity mcbashface May 26 '19 at 13:48
-
-
-
1You could make this a function like this:
find_function()( shopt -s extdebug; declare -F "$@"; )
. With the parens on the function body, it gets executed in a subshell and the change to shopts doesn't affect the caller. And the-f
doesn't seem to be needed. – wjandrea May 27 '19 at 18:16
This is actually more complicated than it appears at first. Which files are read by your shell depends on what type of shell you are currently running. Whether it is interactive or not, whether it is a login or a non-login shell and what combination of the above. To search through all the default files that can be read by the different shells, you can do (change $functionName
to the actual name of the function you are looking for):
grep "$functionName" ~/.bashrc ~/.profile ~/.bash_profile ~/.bash.login \
~/.bash_aliases /etc/bash.bashrc /etc/profile \
/etc/profile.d/* /etc/environment 2> /dev/null
If that doesn't work, you may be calling a non-default file using .
or its alias source
. To find such cases, run:
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
That probably needs some explanation. The -P
enables Perl Compatible Regular Expressions (PCRE) which let us use some fancier regex syntax. Specifically:
(^|\s)
: match either the beginning of a line (^
) or whitespace (\s
).(\.|source)\s+
: match either a literal.
character (\.
) or the wordsource
, but only if they are followed by one or more whitespace characters.
Here's what that gives me on my 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
As you can see, however, this will print the entire matched line. What we are really interested in is the list of file names called, not the line that is calling them. You can get those with this, more complicated, 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
The -h
flag suppresses the printing of the file names where a match was found, which grep
does by default when told to search through multiple files. The -o
means "only print the matching portion of the line". The extra stuff added to the regex are:
\K
: ignore anything matched up to this point. This is a PCRE trick that lets you use a complex regex to find your match but not include that matched portion when using grep's-o
flag.
On my system, the above command will return:
$ 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
Note that I happen to have a use of .
followed by a space which is not used for sourcing but that's because I have an alias that is calling another language, not bash. That's what gives the weird $a*100/$c
and ")\n"'
in the output above. But that can be ignored.
Finally, here's how to put all of that together and search for a function name in all default files and all files your default files are sourcing:
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
}
Add those lines to your ~/.bashrc
and you can then run (I am using fooBar
as an example function name):
grep_function fooBar
For example, if I have this line in my ~/.bashrc
:
. ~/a
And the file ~/a
is:
$ cat ~/a
fooBar(){
echo foo
}
I should find it with:
$ grep_function fooBar
/home/terdon/a:fooBar(){

- 100,812
-
Your solution is a great educational scripting example, but I find bashity mcbashface's solution simpler. – jarno May 26 '19 at 13:40
-
-
-
@D.BenKnoble they do? You mean other than
array+="foo"
appending the stringfoo
to the 1st element of the array? – terdon May 27 '19 at 19:31 -
-
1@D.BenKnoble thanks! I didn't know bash arrays supported that notation! – terdon May 28 '19 at 12:43
The usual per-user dotfile bash
reads is ~/.bashrc
. However, it may very well source other files, I for instance like to keep aliases and functions in separate files called ~/.bash_aliases
and ~/.bash_functions
, which makes finding them much easier. You can search the .bashrc
for source
commands with:
grep -E '(^\s*|\s)(\.|source)\s' /home/USERNAME/.bashrc
Once you have the list of user-created files you can search them and the user’s .bashrc
with a single grep
call, e.g. for function foo
for my setup:
grep foo /home/USERNAME/.bash{rc,_aliases,_functions}

- 39,982
-
This is not a satisfactory answer. There are a awful lot of functions that are not set by the end user. The questions obviously asks for how to find them. And this is of course a far more interesting use case than finding the functions you have created yourself, which you should normally find again in seconds if you're a bit organized – Atralb Jun 05 '20 at 19:05
-