19

I have a Korn shell script

#!/bin/ksh # set the right ENV case $INPUT in abc) export BIN=${ABC_BIN} ;; def) export BIN=${DEF_BIN} ;; *) export BIN=${BASE_BIN} ;; esac # exit 0 <- bad idea for sourcing the file 

now these VARs are export'ed only in a subshell, but I want them to be set in my parent shell as well, so when I am at the prompt those vars are still set correctly.

I know about

. .myscript.sh 

but is there a way to do it without 'sourcing'? as my users often forget to 'source'.


EDIT1: removing the "exit 0" part - this was just me typing without thinking first

EDIT2: to add more detail on why do i need this: my developers write code for (for simplicity sake) 2 apps : ABC & DEF. every app is run in production by separate users usrabc and usrdef, hence have setup their $BIN, $CFG, $ORA_HOME, whatever - specific to their apps.

so

  • ABC's $BIN = /opt/abc/bin # $ABC_BIN in the above script
  • DEF's $BIN = /opt/def/bin # $DEF_BIN

etc.

now, on the dev box developers can develop both ABC and DEF at the same time under their own user account 'justin_case', and I make them source the file (above) so that they can switch their ENV var settings back and forth. ($BIN should point to $ABC_BIN at one time and then I need to switch to $BIN=$DEF_BIN)

now, the script should also create new sandboxes for parallel development of the same app, etc. this makes me to do it interactively, asking for sandbox name, etc.

  • /home/justin_case/sandbox_abc_beta2
  • /home/justin_case/sandbox_abc_r1
  • /home/justin_case/sandbox_def_r1

the other option i have considered is writing aliases and add them to every users' profile

  • alias 'setup_env=. .myscript.sh'

and run it with

  • setup_env parameter1 ... parameterX

this makes more sense to me now

1
  • Yes -- you can do this with an alias or function (I prefer functions myself) where you have the function prod() { export VAR=snoopy ; } ; and the function dev() { export VAR=woodstock ; } ; Commented Jul 8, 2009 at 21:27

5 Answers 5

16

I think it is a "no can do" sort of problem...

First -- you wouldn't want to source that script because of the exit 0 at the end.

Second, no unix child process can directly change the environment of the parent. Otherwise all sorts of crazy things would be possible.

Could you add something to their environment with the default profile or bashrc files, or could you write a wrapper for whatever program it is they are trying to run?

Allow me to elaborate on the "wrapper" concept.

Let's say you want to run the program snoopy with either PROD or DEV in environment variable "OPTIONS" depending on if you want production or development. IF it isn't set, let's say snoopy does something zany like wipe out the database for production and development...

rename "snoopy" to snoopy.bin (or .snoopy.bin)

then put a script in that same location named "snoopy" that contains this:

#!/bin/sh export OPTIONS case "$OPTIONS" in PROD) ;; DEV) ;; *) OPTIONS=DEV ;; esac #the binary is actually named snoopy.bin exec "$0.bin" "$@" 

If you don't want to muck with the actual file, put this script somewhere in the filesystem that will be ahead of the actual snoopy program in the user's PATH and have a full path to the binary in the script's exec statement...

11

The answer is sourcing. Sourcing allows you to include variables in a script in the current shell, but never a parent of that. It is true, you have to be careful not to use any exit command or similar, because that would close your current shell.

You can source a script by using the ".", i.e.

. ./myscript.ksh

1
4

Maybe if you try...

#!/bin/bash mknod fifo p ( echo 'value' > fifo & ) VARIABLE=`cat fifo` rm fifo 

It's not exactly variable exporting, but it can provide basic communication with the parent process.

3

OK, don't laugh now. Very quick and very dirty solution is to add

echo "Please copy and paste this command:" echo "" echo "export BIN=\"$BIN\"" 

to your script.

Another approach. Just exec a subordinate $SHELL in your script, maybe with a different prompt (change $PS1 to notify users under which environment they are working to reduce confusion).

Another approach (my favorite). Users forget to source your script, why is that? Sourcing is precisely the standard way to go. Maybe a good way of reminding them is to remove the first line (#!/bin/sh) and then chmod a-x it. It can be still sourced after that, but it obviously cannot be mistakenly executed.

Rant: All in all, I feel you have had a strange idea in the first place. Maybe not strange... I would say somewhat non-Unix in style. I've never ever needed to export environment to parent in my life. I saw once a similar situation - login .profile asking which of three different environments to set for a single oracle account. Bad idea, in the end it turned out that users preferred to migrate to sudo (they sudo'd to three different oraxxx accounts). What are you trying to achieve, if I may ask?

1
  • 5
    By the way, there's a hashbang for scripts that should be sourced but not executed: #!/bin/false. Commented Nov 6, 2020 at 21:02
0

I think there is a way to do so with 2 bidirectional fifo connected to 2 files descriptors in a nested double loop

But as the 'son' process can "~send orders~" to its 'father' process, be careful and use it at your own risk

Here is an example where father process learn from son process and son process learn from its father process (each process can pass values to each other)

This example does nothing else interesting at the moment than exchanging values and use them as variables between the 2 process (father <--> son), but you can call foreign functions in the father loop (father process) or in the son loop (son process) or both

Consider the following script:

#!/bin/bash # NBA testing bash fd & pipes for bidirectionnal communication bewteen 2 process # cleaning fifo if exist [[ -p /dev/shm/tfpfifo ]] && rm -f /dev/shm/tfpfifo [[ -p /dev/shm/rfpfifo ]] && rm -f /dev/shm/rfpfifo # creating fifo if do not exist [[ ! -p /dev/shm/tfpfifo ]] && mkfifo /dev/shm/tfpfifo [[ ! -p /dev/shm/rfpfifo ]] && mkfifo /dev/shm/rfpfifo # exit fifo if fifo do not exist [[ ! -p /dev/shm/tfpfifo ]] && echo "ERROR: FIFO NOT CREATED" && exit 29 [[ ! -p /dev/shm/rfpfifo ]] && echo "ERROR: FIFO NOT CREATED" && exit 29 # defining (and init to something) files descripor variables (would be overwritten by bash !) fdt=fdtfp; fdr=fdrfp; # piping files descriptors to fifo pipes (one father -> son and one son -> father) exec {fdt}<>/dev/shm/tfpfifo; exec {fdr}<>/dev/shm/rfpfifo; # defining carriage return CR=$(echo -en "\\r") # defining loop turn k=0 # main forked loop while read <&${fdt} do rep=${REPLY//$CR/} cur_pid=$BASHPID cur_ppid=$PPID [[ ${rep} == 'start' ]] \ && echo -e "\rfrom_pipe:${rep}" || echo -e "\nmy_catched_reply_from_son:${rep}" REP=$(echo -e "${rep}" |tr " " "\n") my_son_pid=$(echo -e "${REP}"| grep son_pid: |cut -d: -f2) my_cur_pid=$(echo -e "${REP}"| grep son_ppid: |cut -d: -f2) my_cur_ppid=$(echo -e "${REP}"| grep son_pppid: |cut -d: -f2) [[ ${my_son_pid} == "" ]] && while read <&${fdr} do reply=${REPLY//$CR/} local_pid=$BASHPID SON_REP=$(echo -e "${reply}" |tr " " "\n") parent_pid=$(echo -e "${SON_REP}"| grep cur_pid: |cut -d: -f2) pparent_pid=$(echo -e "${SON_REP}"| grep cur_ppid: |cut -d: -f2) k=$(echo -e "${SON_REP}"| grep k: |cut -d: -f2) echo -e "\nson_catched_reply_from_me:${reply}" [[ ${k} != "" && ${parent_pid} != "" && ${pparent_pid} != "" ]] \ && [[ $k -ne 3 ]] \ && echo son_pid:$local_pid son_ppid:${parent_pid} son_pppid:${pparent_pid} >&${fdt} echo -e "son_k=$k son_BASHPID=$BASHPID" sleep .1 done & [[ $k -ne 4 ]] \ && echo -e "k:${k} cur_pid:${cur_pid} cur_ppid:${cur_ppid}" >&${fdr} [[ ${my_son_pid} != "" ]] && echo -e "my_son_pid_recieved_from_son=$my_son_pid" [[ ${my_cur_pid} != "" ]] && echo -e "my_cur_pid_recieved_from_son=$my_cur_pid" [[ ${my_cur_ppid} != "" ]] && echo -e "my_cur_ppid_recieved_from_son=$my_cur_ppid" [[ $k -eq 0 ]] && echo -e "\rk=$k my_BASHPID=$BASHPID - first round" \ || echo -e "k=$k my_BASHPID=$BASHPID" sleep 1 ((k++)) [[ $k -eq 4 ]] && kill $my_son_pid && kill $my_cur_pid done & # starting job ps -ef | grep -q "$!" \ && ([[ "$my_son_pid" == "" ]] && echo start >&${fdt}) \ || (exec {fdt}>&- && exec {fdr}>&-) # removing files descriptors after job sleep 5 && exec {fdt}>&- && exec {fdr}>&- # cleaning fifo if exist [[ -p /dev/shm/tfpfifo ]] && rm -f /dev/shm/tfpfifo [[ -p /dev/shm/rfpfifo ]] && rm -f /dev/shm/rfpfifo 

Output (I let 3 round in loop to show that communication can continue as long as each process is able to 'feed' each other reading fifo and to show that you can control the number of round with a well defined 'k' or similar method) :

from_pipe:start k=0 my_BASHPID=1158894 - first round son_catched_reply_from_me:k:0 cur_pid:1158894 cur_ppid:4163 son_k=0 son_BASHPID=1159116 my_catched_reply_from_son:son_pid:1159116 son_ppid:1158894 son_pppid:4163 my_son_pid_recieved_from_son=1159116 my_cur_pid_recieved_from_son=1158894 my_cur_ppid_recieved_from_son=4163 k=1 my_BASHPID=1158894 son_catched_reply_from_me:k:1 cur_pid:1158894 cur_ppid:4163 son_k=1 son_BASHPID=1159116 my_catched_reply_from_son:son_pid:1159116 son_ppid:1158894 son_pppid:4163 my_son_pid_recieved_from_son=1159116 my_cur_pid_recieved_from_son=1158894 my_cur_ppid_recieved_from_son=4163 k=2 my_BASHPID=1158894 son_catched_reply_from_me:k:2 cur_pid:1158894 cur_ppid:4163 son_k=2 son_BASHPID=1159116 my_catched_reply_from_son:son_pid:1159116 son_ppid:1158894 son_pppid:4163 my_son_pid_recieved_from_son=1159116 my_cur_pid_recieved_from_son=1158894 my_cur_ppid_recieved_from_son=4163 k=3 my_BASHPID=1158894 son_catched_reply_from_me:k:3 cur_pid:1158894 cur_ppid:4163 son_k=3 son_BASHPID=1159116 

Also, this method is not limited to father <--> son process and can be applied between 2 distinct process which could be for example forked from the same shell / script (but it's not an requirement) If someone is interested in a version of this script for 2 different process (not father <--> son but process1 <--> process2) it could be a good exercise for the reader or I can provide an example, just ask.

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.