Sub-Processing

Sub-Shells

A Subshell is an execution context similar to a sub-process, but is more optimized and lightweight Than fork and exec system calls.

Caution

The sub-shell functions behave almost-identically to sub-processing, except that parent local variables do NOT need to be exported to be visible to a sub-shell

cd /tmp

# creating a file with 4 variable assignments
echo 'a=apples' > fruits.env
echo 'b=berries' >> fruits.env
echo 'c=carrots' >> fruits.env
echo 'd=durian' >> fruits.env

z=zebra

(
    set -a
    source fruits.env
    set +a
    bash -c 'printf "$a $b $c $d \n"'

    # z doesn't need to be exported to be visible to the sub-shell
    echo $z

    # exporting z to make it available to a sub-process
    export z
    bash -c 'printf "$z\n"'
)


[[ $a || $b || $c || $d ]] || printf "variables not visible in the parent shell\n"

Bash Functions

There are two types of functions

  • Function executed in the current shell in the form of function func_name(){command-list}

  • Function executed in a sub-shell function func_name()(command-list)

A function can be exported (exporting functions) with

export -f func_name

Here are two examples to illustrate the differences between the two types of functions

# not exported
a=apples

# shell PID
echo $BASHPID

function play(){
    # same shell PID
    echo $BASHPID

    # local variables to the caller are visible
    echo "a is $a"

    b=berries
}

# execute the function
play

# local variables set within the function are visible to the caller
echo "b is $b"

The other type is executed in a sub-shell

function play()(
    # different PID
    echo $BASHPID

    # parent-local variable is visible to the child
    echo "a = $a"

    # parent-exported variable is visible to the child
    echo "z is $z"

    # only available to the child and its children
    export b=berries

    # can provide return status
    return 0
)

# not exported
a=apples
export z=zebra

# shell PID
echo $BASHPID

# this function call sees the two variables a=apples and z=zebra
play

[[ $? ]] && echo "last command status successful"

# child env variables cannot be seen by the parent process
[[ $b ]] || echo "variable b can never be visible to the parent"


# this call sees a different variable for a
a=apricot play


# but its value remains the same in the parent
echo $a

Passing Arguments

There are two mechanisms for passing arguments to a sub-process

  • positional arguments

  • Environment Variables

command line arguments involve the following special variables

  • $# the count of positional args starting of a function from 1

  • $0 zeroth positional param is the command with which the child process was executed

  • $1 first positional parameter passed as a command line argument, if it exists

  • $* and $@ expansion of positional arguments, starting from 1

  • "$*" is equivalent to "$1 $2 ..."

  • "$@" is equivalent to "$1" "$2" ...

function printer()(
    echo "count of passed arguments is: $#"

    [[ $1 ]] && echo "the first argument is $1"
    [[ $2 ]] && echo "the second argument is $2"

    echo "all arguments are $@"
)

printer apples bananas
printer tigers