188

I have a script that has some functions.

Can I run one of the function directly from command line?

Something like this?

myScript.sh func()
Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
AAaa
  • 3,659
  • 7
  • 35
  • 40

10 Answers10

341

Well, while the other answers are right - you can certainly do something else: if you have access to the bash script, you can modify it, and simply place at the end the special parameter "$@" - which will expand to the arguments of the command line you specify, and since it's "alone" the shell will try to call them verbatim; and here you could specify the function name as the first argument. Example:

$ cat test.sh
testA() {
  echo "TEST A $1";
}

testB() {
  echo "TEST B $2";
}

"$@"


$ bash test.sh
$ bash test.sh testA
TEST A 
$ bash test.sh testA arg1 arg2
TEST A arg1
$ bash test.sh testB arg1 arg2
TEST B arg2

For polish, you can first verify that the command exists and is a function:

# Check if the function exists (bash specific)
if declare -f "$1" > /dev/null
then
  # call arguments verbatim
  "$@"
else
  # Show a helpful error
  echo "'$1' is not a known function name" >&2
  exit 1
fi
that other guy
  • 116,971
  • 11
  • 170
  • 194
sdaau
  • 36,975
  • 46
  • 198
  • 278
  • 24
    Use `"$@"` in most cases. `$@` is not safe in some cases. – fumiyas Apr 10 '15 at 13:21
  • 1
    How to run more than a function? for example can I run `bash test.sh testA testB` ? – hellojoshhhy Aug 25 '18 at 08:22
  • This also needs to be wrapped with `[ ! -z "$1" ]` otherwise source will raise the else statement in some bash verions – JackLeo Sep 03 '18 at 14:27
  • I've been searching near and far for this magical "$@" variable to use whichever function is called on a file, thank you @sdaau! – Justin Hammond Feb 15 '20 at 07:08
  • What if I have some other code at the beginning of `test.sh` which are not functions? Does it still work? - I tried myself and yes it works! – Ömer An Jul 24 '21 at 09:33
  • how will script know where arguments end and another command begins? `./script.sh func1 arg1 && ./script.sh func2 arg1` will this work? – marko kraljevic Jan 01 '22 at 18:34
  • Here is probably an error: `# Check if the function exists (bash specific) if declare -f "$1" > /dev/null`. $1 refers to the first arg passed to the function and not to the function. The function cannot be addressed with a parameter. $0 is the script. – Timo Apr 12 '22 at 15:44
87

If the script only defines the functions and does nothing else, you can first execute the script within the context of the current shell using the source or . command and then simply call the function. See help source for more information.

Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
  • 4
    The only problem with this method is that if you use `exit` in your function, it will close the terminal after the function executed. Is there any way around this? @SvenMarnach – user1527227 Sep 02 '14 at 23:01
  • @user1527227: Go with the next answer in that case, given you have control of the shell script. If you don't, you can call an interactive subshell first (just enter `bash`), and `exit` will only terminate the subshell, but not your terminal. – Sven Marnach Sep 03 '14 at 10:09
  • 4
    would be good if there was an actual code example for this – cryanbhu Jan 14 '21 at 09:32
70

The following command first registers the function in the context, then calls it:

. ./myScript.sh && function_name
NaN
  • 7,441
  • 6
  • 32
  • 51
TimonWang
  • 882
  • 6
  • 8
  • 3
    You can also simply use source. As in - source myScript.sh && function_name – stolen_leaves Feb 27 '20 at 14:14
  • 5
    may i know why your alternative is simpler? Not being snarky, i just really don't get it? – cryanbhu Jan 14 '21 at 09:35
  • What if user wants to pass some command line arguments/options to the script? – yabhishek Feb 04 '21 at 09:50
  • 2
    @stolen_leaves But what's the point of using source? You can also use `sh myscript.sh && function_name`, `bash myscript.sh && function_name`, `cat myscript.sh && function_name`, and the list goes on and on. But I don't see why you want to do something that is longer. Makes no sense to me. – DaCuteRaccoon Mar 13 '22 at 23:00
  • this causes a disconnect from my VM when I'm in an SSH console. Don't know why, but it does so repeatably, interesting – DanDan Mar 12 '23 at 19:32
27

Briefly, no.

You can import all of the functions in the script into your environment with source (help source for details), which will then allow you to call them. This also has the effect of executing the script, so take care.

There is no way to call a function from a shell script as if it were a shared library.

sorpigal
  • 25,504
  • 8
  • 57
  • 75
  • 2
    I think this should be given more weight as an appropriate answer. I tried to do something similar to what the OP is wanting, but shell scripting simply isn't engineered for "clean-cut, OOP development" (IMHO). – Dan L Dec 18 '14 at 17:23
21

Using case

#!/bin/bash

fun1 () {
    echo "run function1"
    [[ "$@" ]] && echo "options: $@"
}

fun2 () {
    echo "run function2"
    [[ "$@" ]] && echo "options: $@"
}

case $1 in
    fun1) "$@"; exit;;
    fun2) "$@"; exit;;
esac

fun1
fun2

This script will run functions fun1 and fun2 but if you start it with option fun1 or fun2 it'll only run given function with args(if provided) and exit. Usage

$ ./test 
run function1
run function2

$ ./test fun2 a b c
run function2
options: a b c
Ivan
  • 6,188
  • 1
  • 16
  • 23
3

I have a situation where I need a function from bash script which must not be executed before (e.g. by source) and the problem with @$ is that myScript.sh is then run twice, it seems... So I've come up with the idea to get the function out with sed:

sed -n "/^func ()/,/^}/p" myScript.sh

And to execute it at the time I need it, I put it in a file and use source:

sed -n "/^func ()/,/^}/p" myScript.sh > func.sh; source func.sh; rm func.sh

Cthulhu
  • 5,095
  • 7
  • 46
  • 58
Christoph Lösch
  • 645
  • 7
  • 22
0

Edit: WARNING - seems this doesn't work in all cases, but works well on many public scripts.

If you have a bash script called "control" and inside it you have a function called "build":

function build() { 
  ... 
}

Then you can call it like this (from the directory where it is):

./control build

If it's inside another folder, that would make it:

another_folder/control build

If your file is called "control.sh", that would accordingly make the function callable like this:

./control.sh build
Arturas M
  • 4,120
  • 18
  • 50
  • 80
0

Solved post but I'd like to mention my preferred solution. Namely, define a generic one-liner script eval_func.sh:

#!/bin/bash
source $1 && shift && "@a"

Then call any function within any script via:

./eval_func.sh <any script> <any function> <any args>...

An issue I ran into with the accepted solution is that when sourcing my function-containing script within another script, the arguments of the latter would be evaluated by the former, causing an error.

Evan
  • 429
  • 4
  • 7
0

The other answers here are nice, and much appreciated, but often I don't want to source the script in the session (which reads and executes the file in your current shell) or modify it directly.

I find it more convenient to write a one or two line 'bootstrap' file and run that. Makes testing the main script easier, doesn't have side effects on your shell session, and as a bonus you can load things that simulate other environments for testing. Example...

# breakfast.sh
make_donuts() { 
     echo 'donuts!'
}

make_bagels() {
     echo 'bagels!'
}
# bootstrap.sh
source 'breakfast.sh'

make_donuts

Now just run ./bootstrap.sh.Same idea works with your python, ruby, or whatever scripts.

Why useful? Let's say you complicated your life for some reason, and your script may find itself in different environments with different states present. For example, either your terminal session, or a cloud provider's cool new thing. You also want to test cloud things in terminal, using simple methods. No worries, your bootstrap can load elementary state for you.

# breakfast.sh
# Now it has to do slightly different things
# depending on where the script lives!

make_donuts() {
    if [[ $AWS_ENV_VAR ]]
    then
        echo '/donuts'
    elif [[ $AZURE_ENV_VAR ]]
    then
        echo '\donuts'
    else 
        echo '/keto_diet'
    fi
}

If you let your bootstrap thing take an argument, you can load different state for your function to chew, still with one line in the shell session:

# bootstrap.sh
source 'breakfast.sh'

case $1 in
    AWS)
        AWS_ENV_VAR="arn::mumbo:jumbo:12345"
    ;;
    AZURE)
        AZURE_ENV_VAR="cloud::woo:_impress"
    ;;
esac

make_donuts # You could use $2 here to name the function you wanna, but careful if evaluating directly.

In terminal session you're just entering: ./bootstrap.sh AWS Result: # /donuts

Rab
  • 2,806
  • 2
  • 16
  • 20
-3

you can call function from command line argument like below

function irfan() {
echo "Irfan khan"
date
hostname
}
function config() {
ifconfig
echo "hey"
}
$1

Once you defined the functions put $1 at the end to accept argument which function you want to call. Lets say the above code is saved in fun.sh. Now you can call the functions like ./fun.sh irfan & ./fun.sh config in command line.