52

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?

2
  • 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, 2016 at 18:06
  • 1
    Related: unix.stackexchange.com/q/322459/117549
    – Jeff Schaller
    Nov 12, 2016 at 18:13

6 Answers 6

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
1
  • "-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, 2016 at 9:57
40

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.

6
  • 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. May 15, 2017 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 3, 2019 at 15:44
  • I do not have ~/.bash_profile in Xubuntu 18.04.
    – jarno
    Dec 3, 2019 at 12:00
  • Why do you strongly recommend having shopt -s extdebug set as default?
    – jarno
    Dec 3, 2019 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 3, 2019 at 16:01
18

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
5
  • The question does specify bash...
    – Jeff Schaller
    Nov 12, 2016 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. Nov 12, 2016 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, 2016 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. Nov 12, 2016 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, 2021 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.

4
-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.

-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

1
  • 1
    Link only answers are absolutely not encouraged. Please add some explanation.
    – heemayl
    Nov 13, 2016 at 3:48

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .