How to read environment variable inside shell file - shell

i have a tcl file and defining my environment variable inside that file. something like below
set env(ET_PINASSIGN_SCRIPT) $ET_PINASSIGN_SCRIPT
where $ET_PINASSIGN_SCRIPT variable will have a user incoming input value. Now I need to read this env variable in a shell file (#!/bin/ksh
). This is what i am trying and not working
$env ET_PINASSIGN_SCRIPT .
Any suggestions?
Thanks

I understand this is not possible. A program which is running, eg. your script, receives a duplicate of the environment, and can modify it. But when the program stops running, it disappears, together with its environment and changes.
There are few direct methods to communicate 'from the dead' process. You could create a temporary file, or return simple integers from the exit code.
Another way, would be to run the other program concurrently in a way that they share the same environment,
More info on the environment:
http://en.wikipedia.org/wiki/Environment_variable
Edit: I feel I wasn't clear enough: I just wanted to point out that programming that modifies the environment for other programs is 'extreme' (i.e. dangerous) programming. All kind of tricks can be done which can have lasting influences on the use of the computer.

The simplest method would be to make the Tcl code write a shell script to set the variable to standard out (with puts) and to then do:
eval `tclsh yourscript.tcl`
in your shell.
The Tcl code would probably be something like:
puts "ET_PINASSIGN_SCRIPT='$ET_PINASSIGN_SCRIPT'"
except you'll have to worry about any embedded ' characters, so might do…
puts "ET_PINASSIGN_SCRIPT='[string map {' '\\''} $ET_PINASSIGN_SCRIPT]'"

Related

Assign variable in the background shell

I want to assign a variable in the background while I'm showing a progress indicator like this
sleep_echo(){
sleep $1
echo $2
}
show_ellapsed_time () {
#...
}
(mivar=$(sleep_echo 3 hello)) &
show_ellapsed_time $!
echo $mivar
echo "after asignment"
When I run this in terminal it outputs the progress indicator and afterwards echoes myvar
processing 00:00:03 [\]
hello
after asignment
However the assignation doesn't happen when I run it within a script
processing 00:00:03 [\]
after asignment
Why is this failing? Isn't te function invoked?
A child process cannot communicate with its parent process through variable assignment.
In Bash, a child process can be created in several ways :
Launching an external command
Launching a subshell by enclosing statements with ()
Launching a subshell when using redirection (such as pipes)
Launching anything in the background with &
External commands receive a copy of all shell variables that are marked for export (e.g. export VAR, local -x VAR)
Subshells receive a copy of all variables. This is useful, but makes it easy to not realize you are in a new context. Any assignment made in the child process will never be seen by the parent.
What you can do:
Use inter-process communication to capture the output
Put the information you need in a file, as files are accessible by all processes (assuming adequate permissions)
Use a named FIFO, which is a special kind of file allowing separate processes to communicate (see mkfifo).
Structure your script in a way that you keep the code blocks that most need it in the main context by using the right kind of construct to build pipes.
For that last one, using process substitution like <(COMMAND) and >(COMMAND) is often much better than the typical pipelines built with |, for instance allowing the body of a while loop (or, really, any other statement the substitution is fed to/from) to remain in the context of the script. This allows variable assignments inside loops to have the intended effect.
In your case, you have these lines:
(mivar=$(sleep_echo 3 hello)) &
show_ellapsed_time $!
Maybe you could replace them with something like :
show_ellapsed_time &
BG_PID=$!
mivar=$(sleep_echo 3 hello)
kill $BG_PID
I do not know enough about your show_ellapsed_time to know if that could work without the parameter you were passing to that function, but the assignment would work. You could also pass the PID of the main shell to the function, like this :
show_ellapsed_time $$ &
your variable is inside another scope. If you enclose some commands with round brackets ( and ), the variables you use within that scope are no longer accessible once outside that scope. An example:
(myvar=test)
echo "myvar=$myvar"
will give:
myvar=
since myvar is not known anymore. The only reason your command seems to work in the terminal; is that probably you have set your variable mivar during your tests; so the variable is still set. If you open a new terminal your script won't work anymore.
Sending the process to the background (using &); will also cause the scope to change; so even removing the brackets won't help. I think you should redesign your script (or consider using a higher level language), since it seems like you want to introduce multithreading into bash :)

Setting Fish Environment Variable Only Once in Function

I am trying to use fish as my shell. When I login with LightDM, I want to start certain Xsession apps, but only when the shell in invoked at the outset by LightDM.
I have tried this in ~/.config/fish/config.fish:
###################################################################
# Start xsession applications, but only once.
if test -z "$XSESSION_STARTED"
set -xg XSESSION_STARTED 'f'
end
if test "$XSESSION_STARTED" = 'f'
xsession-apps
end
The function xsession-apps then starts all the apps in the background and sets the environment variable at the end like this:
set -xg XSESSION_STARTED "t"
But XSESSION_STARTED does not appear to get set to 't' and causes the xsession-apps function to get called every time, even when I start a new terminal within gnome-term.
What am I missing. Is there a better way to approach this?
even when I start a new terminal within gnome-term.
That is to be expected. Global variables are set within that particular fish. If you start another fish, it won't have it (unless you start it inside that fish, because the variable is exported).
There's a few ways to approach this:
Don't do it in config.fish at all - use the DE's autostart mechanism or at least ~/.xinitrc. This is the best and cleanest approach, and independent of your shell.
Use universal variables - these are stored persistently and shared for all fish sessions on the machine. The issue here is invalidating it - you need to unset the variable again once you logout/reboot, but if your machine crashed that wouldn't happen
Use a flag file on a tmpfs (i.e. in RAM) - this will be automatically invalidated if your machine stops, whatever the cause. You need to setup a tmpfs for it, though.
Here is the code I used in ~/.xsessionrc:
# Apps launched directly by X window managers don't have their environment set
if [ $SHELL = "/usr/bin/fish" ]
then
/usr/bin/fish -c xsession-apps
else
source ~/src/dotfiles/keychain.sh
source ~/src/dotfiles/shell/aliases
source ~/src/dotfiles/shell/env
source ~/src/dotfiles/xsession-apps
eval "$(~/.rbenv/bin/rbenv init -)"
fi
It initializes fish or bash/zsh, depending on what I'm in the mood for at the time. Lately, I'm liking fish. I then defined a fish function called xsession-apps to launch the things I wanted started up in my X session, such as dropbox, hplip, xmobar, etc. I have a similar setup as a bash script, also called xsession-apps that gets sourced if I'm not using fish.
By the way, I use xmonad as my windowing environment.

Using variables between files in shell / bash scripting

This question has been posted here many times, but it never seems to answer my question.
I have two scripts. The first one contains one or multiple variables, the second script needs those variables. The second script also needs to be able to change the variables in the first script.
I'm not interested in sourcing (where the first script containing the variables runs the second script) or exporting (using environment variables). I just simply want to make sure that the second script can read and change (get and set) the variables available in the first script.
(PS. If I misunderstood how sourcing or exporting works, and it applies to my scenario, please let me know. I'm not completely closed to those methods, after what I've read, I just don't think those things will do what I want)
Environment variables are per process. One process can not modify the variables in another. What you're asking for is not possible.
The usual workaround for scripts is sourcing, which works by running both scripts in the same shell process, but you say you don't want to do that.
I've also given this some thought. I would use files as variables. For example in script 1 you use for writing variable values to files:
echo $varnum1 > /home/username/scriptdir/vars/varnum1
echo $varnum2 > /home/username/scriptdir/vars/varnum2
And in script 2 you use for reading values from files back into variables:
$varnum1=$(cat /home/username/scriptdir/vars/varnum1)
$varnum2=$(cat /home/username/scriptdir/vars/varnum2)
Both scripts can read or write to the variables at any given time. Theoretically two scripts can try to access the same file at the same time, I'm not sure what exactly would happen but since each file only contains one value, the time to read or write should be extremely short.
In order to even reduce those times you can use a ramdisk.
I think this is much better than scripts editing each other (yuk!). Live editing of scripts can mess up scripts and only works when you initiate the script again after the edit was made.
Good luck!
So after a long search on the web and a lot of trying, I finally found some kind of a solution. Actually, it's quite simple.
There are some prerequisites though.
The variable you want to set already has to exist in the file you're trying to set it in (I'm guessing the variable can be created as well when it doesn't exist yet, but that's not what I'm going for here).
The file you're trying to set the variable in has to exist (obviously. I'm guessing again this can be done as well, but again, not what I'm going for).
Write
sudo sed -i 's/^\(VARNAME=\).*/\1VALUE/' FILENAME
So i.e. setting the variable called Var1 to the value 5, in the file
test.ini:
sudo sed -i 's/^\(Var1=\).*/\15/' test.ini
Read
sudo grep -Po '(?<=VARNAME=).*' FILENAME
So i.e. reading the variable called Var1 from the file test.ini
sudo grep -Po '(?<=Var1=).*' test.ini
Just to be sure
I've noticed some issues when running the script that sets variables from a different folder than the one where your script is located.
To make sure this always go right, you can do one of two things:
sudo sed -i 's/^\(VARNAME=\).*/\1VALUE/' `dirname $0`/FILENAME
So basically, just put `dirname $0`/ (including the backticks) in front of the filename.
The other option is to make `dirname $0`/ a variable (again including the backticks), which would look like this.
my_dir=`dirname $0`
sudo sed -i 's/^\(VARNAME=\).*/\1VALUE/' $my_dir/FILENAME
So basically, if you've got a file named test.ini, which contains this line: Var1= (In my tests, the variable can start empty, and you will still be able to set it. Mileage may vary.), you will be able to set and get the value for Var1
I can confirm that this works (for me), but since you all, with way more experience in scripting then me, didn't come up with this, I'm guessing this is not a great way to do it.
Also, I couldn't tell you the first thing about what's happening in those commands above, I only know they work.
So if I'm doing something stupid, or if you can explain to me what's happening in the commands above, please let me know. I'm very curious to find out what you guys think if this solution.

Where does Ruby memory config go and how can one check if it is set?

In REE, and MRI 1.9+, ruby's garbage collector can be tuned:
http://www.rubyenterpriseedition.com/documentation.html#_garbage_collector_performance_tuning
http://smartic.us/2010/10/27/tune-your-ruby-enterprise-edition-garbage-collection-settings-to-run-tests-faster/
http://blog.evanweaver.com/articles/2009/04/09/ruby-gc-tuning/
But none of these articles say where to put this configuration. I imagine that if it's in the environment, ruby will pick it up when it starts -- however, there's no way to check this as far as I can tell. The settings don't show up in any runtime constants that I can find.
So, where do I put this configuration, and how can I double-check that it's being used?
These settings are environment variables, so you would just need to set them in the parent process of the ruby process itself. Many people recommend creating a simple shell script for this purpose, perhaps calling it /usr/local/bin/ruby-custom:
#!/bin/bash
export RUBY_HEAP_MIN_SLOTS=20000
export RUBY_HEAP_SLOTS_INCREMENT=20000
...etc...
exec "/path/to/ruby" "$#"
The first few lines set whichever custom variables you want, and the last line invokes ruby itself, passing it whatever arguments this script was initially given.
You will next need to mark this script as executable (chmod a+x /usr/local/bin/ruby-custom) and then configure Passenger to use it as the ruby executable, by adding this to your Apache .conf file:
PassengerRuby /usr/local/bin/ruby-custom

Ruby, Unicorn, and environment variables

While playing with Heroku, I found their approach of using environment variables for server-local configuration brilliant. Now, while setting up an application server of my own, I find myself wondering how hard that would be to replicate.
I'm deploying a sinatra application, riding Unicorn and Nginx. I know nginx doesn't like to play with the environment, so that one's out. I can probably put the vars somewhere in the unicorn config file, but since that's under version control with the rest of the app, it sort of defeats the purpose of having the configuration sit in the server environment. There is no reason not to keep my app-specific configuration files together with the rest of the app, as far as I'm concerned.
The third, and last (to my knowledge) option, is setting them in the spawning shell. That's where I got lost. I know that login and non-login shells use different rc files, and I'm not sure whether calling something with sudo -u http stuff is or not spawning a login shell. I did some homework, and asked google and man, but I'm still not entirely sure on how to approach it. Maybe I'm just being dumb... either way, I'd really appreciate it if someone could shed some light on the whole shell environment deal.
I think your third possibility is on the right track. What you're missing is the idea of a wrapper script, whose only function is to set the environment and then call the main program with whatever options are required.
To make a wrapper script that can function as a control script (if prodEnv use DB=ProdDB, etc), there is one more piece that simplifies this problem. Bash/ksh both support a feature called sourcing files. This an operation that the shell provides, to open a file and execute what is in the file, just as if it was in-lined in the main script. Like #include in C and other languages.
ksh and bash will automatically source /etc/profile, /var/etc/profile.local (sometimes), $HOME/.profile. There are other filenames that will also get picked up, but in this case, you'll need to make your own env file and the explicitly load it.
As we're talking about wrapper-scripts, and you want to manage how your environment gets set up, you'll want to do the sourcing inside the wrapper script.
How do you source an environment file?
envFile=/path/to/my/envFile
. $envFile
where envFile will be filled with statements like
dbServer=DevDBServer
webServer=QAWebServer
....
you may discover that you need to export these variable for them to be visble
export dbServer webServer
An alternate assignment/export is supported
export dbServer=DevDBServer
export webServer=QAWebServer
Depending on how non-identical your different environments are, you can have your wrapper script figure out which environment file to load.
case $( /bin/hostame ) in
prodServerName )
envFile=/path/2/prod/envFile ;;
QASeverName )
envFile=/path/2/qa/envFile ;;
devSeverName )
envFile=/path/2/dev/envFile ;;
esac
. ${envFile}
#NOW call your program
myProgram -v -f inFile -o outFile ......
As you develop more and more scripts in your data processing environment, you can alway source your envFile at the top. When you eventually change the physical location of a server (or it's name), then you have only one place that you need to make the change.
IHTH
Also a couple of gems dealing with this. figaro that works both with or without heroku. Figaro uses a yaml file (in config and git ignored) to keep track of variables. Another option is dotenv that reads variables from an .env file. And also another article with all them options.
To spawn an interactive shell (a.k.a. login shell) you need to invoke sudo like this:
sudo -i -u <user> <command>
Also you may use -E to preserve the environment. This will allow some variables to be pased for your current environment to the command invoked with sudo.
I solved a similar problem by explicitly telling Unicorn to read a variables file as part of startup in its init.d script. First I created a file in a directory above the application root called variables. In this script I call export on all my environment variables, e.g. export VAR=value. Then I defined a variable GET_VARS=source /path/to/variables in the /etc/init.d/unicorn file. Finally, I modified the start option to read su - $USER -c "$GET_VARS && $CMD" where $CMD is the startup command and $USER is the app user. Thus, the variables defined in the file are exported into the shell of Unicorn's app user on startup. Note that I used an init.d script almost identical to the one from this article.

Resources