why are my environmental variables permanent? - tcsh

This problem has been bothering me for awhile. Occasionally I would set up an environmental variable in my .tcshrc file. As soon as I don't need it any more, I would remove it from my .tcshrc file; however, for some reason the variable became permanent in my shell. I thought that everything in the .tcshrc file were not meant to be permanent once the settings are commented or removed. This specifically happened to a new PATH that I wanted to add. Now that I don't need it any more, removing it from the .tcshrc file does nothing.

Your .tcshrc is an executable script. You have to actually execute it for changes to it to have any effect. This typically occurs (automatically) when you start a new shell.
Another issue you may be running into is that environment varibles ar inherited by child processes. If your .tcshrc adds to variables rather than overwriting them (which isn't an uncommon thing to do with PATH) then this may cause the behavior you're seeing. Your current shell process has the extra entry in the PATH, and even though the child shell isn't adding it to the PATH (due to your recent .tcshrc change) it's already there.

Environment variables set in .tcshrc affect any tcsh shell that used that version of .tcshrc. Modifying the file affects any future tcsh processes you start, but it has no effect on any existing processes. Once a variable is set in a shell process, the only way to unset it is to use the "unsetenv" command (or "setenv" with a different value).
Any environment variables in your current tcsh process (the one that used the old version of .tcshrc) will also be inherited by any processes you invoke from tcsh.
For example:
Start a new tcsh process with setenv FOO BAR in your .tcshrc. $FOO is set to "BAR".
Modify your .tcshrc file, deleting the "setenv" command. This has no effect on any environment variable in any running process.
Launch bash from your tcsh prompt. The bash shell process has $FOO set to "BAR", because it inherited it from its parent process (your tcsh process), which got it from the old .tcshrc file.
Log out and log in again, All the processes that had $FOO set to "BAR" are now gone. You have a new tcsh process that read from the new .tcshrc, so $FOO isn't set.

Related

Access local shell variables in vim

In vim I can access my bash environment variables such as $PWD and $PATH. I would like to know how to access my temporary shell variables in vim too.
For example, suppose I was in my terminal and define a variable foo="bar". Then I enter vim and try to access this variable with the following command :!echo $foo, but it does not recognize this variable. From my understanding, vim starts a new shell each time a bash command is invoked and then closes it immediately after. Is there a way to use the same shell in vim that my local variable foo was defined in?
No, you can't interact with the parent shell from a subprocess it spawned (without that shell's active participation, which isn't reasonably/practically available in the scenario at hand) -- but you can export your variables to make them accessible to new shells started in child processes.
Running
set -a
...will make any variable defined going forward be automatically exported to the environment, even without an explicit export command.
Since (unlike the C system() function) vim's system() honors the SHELL environment variable, if SHELL=/bin/bash (or :set shell=/bin/bash has been run in vim), you can also invoke exported functions from vim. That is, if you define the function and export it as follows:
foo() { echo "bar"; }
export -f foo
...then you can invoke it with !foo from inside vim.
Even then, however, this is running in a new, transient shell instance, not the original parent process.
Explanation
Environment variables and shell variables are two entirely different concepts, but as we manipulate them in a similar way in bash, it's easy to get confused.
Whenever a process is created (by fork), it may include an environment, given by its parent at fork-time. The child process may then access and modify its content. How this is done as a user depends on the program :
In vim, you can access an environment variable like this : :echo $foo
In bash, you can access it like this : $ echo "$foo"
In most programming languages, you can access it with a syntax coherent with the rest of the language, such as ENV['foo'] in ruby
On the other hand, a program may allocate memory for any internal use, but notably, it will quite often define and use variables. Once again, this depends on the program :
In vim, you would use the :let command to assign an internal variable
In bash, you would assign a variable with $ foo='bar', and then read it with $ echo "$foo"
In most programming languages, you have a variation of the foo='bar' syntax, sometimes with type declarations, etc
As you can see, bash uses the same syntax to read an environment variable and one of its own private variables, which can lead to some confusion.
When you execute vim from your bash shell, the environment is copied over from the parent process (bash) to the child (vim), but the private memory of bash (including the variables you may have defined) are not.
Thus, accessing them from the child process would require some inter-process communication mechanism, between parent and child. While technically doable, this option is not implemented in bash nor vim.
Solution
In order for your variable to be accessible from vim (or any forked process, for that matter), you need it to be present in the environment of your vim process.
Several options to do that :
$ export foo='bar' : This will mark your variable for export to the environment of subsequently executed commands. That's what you want in most cases.
$ foo='bar' vim : This adds your variable to the environment of this vim command. Very useful for troubleshooting, or for one-liners.
$ set -a : As you can see in bash manpage, this marks every subsequent definitions for export to the environment of subsequent commands. It's essentially equivalent to prepending every subsequent definition by export.
To go further
The question uses the :!echo $foo syntax to display the value of foo, which is yet another usecase. The ! here is actually an escape sequence that allows you to execute a shell command from vim.
However, vim cannot execute anything in the parent shell (the one you executed the vim command in), so it creates a new bash shell in a child process, executes echo in it, and displays the result.
In the current case, the result is mostly the same, but it could easily be misleading in other situations, so it's important to understand what is happening here.
There is another vim syntax, using expand, that allows one to lookup variables : :echo expand("$foo")
It however works entirely differently.
If no internal variable named foo exists, vim will invoke a shell to look it up (similarly to what ! would do).
This options is way slower than an environment lookup, and not recommended for most usecases.
If you want to use a value from your shell on the :substitute command, there's actually a way to do it.
I don't know if it solves your need but here we go.
Let's say we want to substitute Mydir by your PWD:
:s/Mydir/\=expand($PWD)/g

Makefile to add shell aliases

I am already using a makefile, and I was hoping to be able to use it to store a few useful aliases that the user could then invoke. I know that I can make a bash file with the aliases already built in, so and run it with source, so I can do something like:
# File: aliases.sh
alias useful="command to run"
alias also-useful="another command -to run"
Then I can run this in the current terminal session with:
source ./aliases.sh
Using a Makefile
So I was hoping to achieve something similar with a makefile, I was hoping to have a simple aliases entry, so the user could just run:
make aliases
I prefer to avoid adding an extra file if this is at all possible, because I don't want to add extra files for such simple tasks. If you have any suggestions that would be better, I'd be open to hearing them too.
If what you're asking is for make aliases to create aliases that you can then invoke at your shell prompt, something like:
$ make aliases
$ useful
then that is impossible and the reason has nothing to do with make.
In a UNIX/POSIX system the process hierarchy is strict: a process starts one or more sub-processes, and each of those can start more, etc. So a login manager process starts your shell (or your window manager), your shell starts make, which is another process, and make will run another shell as a subprocess to run each recipe, and each shell will run programs like compilers, commands like rm which are also processes, etc.
It is a fundamental rule of all processes that they cannot modify the environment (memory) of their parents (and they can only modify the environment of their children before they are started). So, if you start a new shell and change your working directory then exit that shell, the parent's shell is not changed. If you set an environment variable in the child process, the variable is not set in the parent. Etc.
Shell aliases are part of a particular shell's memory. So a program you start cannot create aliases in its parent shell. It doesn't matter if that program is make or anything else.
That's why you have to use the special command source to load those into your shell: instead of running a new shell, the source command tells the current shell to run the commands in the script as if you'd typed them in at the command line... so no new process is created and the current shell's environment and memory is modified. If you ran your aliases file as a shell script, via aliases rather than source aliases, then a new shell would be created, the aliases would be defined, then the shell would exit and all the aliases would be gone again.
So, all that to say it's not possible for make to define aliases in the shell that invokes it: the operating system won't allow it.

Use of a pipe prevents left process to export variables. Why?

I have the following one-line bash file foo.sh:
export PATH=<new path>
In another script, I use:
echo $PATH # --> old path
. foo.sh | grep bar
echo $PATH # --> old path!!!!
Depending on the machine I execute this second script on, the PATH is or is not updated in the main script. On the machines where it does not work, whatever the command right of the pipe, it still does not work. On the contrary, if I drop the pipe, it always work whatever the machine.
My machines are supposed to have the exact same configuration (even though, considering this issue, it looks as if they don't). Bash version is 4.1.2.
Do you have any idea where/what to look to understand this behaviour?
In bash, all parts of a pipeline are executed in separate subshells, which is why sourcing the script doesn't change the path.
Some shells are able to run the last command in the current shell environment (ksh93, for example), but bash does not (unless job control is disabled and the lastpipe shell option is enabled, and the pipeline is not executed in the background).
The bash manual states, in the "Pipelines" section,
Each command in a pipeline is executed as a separate process (i.e., in
a subshell).

Why are bash script variables not saving?

I have a simple bash script:
#!/bin/bash
JAVA_HOME=/usr
EC2_HOME=~/ec2-api
echo $EC2_HOME
export PATH=$PATH:$EC2_HOME/bin
I run the script like so
$ ./ec2
/Users/user/ec2-api
The script runs and produces the correct output.
However, when I now try to access the EC2_HOME variable, I get nothing out:
$ echo $EC2_HOME
I get a blank string back. What am I doing wrong?
Do either of the following instead:
source ec2
or
. ec2
(note the . notation is just a shortcut for source)
Explanation:
This is because ./ec2 actually spawns a subshell from your current shell to execute the script, and subshells cannot affect the environment of the parent shell from which it spawned.
Thus, EC2_HOME does get set to /Users/user/ec2-api correctly in the subshell (and similarly the PATH environment variable is updated and exported correctly in the subshell as well), but those changes won't propagate back to your parent shell.
Using source runs the script directly in the current shell without spawning a subshell, so the changes made will persist.
(A note on export: export is used to tell new shells spawned from the current shell to use the variables exported from the current shell. So for any variables you would only use in the current shell, they need not be exported.)
A shell script can never modify the environment of their parent.
To fix your problem, you can use the dot (.) command:
$ . ./ec2
and that should work. In cshell, it would be
% source ./ec2
To learn more about shells and scripts, my best resource is by far Unix power tools.

Bash shell losing environment variables between two lines

I have a bash script as follows:
rvm use 1.8.7
rvm list
The first line is a function loaded within my .bashrc file that defines some enviroment variables. When executing the second line, those variables have been set to their previous values (the set values have been lost). What am I missing here?
Running on a ubuntu box.
A subshell is being created and the variables are set within it. When the subshell exits, the changes are lost. This often happens when a while loop is in a pipe. Without seeing the function it's impossible to be more specific than that.
when you define environment variables that you want to make available to all subshells you need to prefix it with export like so:
export myvar="some value"
I would check that rvm is doing this properly

Resources