11

I'm trying to get around a problem that seems to me you cannot pass open db2 connection to a sub-shell.

My code organization is as follows:

Driver script (in my_driver.sh)

# foo.sh defines baz() bar(), which use a db2 connection
# Also the "$param_file" is set in foo.sh!
source foo.sh

db2 "connect to $dbName USER $dbUser using $dbPass"

function doit
{
    cat $param_file | while read params
    do
        baz $params
        bar $params
    done
}

doit

I've simplified my code, but the above is enough the give the idea. I start the above:

my_driver.sh

Now, my real issue is that the db2 connection is not available in sub-shell:

I tried:

. my_driver.sh

Does not help

If I do it manually from the command line:

source foo.sh

And I set $params manually:

baz $params
bar $params

Then it does work! So it seems that doit or something else acts as if bar and baz are executed from a sub-shell.

I would be elated if I can somehow figure out how to pass db2 open connection to sub-shell would be best.

Otherwise, these shell functions seem to me that they run in a sub-shell. Is there a way around that?

Rachid K.
  • 4,490
  • 3
  • 11
  • 30
lzc
  • 919
  • 7
  • 16
  • I think the answer is obvious to you. Yes it is in a subshell! you can easily test this by trying to print environment variables. – raam86 Mar 22 '16 at 15:39
  • Yes, it is.. but **can** I run `bar` or `baz` *not* within a subshell ? – lzc Mar 22 '16 at 15:40
  • 1
    In your code I don't see any possibility that ```baz``` or ```bar``` run in a subshell. Probably they themselves run db2 commands in a subshell ? – dekkard Mar 22 '16 at 16:03
  • Thanks @dekkard **you made me think** :) so for now, I'm still not sure if indeed `bar` or `baz` are called within a subshell, but I solved my problem by wrapping around my `db2` calls in `{` curlies `}` ! – lzc Mar 22 '16 at 17:13
  • That last comment was just **wrong**, hmm, how can I just delete the comment only ? – lzc Mar 27 '16 at 20:13
  • @lzc: if you had included the pipe and while loop *in your original question*, it would have been much easier to answer correctly. The edit really makes it a different question, and you should at least acknowledge that by indicating that it was edited. The function call does not require a subshell but the pipe does. – rici Mar 28 '16 at 20:06

3 Answers3

9

The shell does not create a subshell to run a function.

Of course, it does create subshells for many other purposes, not all of which might be obvious. For example, it creates subshells in the implementation of |.

db2 requires that the all db2 commands have the same parent as the db2 command which established the connection. You could log the PID using something like:

echo "Execute db2 from PID $$" >> /dev/stderr
db2 ...

(as long as the db2 command isn't execute inside a pipe or shell parentheses.)


One possible problem in the code shown (which would have quite a different symptom) is the use of the non-standard syntax

function f

To define a function. A standard shell expects

f()

Bash understands both, but if you don't have a shebang line or you execute the scriptfile using the sh command, you will end up using the system's default shell, which might not be bash.

rici
  • 234,347
  • 28
  • 237
  • 341
  • FYI: I'm using the bash, and AFAIK using `function` keyword is [optional](http://www.gnu.org/software/bash/manual/bashref.html#Shell-Functions) also the `>> /dev/stderr` is misleading or for whatever reason it does not work for me... did you mean to say: `echo hello world 2>> /tmp/my_stderr.log >&2` ? – lzc Mar 27 '16 at 20:27
0

Found solution, but can't yet fully explain the problem ....

if you change doit as follows it works!

function doit
{
    while read params
    do
        baz $params
        bar $params
    done < $param_file
}

Only, I'm not sure why? and how I can prove it ...

If I stick in debug code:

echo debug check with PID=$$ PPID=$PPID and SHLVL=$SHLVL

I get back same results with the | or not. I do understand that cat $param_file | while read params creates a subshell, however, my debug statements always show the same PID and PPID...

So my problem is solved, but I'm missing some explanations.

I also wonder if this question would not be more well suited in the unix.stackexchange community?

lzc
  • 919
  • 7
  • 16
  • 1
    The `BASH_SUBSHELL` variable in the debug code **does** show that that the *right side* of the `|` is a subshell ... thus any subsequent `db2` calls is no longer within the parent shell! – lzc Mar 28 '16 at 19:44
0

A shell function in such shells as sh (i.e. Dash) or Bash may be considered as a labelled commands group or named "code block" which may be called multiple times by its name. A command group surrounded by {} does not create a subshell or "fork" a process, but executes in the same process and environment.

Some might find it relatively similar to goto where function names represent labels as in other programming languages, including C, Basic, or Assembler. However, the statements vary quite greatly (e.g. functions return, but goto - doesn't) and Go To Statement may be Considered Harmful.


Shell Functions

Shell functions are a way to group commands for later execution using a single name for the group. They are executed just like a "regular" command. When the name of a shell function is used as a simple command name, the list of commands associated with that function name is executed. Shell functions are executed in the current shell context; no new process is created to interpret them.

Functions are declared using this syntax:

fname () compound-command [ redirections ]

or

function fname [()] compound-command [ redirections ]

This defines a shell function named fname. The reserved word function is optional. If the function reserved word is supplied, the parentheses are optional.

Source: https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html or man bash.

Grouping Commands Together

Commands may be grouped by writing either

(list)

or

{ list; }

The first of these executes the commands in a subshell. Builtin commands grouped into a (list) will not affect the current shell. The second form does not fork another shell so is slightly more efficient. Grouping commands together this way allows you to redirect their output as though they were one program:

{ printf " hello " ; printf " world\n" ; } > greeting

Note that "}" must follow a control operator (here, ";") so that it is recognized as a reserved word and not as another command argument.

Functions

The syntax of a function definition is

name () command

A function definition is an executable statement; when executed it installs a function named name and returns an exit status of zero. The command is normally a list enclosed between "{" and "}".

Source: https://linux.die.net/man/1/dash or man sh.

Transfers control unconditionally.

Used when it is otherwise impossible to transfer control to the desired location using other statements... The goto statement transfers control to the location specified by label. The goto statement must be in the same function as the label it is referring, it may appear before or after the label.

Source: https://en.cppreference.com/w/cpp/language/goto

Goto

... It performs a one-way transfer of control to another line of code; in contrast a function call normally returns control. The jumped-to locations are usually identified using labels, though some languages use line numbers. At the machine code level, a goto is a form of branch or jump statement, in some cases combined with a stack adjustment. Many languages support the goto statement, and many do not...

Source: https://en.wikipedia.org/wiki/Goto


Related:
https://mywiki.wooledge.org/BashProgramming#Functions
https://uomresearchit.github.io/shell-programming-course/04-subshells_and_functions/index.html (Subshells and Functions...)
Is there a "goto" statement in bash?
What's the difference between "call" and "invoke"?
https://en.wikipedia.org/wiki/Call_stack
https://mywiki.wooledge.org/BashPitfalls

Artfaith
  • 1,183
  • 4
  • 19
  • 29