Get cmd to re-evaluate environment variables - windows

Assuming I have the following set as system environment variables in Windows 7
FOO = foo
path = ...;%FOO%/bin
Take the following example run in cmd
set FOO=bar
start cmd
echo %FOO%
//bar
echo %path%
//...;foo/bin
The path environment variable did not re evaluate itself upon launch of cmd, however the new FOO variable did stick. How can I get path to reevaulate itself based on the new FOO variable set in the parent command terminal?
EDIT: I'm looking for path to become ...;bar/bin

There is no "command line equivalent of opening cmd.exe from the desktop or start menu" that reproduces the behavior you care about. Environment variables are inherited from the parent process.
The shell reads the registry and performs interpolation (using its own environment, which is the process of being read from the registry, and knows nothing of variables you set in a command interpreter), which is why updates to the registry settings are reflected in a cmd.exe launched from the shell.
If you launch a new cmd.exe from a running cmd.exe, you won't get the shell behavior, and you will get the existing environment inherited. There's nothing in Windows that uses variables in a command interpreter to interpolate the registry settings. The code responsible for reading the environment from the registry is completely unrelated to cmd.exe... it is in explorer.exe (or probably one of the shell DLLs used by explorer).
This answer, which uses VB Script to read the registry and construct a batch file, is as good as you can get. I haven't tested whether interpolation is performed in the registry-access COM component (Environment("System") method on a WScript.Shell object) used by VB script, or if the environment variable references survive into the batch file and are interpolated during batch processing. So you may be confounded by the order of evaluations and variable assignments, in which case you'd better adapt the script to fetch just the PATH setting itself and leave all other variables alone.

I also found this to be useful
set path=%path:foo/bin=bar/bin%
Not as dynamic as I wanted but it works to replace a portion of a variable.

Related

echo/access environmental variables from powershell

In windows cmd I could echo any environmental variable with echo %VARIABLENAME%, such as
But in powershell, this behavior is inconsistent and I could not understand. For certain variables like $HOME I could do the same thing (echo $VARIABLENAME) as in windows cmd.
But for some other variables I could not simply echo but have to use .NET's class methods, such like
I would like to:
Understand the difference between Powershell and windows cmd. Why they behave differently when accessing and printing environmental variables.
Understand why certain variable is echolable while others are not in powershell. What is the rule behind that.
I am new to powershell. The purpose of this question is not just getting the variable printed, but understanding how things work in powershell and the difference between windows cmd, so that I could better use it.
PowerShell exposes environmental variables via the $Env: scope (you can read more about scopes here)
So to access the USERPROFILE environmental variable, you could do the following (note, I'm using Write-Host in place of echo here, see this answer for details on the difference between the two):
Write-Host $Env:USERPROFILE
PowerShell also exposes a number of automatic variables and these are made available to all scripts and commands.
$HOME for example is one such automatic variable.
For further information on these, see Automatic Variables

Session Environment Variables for Windows CMD shell

I understand from my googling that on *nix systems you can use the export command to set a temporary environment variable for the current session. What's the Windows equivalent for the plain old CMD shell?
I finally figured out that SET is the equivalent of export.
Furthermore, to reference the variable, it needs to be surrounded by percent signs:
SET MY_VARIABLE=42
echo %MY_VARIABLE%
SET documentation: ss64.com/nt/set.html

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

What does the 'export' command do?

I happen to run some commands blindly, in order to get things done.
I started to work with Jenkins recently, and then I had to use this export command to run the Jenkins WAR archive. What does the export command do in general, and why do we need to run this command, while running Jenkins (after the Jenkins home is set)?
export in sh and related shells (such as Bash), marks an environment variable to be exported to child-processes, so that the child inherits them.
export is defined in POSIX:
The shell shall give the export attribute to the variables corresponding to the specified names, which shall cause them to be in the environment of subsequently executed commands. If the name of a variable is followed by = word, then the value of that variable shall be set to word.
I guess you're coming from a Windows background. So I'll contrast them (I'm kind of new to Linux too). I found a user's reply to my comment, to be useful in figuring things out.
In Windows, a variable can be permanent or not. The term environment variable includes a variable set in the cmd shell with the SET command, as well as when the variable is set within the Windows GUI, thus set in the registry, and becoming viewable in new cmd windows.
E.g., the documentation for the set command in Windows "Displays, sets, or removes environment variables. Used without parameters, set displays the current environment settings."
In Linux, set does not display environment variables. It displays shell variables which it doesn't call/refer to as environment variables. Also, Linux doesn't use set to set variables (apart from positional parameters and shell options, which I explain as a note at the end), only to display them and even then only to display shell variables. Windows uses set for setting and displaying, e.g., set a=5, but Linux doesn't.
In Linux, I guess you could make a script that sets variables on bootup, e.g., /etc/profile or /etc/.bashrc, but otherwise, they're not permanent. They're stored in RAM.
There is a distinction in Linux between shell variables, and environment variables. In Linux, shell variables are only in the current shell, and environment variables, are in that shell and all child shells.
You can view shell variables with the set command (though note that, unlike Windows, variables are not set in Linux with the set command).
set -o posix; set (doing that set -o posix once first, helps not display too much unnecessary stuff). So set displays shell variables.
You can view environment variables with the env command.
Shell variables are set with, e.g., just a = 5.
Environment variables are set with export. Export also sets the shell variable.
Here you see shell variable zzz set with zzz = 5, and see it shows when running set, but it doesn't show as an environment variable.
Here we see yyy set with export, so it's an environment variable. And see it shows under both shell variables and environment variables:
$ zzz=5
$ set | grep zzz
zzz=5
$ env | grep zzz
$ export yyy=5
$ set | grep yyy
yyy=5
$ env | grep yyy
yyy=5
$
Other useful QnAs:
https://unix.stackexchange.com/questions/176001/how-can-i-list-all-shell-variables
https://askubuntu.com/questions/26318/environment-variable-vs-shell-variable-whats-the-difference
Note: One point which elaborates a bit and is somewhat corrective to what I've written, is that, in Linux bash, 'set' can be used to set "positional parameters" and "shell options/attributes", and technically both of those are variables, though the man pages might not describe them as such.
But still, as mentioned, set won't set shell variables or environment variables). If you do set asdf then it sets $1 to asdf, and if you do echo $1 you see asdf.
If you do set a=5 it won't set the variable a, equal to 5. It will set the positional parameter $1 equal to the string of "a=5". So if you ever saw set a=5 in Linux it's probably a mistake unless somebody actually wanted that string a=5, in $1.
The other thing that Linux's set can set, is shell options/attributes. If you do set -o you see a list of them. And you can do for example set -o verbose, off, to turn verbose on (by the way, the default happens to be off, but that makes no difference to this). Or you can do set +o verbose to turn verbose off. Windows has no such usage for its set command.
In simple terms, environment variables are set when you open a new shell session. At any time if you change any of the variable values, the shell has no way of picking that change. That means the changes you made become effective in new shell sessions.
The export command, on the other hand, provides the ability to update the current shell session about the change you made to the exported variable. You don't have to wait until new shell session to use the value of the variable you changed.

why are my environmental variables permanent?

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.

Resources