56

I cannot figure out how to find the file where a bash function is defined (__git_ps1 in my case).

I experimented with declare, type, which, but nothing tells me the source file. I read somewhere that declare can print the file name and the line number, but it was not explained how. The help page for declare does not say it either.

How can I get this information?

Alexey
  • 1,988
  • If the path to the function's file is not included in $PATH, then type won't work. You might want to try just using find or locate. locate will be much faster, since it uses a pre-existing database, but it won't work if the command was installed just recently. – user628544 Nov 12 '16 at 18:06
  • 1
    Related: http://unix.stackexchange.com/q/322459/117549 – Jeff Schaller Nov 12 '16 at 18:13

6 Answers6

46

If you are prepared to run the function, then you can get the information by using set -x to trace the execution and setting the PS4 variable.

  1. Start bash with --debugger or else use shopt -s extdebug to record extra debugging info.

  2. Set PS4, the 'prompt' printed when tracing to show the source line.

  3. Turn on tracing.

  4. you can then run your function and for each line you will get the filename of the function.

  5. use set +x to turn off tracing.

So for this case you would run

bash --debugger
PS4='+ ${BASH_SOURCE[0]} '
set -x ; __git_ps1 ; set +x
heemayl
  • 56,300
icarus
  • 17,920
  • "-x After expanding each simple command, for command, case command, select command, or arithmetic for command, display the expanded value of PS4, followed by the command and its expanded arguments or associated word list." Nice! – l0b0 Nov 19 '16 at 09:57
42

If you are not willing to run the function, you can still set up debugging and get the information. The steps are

  1. start bash --debugger or shopt -s extdebug before the function is defined.
  2. declare -F __git_ps1

and it will report where the function is defined.

The advantages of this method compared to seeing the annotated execution trace with PS4 are

  • A lot less output
  • It directly answers the question

The advantages of the execution trace are

  • See all called functions at once
  • See the relations between called functions
  • See recursion

I strongly recommend having shopt -s extdebug at the start of both ~/.bashrc and ~/.bash_profile to cover the different files used in different Invocation cases.

icarus
  • 17,920
  • 2
    It also works if shopt -s extdebug is called after the function is defined. Beware line numbers may be off (a current bug) if the function is declared through eval. – Stéphane Chazelas May 15 '17 at 15:35
  • I was after a misguided completion for vpnc. After bash --debugger I had to actually trigger the completion to get the completion function defined and get it reported from declare -F _vpnc. – Harald Feb 03 '19 at 15:44
  • I do not have ~/.bash_profile in Xubuntu 18.04. – jarno Dec 03 '19 at 12:00
  • Why do you strongly recommend having shopt -s extdebug set as default? – jarno Dec 03 '19 at 12:01
  • @jarno See the manual page, but it allows you to see this information, allows DEBUG traps to be inherited and has no downside as far as I can see. See the link for alternatives to ~/.bash_profile or just create it. – icarus Dec 03 '19 at 16:01
  • Thanks @icarus while I'm not sure I agree wih having the option enabled all the time - this did help a lot, now part of my ~/.bashrc: wherefunc () { (shopt -s extdebug; declare -F "${*}"); } – nhed Apr 19 '21 at 18:14
20

It doesn't seem to be possible in bash, but it is in zsh:

$ type __git_ps1
> __git_ps1 is a shell function from /usr/share/git/git-prompt.sh
pfnuesel
  • 5,837
  • The question does specify bash... – Jeff Schaller Nov 12 '16 at 18:19
  • 5
    @JeffSchaller: The obvious way to use this answer is to set up zsh to source the same files that bash would, then use it to find the definition. This answer isn't suggesting switching to zsh, just that it's obviously a useful tool that understands shell syntax better than generic tools like find / locate / grep. – Peter Cordes Nov 12 '16 at 19:28
  • I would claim, then, @PeterCordes , that this answer currently does not do what you say it should do in order to answer the Question. I don't know if zsh natively reads the same files that bash does. – Jeff Schaller Nov 12 '16 at 21:12
  • @JeffSchaller: Agreed this answer is in need of improvement. zsh almost certainly doesn't read the same ~/.whatever files as bash by default, and will only give useful answers for functions defined in shared locations like in this case, that aren't redefined in ~/.bashrc or whatever. – Peter Cordes Nov 12 '16 at 23:22
  • Based on @icarus's answer wherefunc () { (shopt -s extdebug; declare -F "${*}"); } meaning it is quite possible in bash (not sure about circa 2016) – nhed Apr 19 '21 at 18:13
9

@icarus's great solution works for functions, as long as they are defined literally and not the result of an eval of the contents of another file (in which the file with the eval will show up as the source). It will not print the source file of aliases, shell built-ins (like echo) and executables (binary or not), and I believe this information is not available in general. Some commands might print their source files (and may even be truthful about it), either in the course of normal execution or in response to a signal.

__git_ps1 is defined in /usr/share/git/git-prompt.sh and /usr/share/git/completion/git-prompt.sh on my system, Arch Linux, so it may be the same for you.

Have a look at the Invocation section of man bash if you want look for commands specifically sourced at the start of the shell - they may source other files which in turn source other files.

l0b0
  • 51,350
  • Can one get a list of files that are sourced upon starting bash? – pfnuesel Nov 12 '16 at 16:41
  • @pfnuesel - it's up to you. The defaults are in $HOME/.bashrc and in $HOME/.profile. See: http://www.linuxfromscratch.org/blfs/view/svn/postlfs/profile.html which explains some of the details of when and how each file is sourced. – Joe Nov 19 '16 at 04:40
  • 2
    @Joe https://www.gnu.org/software/bash/manual/html_node/Bash-Startup-Files.html is a much better reference. /etc/profile, $HOME/.bash_profile, $HOME/.bash_login, and the contents of $ENV and $BASH_ENV need to be added to your list. – icarus Nov 19 '16 at 05:32
  • @icarus - Thanks. That is a better explanation. – Joe Nov 20 '16 at 08:49
-1

Declare a function with the same name and set it readonly as early as possible, then activate xtrace mode, something like:

__git_ps1(){ :;}
readonly -f __git_ps1
set -x

After that when you log in you will see trace information that includes sourcing of files. In the moment there is an attempt for a declaration of the existing readonly function, you will see an error message. The last sourced file before it should contain the declaration you look for.

You may need to put this in the system bash profile. Also remember to revert the changes after finding the culprit.

woodengod
  • 493
-3

Did you try this?

grep -rnw '/path/to/somewhere/' -e "pattern" 

or any of the other commands found here:

How do I find all files containing specific text on Linux? | Stack Overflow

It seems that I need to give you more of an explanation. Your question asks "" Thus if you run the following command, it should return all files where a bash function is defined.

grep -rnw 'Path2Search' -e "#!/bin/bash"

BASH Programming - Functions | Linux Documentation Project

RadFox
  • 109