Setting /bin/sh environment variables in the script

Many folks work in a mixed Shell environment. To assist the un-enlightened, often a tcl or tk script is called from another shell.

For example:

 #!/bin/sh
 #\
 exec tclsh "$0" "$@"

The "#\" gets skipped because /bin/sh thinks it is a comment. When the script calls itself with the "exec $0", Tcl reads the "#\" line as a continuation of a comment, and skips over it to the next line.

The problem with this is that sometimes we get used to our personal environments, and our scripts break when someone else runs them.

The good news is that the same idea works for other environmental variables.

 #!/bin/sh
 #\
 PATH=${PATH}:/path/to/tclsh/and/wish
 #\
 LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib
 #\
 export PATH LD_LIBRARY_PATH
 #\
 exec tclsh "$0" "$@"

LH


To read a shell environment variable in your Tcl script, try doing something like this:

    global env
    set olddisplay $env(DISPLAY)
    set env(DISPLAY) unix:0

LV wonders whether this might be expressed as well in this fashion:

    set olddisplay $::env(DISPLAY)
    set ::env(DISPLAY) unix:0

and forget about global .

You can check to see if the variable exists by doing something like:

    if {[info exists env(VARNAME)]} {
        # okay, it's there, use it
        set value $env(VARNAME)
    } else {
        # the environment var isn't set, use a default
        set value "the default value"
    }

There are some special magic variables relating to the shell environment.

The program name is assigned to the global variable argv0 and any arguments to it are placed in the global variable argv as a list. The variable argc is set to the number of elements in the list argv. As an example:

    #! /usr/local/bin/tclsh

    if { $argc != 2 } {
        puts stderr "$argv0: Usage: $argv0 <infile> <outfile>"
        exit 1
    }

    set infile  [lindex $argv 0]
    set outfile [lindex $argv 1]

For setting environment variables for exec'd processes:

  exec /usr/bin/env PATH=/usr/local/bin:${PATH} myprog

Will work with all UNICES that I know about. -PSE

rdt however, several Linux machines that I have worked with place env in a differrent directory.


Also see exec magic for a discussion of issues regarding "$@"

EE


LV Many times people come looking for a way to set an environment variable in such a way as to influence a parent process. This is, in generally, rather difficult to do. One generally solves this problem via cooperation - in some way communicating back to the parent process that it needs to set the variable itself.

For example, you could write out to stdout the value you want the parent to get, then have the parent process read the child's stdout and put the value into the variable.

Or you could write out a text file containing variable names and values (perhaps in shell format) then have the parent read in the values and take the action. Or you could set up some sort of socket, pipe, etc. and communciate that way.

But at least in Unix like systems, a child process DOES NOT HAVE WRITE ACCESS to the process space of the parent. Period .

CL puts it more starkly: "... you can't change the value of an environment variable in another already running process", according to the authoritative Unix Programming FAQ [L1 ] (why? Among other reasons, security considerations prohibit such an operation). However, as Larry hints above, there are ways to change the question slightly to give effective control over ...

See "Setting environment variables with a script" for more.


SETTING ENVIRONMENT VARIABLES UNDER WINDOWS OS

The following information was extracted from a thread that appeared in c.l.t.:

--> 9-9-2002, Marty Backe writes:

set ::env(MyVar) d:\Program

Note that this will only effect child processes of the Tcl program that issues the command.

--> 9-9-2002, Donal K. Fellows writes:

Well, setting environment variables is easy. Just use the global 'env' array:

  set env(MyVar) {d:\Program}

There are some things to watch out for. On windows, setting an env-var to the empty string deletes it (because that's how the platform operates.) And on all platforms, propagating env-var settings to the parent environment is non-trivial (i.e. requires cooperation from the parent, like executing code that is passed back to it); this restriction is important (for things like security) but is occasionally very annoying.

(Also, if you want to put backslashes in variables, watch out for Tcl's quoting which can interact with it in some circumstances.)

--> 10-9-2002, Tom Wilkason contributes a recipe for setting environmental variables under MS Windows OS, which have a system wide scope:

Under NT and beyond, you have a couple of choices. You can use the script below or exec the setx program that is part of the windows resource kit (a free download). Under Win9X I believe you will have to modify the autoexec.bat file and reboot.

;## ;# Set a system wide Env variable ;# proc setSysEnv {key value} {

   package require registry
   global env
   ;# Support OS expandable params
   if {$key == "PATH" || [string match "*%*" $value]} {
      # Indicate this may have expandable params in it
      set type expand_sz
   } else {
      set type sz
   }
   if {[catch {
      if {[catch {
         registry set \
 "HKEY_LOCAL_MACHINE\\SYSTEM\\ControlSet001\\Control\\Session Manager\\Environment" $key $value $type
         registry set \
 "HKEY_LOCAL_MACHINE\\SYSTEM\\ControlSet002\\Control\\Session Manager\\Environment" $key $value $type
         registry set \
 "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment" $key $value $type
      } result]} {
        catch {registry set "HKEY_CURRENT_USER\\Environment" $key $type}
      } else {
        catch {registry delete "HKEY_CURRENT_USER\\Environment" $key}
      }
      set env($key) $value
   } result]} {
   tk_messageBox -title "OK to Continue" -icon info \
 -message "Error setting env var $key to $value, do you have admin priviledges?\n$result"
   }

}

--> 19/oct/2007 vito: is there other methods, i was thinking to evaluate the tcl outputs, i know in linux i can do something like this: setpath () {

       eval `tclsh setpath.tcl $*`

}

this will simple take whatever the script output and and evaluate it. so i just wondering is there any linux's eval equivalent command in windows? or how can i implement a wrapper script that will do the same job as linux's eval?


arjen - 2009-06-29 03:18:49

Every once in a while I think how nice it would be to be able to "source" a shell script so that you get whatever environment variables it sets in your Tcl program. Like:

 linux> . mypackage.sh    (to extend the PATH or LD_LIBRARY_PATH etc.)
 linux> tclsh myprog.tcl  (has access to the settings in mypackage.sh)

The idea would be:

 # myprog.tcl
 #
 # Get the settings for "mypackage"
 #
 . mypackage.sh

(Where "." is a Tcl command that uses the shell to extract the correct settings)

Sofar, I have considered the way one can implement this . command, but I have not actually implemented it. (Similar things could be useful on Windows)

AM (4 september 2009) Okay, several months later I have actually implemented it. Something goes wrong when a shell script variable contains a newline, but that is preciously rare. Here is the script:

# srcenv.tcl --
#    Small program to import the environment variables set by
#    a shell script
#

# . --
#     Import the environment variables from a shell script
#
# Arguments:
#     scriptname   Name of the script file to source
#
#  Returns:
#     Nothing
#
# Side effects:
#     Changes the contents of the env array
#
proc . {scriptname} {
    global env

    set envNew [exec $::env(SHELL) -c ". $scriptname; env"]

    foreach line [lrange [split $envNew \n] 0 end-1] {

        set eqpos [string first = $line]
        set var   [string range $line 0 [expr {$eqpos-1}]]
        set value [string range $line [expr {$eqpos+1}] end]

        if { $var != "PATH" } {
            set env($var) $value
        } else {
            set env($var) [split $value :]
        }
    }
}

# main --
#     Test the thing
#
puts "Before:"; parray env
. "srcenv.sh"
puts "After:"; parray env 

If you want to test it, the shell script "srcenv.sh" looks like this:

#/bin/sh
export NEWVAR="new value" 

(Possibly we need more magic, but this is a nice start)


JOB Under Unix I usually use the following code at the very begin of a tcl script in order to propagate environment settings from a shell script into the script.

Asuming the shell script myShellScript.sh is in the same directory as the tcl script and e.g. the environment variable WISHCMD is declared within the shell script.

#!/usr/bin/ksh
# environment setup goes here, befor starting wish \
a0="$0" \
a1="$@" \
. `dirname $0`/myShellScript.ksh script_args_to_follow_here > /dev/null \
# next line starts wish "$0" ${1+"$@"} \
exec ${WISHCMD} $a0 $a1

AB - 2010-09-24 03:52:53

Try out this simple method ....

### Executing Shell script and loading the new environment"  

## Execute shell commands
exec  xterm -e {
setenv abhishek "bansal"; \
source /sw/cshrc/linux64/unicad_24_noopus_ftm; \
env > file; \
cat file | nawk '{gsub("^","set env(") ; \
str=gensub("=","

) {",1); \
gsub("$","}",str);print str}' > file1; \
vi file1 }

## Loading new environment
source file1
parray env

kynan - 2011-09-10 11:36:29

AM: I used your . procedure in a module file and it seems to do the trick, many thanks! Is there an easier way to achieve this with module that I have overlooked?


kynan - 2017-02-08 17:04:54

Answering my own question from 5 years ago. In a module file you can use the following (note the final ; is crucial!):

puts ". /path/to/script;"