1

I'm pulling my hair out trying to understand shell scripting and passing environment variables, and what not.

I'm trying to execute a PHP script from TextWrangler, that in it's turn opens a new PHP script with Terminal. (TextWrangler is a text editor that has the option to execute scripts, located in a designated folder, to operate on the current active document, for instance).

The first script is located in:

/Users/<username>/Library/Application Support/TextWrangler/Scripts/

... and its contents are:

#!/usr/bin/php
<?php
    var_dump( $_SERVER );
    chdir( __DIR__ );
    $file = realpath( '../Unix Support/test.php' );
    exec( sprintf( 'open -a Terminal "%s" &', $file ) );
    exit( 0 );
?>

The second is in:

/Users/<username>/Library/Application Support/TextWrangler/Unix Support/

... and its contents are:

#!/usr/bin/php
<?php
    var_dump( $_SERVER );
    exit( 0 );
?>

TextWrangler passes some environment variables to the first script (which I can access through $_SERVER), and they are as expected. For instance the correct filepath to the current document that is active in TextWrangler.

The first time I execute the script, the environment variables are automatically passed correctly to the second script (that I open with the exec()) as well.

Now comes the frustrating part: when I switch the active document in TextWrangler and run the script again, the first script receives the correct environment variables from TextWrangler again, but the second script still has the old environment variables, unless I had killed Terminal beforehand. So obviously the Terminal session somehow remembers the first environment variables, and doesn't want to update.

But apart from a this very basic understanding, I don't have the faintest idea how environment variables passing work, what scope they have, etc. So, could somebody explain how I can make it so (if possible, to begin with) that the second script receives the correct environment variables again, as well, without having to kill Terminal each time?

I've tried explicitly setting environment variables in the first script before calling exec(), like so:

foreach( $_SERVER as $key => $value )
{
    putenv( "$key=$value" );
}
exec( ... etc. );

I've tried unsetting the environment variables at the end, in both the first and second scripts, like so:

foreach( $_SERVER as $key => $value )
{
    putenv( "$key" );
}

But nothing works as I expect. Any new insights thoroughly appreciated.

edit:

In the meantime I've found an alternative, but unsatisfactory solution: when I call open -n -a Terminal ... (note the added -n argument) with exec(), it starts a new Terminal session each time, with the correct environment variables. But this opens a completely new Terminal instance each time.

update:

I've simplified the process a bit by only using one script in stead of two now, through testing the environment variable SHLVL. I've also managed to do away with a new Terminal instance now, by using a temporary file, like Scott suggested as well. This resulted in the following. But I still feel it's not very elegant.

$shellLevel = getenv( 'SHLVL' );
if( 1 == $shellLevel )
{
    file_put_contents( 'env.dat', serialize( $_SERVER ) );
    exec( sprintf( 'open -a Terminal "%s" &', __FILE__ ) );
    exit( 0 );
}
else
{
    $_SERVER = unserialize( file_get_contents( 'env.dat' ) );
    unlink( 'env.dat' );
    foreach( $_SERVER as $key => $value )
    {
        putenv( "$key=$value" );
    }
}

So, I'm still open too other suggestions (besides the CLI and file suggestions that Scott already proposed). Unless, of course, it is simply not possible (Scott seemed to hint at this already).

1 Answer 1

1

Have you ever used Microsoft Office (Word, Excel, and PowerPoint)?  Have you ever opened two documents/workbooks/presentations at the same time?  Have you noticed that you usually get only a single process of the appropriate tool, even though you have two windows?  It’s pretty clear that that, or something pretty similar, is what’s happening with Terminal -- the second open request is getting handled by the process that was created by the first open, and it is getting handled without creating a new process.  I guess open detects that the process already exists, and just sends a message to it, rather than creating a new process, or something like that.

As to your other question: environment variables are passed from parent process to child process (i.e., upon the creation of a new process through fork) and are preserved across exec calls.  So they go down the process hierarchy, until a process exits or explicitly changes a variable.  So, it looks like the first open causes a fork and an exec of Terminal, thus passing the environment, whereas the second open just sends a message to the existing process to run ../Unix Support/test.php again, and the environment doesn’t get passed.  You seem to have found the only ways to implement the functionality that you want using the environment -- force the creation of a new process every time.  Other approaches would involving passing the required data by other means, such as the command line or a file.

1
  • Scott, thanks. Your last suggestion is what I ended up doing in the meanwhile as well, actually. I simplified things by using only one script now, by testing an environment variable called SHLVL that is passed, to see if the script is the parent or child, and indeed saving the environment in a temporary file if it's the parent and reading and putenv()-ing them, if it's the child script. But it's not very elegant. Are you sure there's not a more elegant way of updating the environment variables for the same Terminal session, besides cli or file? Commented Nov 12, 2013 at 2:38

You must log in to answer this question.

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