I have defined a bash function in my ~/.bashrc file. This allows me to use it in shell terminals. However, it does not seem to exist when I call it from within a script.
How can I define a bash function to be used by scripts as well?
I have defined a bash function in my ~/.bashrc file. This allows me to use it in shell terminals. However, it does not seem to exist when I call it from within a script.
How can I define a bash function to be used by scripts as well?
~/.bash_profile and ~/.bashrc are not read by scripts, and functions are not exported by default. To do so, you can use export -f like so:
$ cat > script << 'EOF'
#!/bin/bash
foo
EOF
$ chmod a+x script
$ ./script
./script: line 2: foo: command not found
$ foo() { echo "works" ; }
$ export -f foo
$ ./script
works
export -f foo could also be called in ~/.bash_profile to make this function available to scripts after login. Be warned that export -f is not portable.
A better solution would be to source the file containing the function using . file. This is much more portable, and doesn't rely on your environment being set up in a particular way.
.bashrc is only read by interactive shells. (Actually, that's an oversimplification: bash is quirky in this respect. Bash doesn't read .bashrc if it's a login shell, interactive or not. And there's an exception even to the exception: if bash's parent process is rshd or sshd, then bash does read .bashrc, whether it's interactive or not.)
Put your function definitions in a file in a known place, and use the . (also spelled source) builtin to include that file in a script.
$ cat ~/lib/bash/my_functions.bash
foo () {
…
$ cat ~/bin/myscript
#!/bin/bash
. ~/lib/bash/my_functions.bash
foo bar
If you want, you can follow ksh's autoload feature. Put each function definition in a file with the same name as the function. List the directories containing the function definitions in the FPATH variable (a colon-separated list of directories). Here's a crude approximation of ksh's autoload which actually loads the function immediately instead of on-demand:
autoload () {
set -- "$(set +f; IFS=:;
for d in $FPATH; do
if [ -r "$d/$1" ]; then echo -E "$d/$1"; break; fi;
done)"
[[ -n $1 ]] && . "$1"
}
Do you need a function? If not, consider pulling out the logic into a separate, stand-alone Bash script in your $PATH. For example, I had this in my ~/.bashrc:
# echo public IP address
alias wanip='dig +short myip.opendns.com @resolver1.opendns.com'
~/bin is in my $PATH, so I created ~/bin/wanip with the following contents:
#!/bin/bash
# echo public IP address
dig +short myip.opendns.com @resolver1.opendns.com
And ran chmod 0755 ~/bin/wanip to make it executable. Now I can execute wanip from other scripts.
I like having wanip in a stand-alone Bash script. It reminds me that I want this logic generally available (besides just in my current interactive Bash session). The script nicely encapsulates the logic and documentation for same.