SETX doesn't append path to system path variable - windows

I have tried below command to append some path to system path variable by batch-file :
setx PATH "%PATH%;C:\Program Files\MySQL\MySQL Server 5.5\bin"
I have checked system variable path after running above batch-file, above path isn't in there.
You can see all windows Variable value content in below :
C:\Program Files (x86)\AMD APP\bin\x86_64;C:\Program Files (x86)\AMDAPP\bin\x86;%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\;C:\ProgramFiles (x86)\ATI Technologies\ATI.ACE\Core-Static;
What am i doing wrong?

To piggy-back on #Endoro's answer (I lack the rep to comment):
If you want to change the system-wide environment variables, you have to use /M, a la:
setx PATH "%PATH%;C:\Program Files\MySQL\MySQL Server 5.5\bin" /M
setx.exe is picky about placement of the /M, BTW. It needs to be at the end.

WARNING!
setx will truncate the value to 1024 characters.
If you use it to modify PATH you might mess up your system.
You can use this PowerShell snippet to add something to your path:
$new_entry = 'c:\blah'
$old_path = [Environment]::GetEnvironmentVariable('path', 'machine');
$new_path = $old_path + ';' + $new_entry
[Environment]::SetEnvironmentVariable('path', $new_path,'Machine');
In case you want to not re-add an already existing entry something like this will do (see for a better version further down):
$new_entry = 'c:\blah'
$search_pattern = ';' + $new_entry.Replace("\","\\")
$old_path = [Environment]::GetEnvironmentVariable('path', 'machine');
$replace_string = ''
$without_entry_path = $old_path -replace $search_pattern, $replace_string
$new_path = $without_entry_path + ';' + $new_entry
[Environment]::SetEnvironmentVariable('path', $new_path,'Machine');
Here a newer version that I'm using now (2017-10-23).
This version handles nested paths correctly.
E.g. it handles the case of PATH containing "c:\tool\foo" and you want to add "c:\tool".
Note, that this expands values that are in path and saves them back expanded.
If you want to avoid this, have a look at the comment of #ErykSun below.
$desired_entry = 'C:\test'
$old_path = [Environment]::GetEnvironmentVariable('path', 'machine');
$old_path_entry_list = ($old_path).split(";")
$new_path_entry_list = new-object system.collections.arraylist
foreach($old_path_entry in $old_path_entry_list) {
if($old_path_entry -eq $desired_entry){
# ignore old entry
}else{
[void]$new_path_entry_list.Add($old_path_entry)
}
}
[void]$new_path_entry_list.Add($desired_entry)
$new_path = $new_path_entry_list -Join ";"
[Environment]::SetEnvironmentVariable('path', $new_path,'Machine');

you shouldn't look at the system environment variables but to your user environment variables:

Should never use setx for a path since it's limited to 1024 chars, as mentioned.
Could use reg add:
set pathkey="HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment"
for /F "usebackq skip=2 tokens=2*" %%A IN (`reg query %pathkey% /v Path`) do (reg add %pathkey% /f /v Path /t REG_SZ /d "%%B;C:\Program Files\MySQL\MySQL Server 5.5\bin")
or set pathkey="HKEY_CURRENT_USER\Environment" for user path.
Then to broadcast the change:
powershell -command "& {$md=\"[DllImport(`\"user32.dll\"\",SetLastError=true,CharSet=CharSet.Auto)]public static extern IntPtr SendMessageTimeout(IntPtr hWnd,uint Msg,UIntPtr wParam,string lParam,uint fuFlags,uint uTimeout,out UIntPtr lpdwResult);\"; $sm=Add-Type -MemberDefinition $md -Name NativeMethods -Namespace Win32 -PassThru;$result=[uintptr]::zero;$sm::SendMessageTimeout(0xffff,0x001A,[uintptr]::Zero,\"Environment\",2,5000,[ref]$result)}"

SETX /M Path "%PATH%;%ProgramFiles%\MySQL\MySQL Server 5.5\bin\
It will append your path to system variable

To update and expand on Endoro's answer for Windows 10, manually add the path to your Path system variable as a new variable. I wasn't able to get setx to work even changing the flags around. Doing it manually was simple.
To get to your system environmental variables -> Windows Key -> Edit the system environmental variables -> Click Environmental Variables -> Select the Path variable in the System variables frame -> Click Edit -> Click New -> Add the path -> Click Okay
Make sure you close all your CLI windows and open a new one if you're trying to verify by checking the version.
Windows showing where to edit the Path environmental variable

I faced the same problem when I tried to add path variables related to fortran. (Eclipse for C/C++/Fortran)
I tried
SETX /M Path "%PATH%;C:\Users\mahidhai\cygwin64\bin"
in command prompt as administrator. I got a warning saying
data was truncated to 1024 characters and stored.
Edit registry via GUI
Run->regedit
Navigate to HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
HKLM is short for HKEY_LOCAL_MACHINE
Doubleclick on the Path entry or invoke value edit mode via context menu
Append the parent directory of your exe separated by a semicolon ;
Refresh the registry changes to the system
No worries on editing the registry, it is safe as long as you don't change random values.
Though if using a GUI you should use the purpose built one.
Edit via purpose built GUI starting in Explorer
Right click on "This PC"
Click on "Properties"
On the left panel of the window that pops up, click on "Advanced System Settings"
Click on the "Advanced" tab
Click on "Environment Variables" button at the bottom of the window
Image from this answer

Related

Reference default path in powershell script

Is there a way to call the system path if the path has been changed in the current terminal? i.e.:
$env:Path = "C:\some new path"
#some coding that requires a different path set up
$env:Path = $defaultPath #would have to define $defaultPath by calling the system default path
Use the following to reload the $env:PATH environment variable from the registry, as future sessions would see it (assuming no further relevant registry updates are made).
If your current session hasn't made any relevant registry updates, this is the same as getting the value that was in effect on session startup - barring any dynamic additions via a $PROFILE script:
$env:PATH = [Environment]::GetEnvironmentVariable('Path', 'Machine'),
[Environment]::GetEnvironmentVariable('Path', 'User') -join ';'
Note:
A process' effective $env:PATH value is a composite value of a machine-level and a user-level registry entry, with the machine-level definition taking precedence, as reflected in the two .NET API calls above.
Note that the underlying registry locations - HKEY_LOCAL_MACHIN\System\CurrentControlSet\Control\Session Manager\Environment and HKEY_CURRENT_USER\Environment - are REG_EXPAND_SZ registry values, i.e. they may be defined in terms of other environment variables, such as %SystemRoot% and %ProgramFiles%.
Both the .NET API calls above - using [Environment]::GetEnvironmentVariable() - and PowerShell's Get-ItemProperty and Get-ItemPropertyValue cmdlets expand (interpolate) such references and return verbatim paths - which is what new processes see by default too.
Given the above, the only way to robustly retrieve the value that was in effect at session startup time is to save it in a variable at startup time.
It's still stored in the registry so you can just query it:
Located: HKLM:\System\CurrentControlSet\Control\Session Manager\Environment
$key = "HKCU:\Environment",
"HKLM:\System\CurrentControlSet\Control\Session Manager\Environment"
(Get-ItemPropertyValue -Path $key -Name Path) -Join ';'
Querying the key using Get-ItemPropertyValue (as suggested by Mklement) will give you just the property's value.

Adding things to the user PATH | Powershell SETX Error [duplicate]

I followed this procedure in order to permanently add a path to SumatraPDF using powershell. The last few commands from the link are meant to check that the path has indeed been added.
When I access the path using the following command,
(get-itemproperty -path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH).Path.split(';')
the result includes the path to SumatraPDF
C:\Windows\system32
C:\Windows
C:\Windows\System32\Wbem
C:\Windows\System32\WindowsPowerShell\v1.0\
C:\Windows\System32\OpenSSH\
C:\ProgramData\chocolatey\bin
C:\texlive\2021\bin\win32
C:\Users\921479\AppData\Local\SumatraPDF
However when I access it using the following command,
($env:path).split(';')
the result does not contain the path to SumatraPDF:
C:\Windows\system32
C:\Windows
C:\Windows\System32\Wbem
C:\Windows\System32\WindowsPowerShell\v1.0\
C:\Windows\System32\OpenSSH\
C:\ProgramData\chocolatey\bin
C:\texlive\2021\bin\win32
C:\Users\921479\AppData\Local\Microsoft\WindowsApps
Finally, actually passing sumatrapdf does not works, which indicates to me that the real path is the one accessed using the get-itemproperty command.
Why does the path set in the registry not correspond to the one set in $env:path? Is there a mistake in the procedure shown in the link I followed? How can I correct it?
I should mention I have already tried restarting the shell but it doesn't help.
Note:
See the middle section for helper function Add-Path
See the bottom section for why use of setx.exe should be avoided for updating the Path environment variable.
The procedure in the linked blog post is effective in principle, but is missing a crucial piece of information / additional step:
If you modify environment variables directly via the registry - which, unfortunately, is the right way to do it for REG_EXPAND_SZ-based environment variables such as Path - you need to broadcast a WM_SETTINGCHANGE message so that the Windows (GUI) shell (and its components, File Explorer, the taskbar, the desktop, the Start Menu, all provided via explorer.exe processes) is notified of the environment change and reloads its environment variables from the registry. Applications launched afterwards then inherit the updated environment.
If this message is not sent, future PowerShell sessions (and other applications) won't see the modification until the next logon / reboot.
Unfortunately, there's no direct way to do this from PowerShell, but there are workarounds:
Brute-force workaround - simple, but visually disruptive and closes all open File Explorer windows:
# Kills all explorer.exe processes, which restarts the Windows shell
# components, forcing a reload of the environment from the registry.
Stop-Process -Name explorer
Workaround via .NET APIs:
# Create a random name assumed to be unique
$dummyName = [guid]::NewGuid().ToString()
# Set an environment variable by that name, which makes .NET
# send a WM_SETTINGCHANGE broadcast
[Environment]::SetEnvironmentVariable($dummyName, 'foo', 'User')
# Now that the dummy variable has served its purpose, remove it again.
# (This will trigger another broadcast, but its performance impact is negligible.)
[Environment]::SetEnvironmentVariable($dummyName, [NullString]::value, 'User')
Workaround by calling the Windows API via an ad hoc-compiled P/Invoke call to SendMessageTimeout() in C#, via Add-Type:
While this is a proper solution, it invariably incurs a noticeable performance penalty due to the ad hoc-compilation the first time it is run in a session.
For details, see this blog post.
The approach in the blog post has another problematic aspect:
It retrieves the expanded environment-variable value from the registry, because that is what Get-ItemProperty and Get-ItemPropertyValue invariably do. That is, if directories in the value are defined in terms of other environment variables (e.g., %SystemRoot% or %JAVADIR%), the returned value no longer contains these variables, but their current values. See the bottom section for why this can be problematic.
The helper function discussed in the next section addresses all issues, while also ensuring that the modification takes effect for the current session too.
The following Add-Path helper function:
Adds (appends) a given, single directory path to the persistent user-level Path environment variable by default; use -Scope Machine to target the machine-level definition, which requires elevation (run as admin).
If the directory is already present in the target variable, no action is taken.
The relevant registry value is updated, which preserves its REG_EXPAND_SZ data type, based on the existing unexpanded value - that is, references to other environment variables are preserved as such (e.g., %SystemRoot%), and may also be used in the new entry being added.
Triggers a WM_SETTINGCHANGE message broadcast to inform the Windows shell of the change.
Also updates the current session's $env:Path variable value.
Note: By definition (due to use of the registry), this function is Windows-only.
With the function below defined, your desired Path addition could be performed as follows, modifying the current user's persistent Path definition:
Add-Path C:\Users\921479\AppData\Local\SumatraPDF
If you really want to update the machine-level definition (in the HKEY_LOCAL_MACHINE registry hive, which doesn't make sense with a user-specific path), add -Scope Machine, but not that you must then run with elevation (as admin).
Add-Path source code:
function Add-Path {
param(
[Parameter(Mandatory, Position=0)]
[string] $LiteralPath,
[ValidateSet('User', 'CurrentUser', 'Machine', 'LocalMachine')]
[string] $Scope
)
Set-StrictMode -Version 1; $ErrorActionPreference = 'Stop'
$isMachineLevel = $Scope -in 'Machine', 'LocalMachine'
if ($isMachineLevel -and -not $($ErrorActionPreference = 'Continue'; net session 2>$null)) { throw "You must run AS ADMIN to update the machine-level Path environment variable." }
$regPath = 'registry::' + ('HKEY_CURRENT_USER\Environment', 'HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment')[$isMachineLevel]
# Note the use of the .GetValue() method to ensure that the *unexpanded* value is returned.
$currDirs = (Get-Item -LiteralPath $regPath).GetValue('Path', '', 'DoNotExpandEnvironmentNames') -split ';' -ne ''
if ($LiteralPath -in $currDirs) {
Write-Verbose "Already present in the persistent $(('user', 'machine')[$isMachineLevel])-level Path: $LiteralPath"
return
}
$newValue = ($currDirs + $LiteralPath) -join ';'
# Update the registry.
Set-ItemProperty -Type ExpandString -LiteralPath $regPath Path $newValue
# Broadcast WM_SETTINGCHANGE to get the Windows shell to reload the
# updated environment, via a dummy [Environment]::SetEnvironmentVariable() operation.
$dummyName = [guid]::NewGuid().ToString()
[Environment]::SetEnvironmentVariable($dummyName, 'foo', 'User')
[Environment]::SetEnvironmentVariable($dummyName, [NullString]::value, 'User')
# Finally, also update the current session's `$env:Path` definition.
# Note: For simplicity, we always append to the in-process *composite* value,
# even though for a -Scope Machine update this isn't strictly the same.
$env:Path = ($env:Path -replace ';$') + ';' + $LiteralPath
Write-Verbose "`"$LiteralPath`" successfully appended to the persistent $(('user', 'machine')[$isMachineLevel])-level Path and also the current-process value."
}
The limitations of setx.exe and why it shouldn't be used to update the Path environment variable:
setx.exe has fundamental limitations that make it problematic, particularly for updating environment variables that are based on REG_EXPAND_SZ-typed registry values, such as Path:
Values are limited to 1024 characters, with additional ones getting truncated, albeit with a warning (as of at least Windows 10).
The environment variable that is (re)created is invariably of type REG_SZ, whereas Path is originally of type REG_EXPAND_SZ and contains directory paths based on other environment variables, such as %SystemRoot% and %JAVADIR%.
If the replacement value contains only literal paths (no environment-variable references) that may have no immediate ill effects, but, for an instance, an entry that originally depended on %JAVADIR% will stop working if the value of %JAVADIR% is later changed.
Additionally, if you base the updated value on the current session's $env:Path value, you'll end up duplicating entries, because the process-level $env:Path value is a composite of the machine-level and current-user-level values.
This increases the risk of running into the 1024-character limit, especially if the technique is used repeatedly. It also bears the risk of duplicate values lingering after the original entry is removed from the original scope.
While you can avoid this particular problem by retrieving the scope-specific value either directly from the registry or - invariably in expanded form - via [Environment]::GetEnvironmentVariable('Path', 'User') or [Environment]::GetEnvironmentVariable('Path', 'Machine'), that still doesn't solve the REG_EXPAND_SZ problem discussed above.
Use setx to permanently update an environment variable. Don't hack the registry.
After you invoke setx, just update the Path environment manually in the current session. Powershell: Reload the path in PowerShell

Set a persistent environment variable from cmd.exe

I have to set environment variables on different windows machines, but I don't want to be bothered changing them manually by getting on the properties screen of "My Computer"
I want to do it from the command line, with a batch file. As far as I understand, using set will only change the variable for the processes I will call in the command window.
I want to set it definitely, so later, when running a new process, it will use those new settings I have set. Is there a way to do that from the command line?
Use the SETX command (note the 'x' suffix) to set variables that persist after the cmd window has been closed.
For example, to set an env var "foo" with value of "bar":
setx foo bar /m
Though it's worth reading the 'notes' that are displayed if you print the usage (setx /?), in particular:
On a local system, variables created or modified by this tool will be available in future command windows but not in the current CMD.exe command window.
On a remote system, variables created or modified by this tool will be available at the next logon session.
In PowerShell, the [Environment]::SetEnvironmentVariable command.
The MSDN documentation for environment variables tells you what to do:
To programmatically add or modify system environment variables, add them to the HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment registry key, then broadcast a WM_SETTINGCHANGE message with lParam set to the string "Environment". This allows applications, such as the shell, to pick up your updates.
You will of course need admin rights to do this. I know of no way to broadcast a windows message from Windows batch so you'll need to write a small program to do this.
:: Sets environment variables for both the current `cmd` window
:: and/or other applications going forward.
:: I call this file keyz.cmd to be able to just type `keyz` at the prompt
:: after changes because the word `keys` is already taken in Windows.
#echo off
:: set for the current window
set APCA_API_KEY_ID=key_id
set APCA_API_SECRET_KEY=secret_key
set APCA_API_BASE_URL=https://paper-api.alpaca.markets
:: setx also for other windows and processes going forward
setx APCA_API_KEY_ID %APCA_API_KEY_ID%
setx APCA_API_SECRET_KEY %APCA_API_SECRET_KEY%
setx APCA_API_BASE_URL %APCA_API_BASE_URL%
:: Displaying what was just set.
set apca
:: Or for copy/paste manually ...
:: setx APCA_API_KEY_ID 'key_id'
:: setx APCA_API_SECRET_KEY 'secret_key'
:: setx APCA_API_BASE_URL 'https://paper-api.alpaca.markets'
Indeed SET TEST_VARIABLE=value works for current process only, so SETX is required. A quick example for permanently storing an environment variable at user level.
In cmd, SETX TEST_VARIABLE etc. Not applied yet (echo %TEST_VARIABLE% shows %TEST_VARIABLE%,
Quick check: open cmd, echo %TEST_VARIABLE% shows etc.
GUI check: System Properties -> Advanced -> Environment variables -> User variables for -> you should see Varible TEST_VARIABLE with value etc.
An example with VBScript (.vbs)
Sub sety(wsh, action, typey, vary, value)
Dim wu
Set wu = wsh.Environment(typey)
wui = wu.Item(vary)
Select Case action
Case "ls"
WScript.Echo wui
Case "del"
On Error Resume Next
wu.remove(vary)
On Error Goto 0
Case "set"
wu.Item(vary) = value
Case "add"
If wui = "" Then
wu.Item(vary) = value
ElseIf InStr(UCase(";" & wui & ";"), UCase(";" & value & ";")) = 0 Then
wu.Item(vary) = value & ";" & wui
End If
Case Else
WScript.Echo "Bad action"
End Select
End Sub
Dim wsh, args
Set wsh = WScript.CreateObject("WScript.Shell")
Set args = WScript.Arguments
Select Case WScript.Arguments.Length
Case 3
value = ""
Case 4
value = args(3)
Case Else
WScript.Echo "Arguments - 0: ls,del,set,add; 1: user,system, 2: variable; 3: value"
value = "```"
End Select
If Not value = "```" Then
' 0: ls,del,set,add; 1: user,system, 2: variable; 3: value
sety wsh, args(0), args(1), UCase(args(2)), value
End If

Is there an equivalent of 'which' on the Windows command line?

As I sometimes have path problems, where one of my own cmd scripts is hidden (shadowed) by another program (earlier on the path), I would like to be able to find the full path to a program on the Windows command line, given just its name.
Is there an equivalent to the UNIX command 'which'?
On UNIX, which command prints the full path of the given command to easily find and repair these shadowing problems.
Windows Server 2003 and later (i.e. anything after Windows XP 32 bit) provide the where.exe program which does some of what which does, though it matches all types of files, not just executable commands. (It does not match built-in shell commands like cd.) It will even accept wildcards, so where nt* finds all files in your %PATH% and current directory whose names start with nt.
Try where /? for help.
Note that Windows PowerShell defines where as an alias for the Where-Object cmdlet, so if you want where.exe, you need to type the full name instead of omitting the .exe extension. Alternatively, you can set an alias for it:
Set-Alias which where.exe
Update: Using Get-Command (alias: gcm) is recommended since it's native to PS and will get all command types: aliases, cmdlets, executables, and functions. Example:
gcm notepad*
While later versions of Windows have a where command, you can also do this with Windows XP by using the environment variable modifiers, as follows:
c:\> for %i in (cmd.exe) do #echo. %~$PATH:i
C:\WINDOWS\system32\cmd.exe
c:\> for %i in (python.exe) do #echo. %~$PATH:i
C:\Python25\python.exe
You don't need any extra tools and it's not limited to PATH since you can substitute any environment variable (in the path format, of course) that you wish to use.
And, if you want one that can handle all the extensions in PATHEXT (as Windows itself does), this one does the trick:
#echo off
setlocal enableextensions enabledelayedexpansion
:: Needs an argument.
if "x%1"=="x" (
echo Usage: which ^<progName^>
goto :end
)
:: First try the unadorned filenmame.
set fullspec=
call :find_it %1
:: Then try all adorned filenames in order.
set mypathext=!pathext!
:loop1
:: Stop if found or out of extensions.
if "x!mypathext!"=="x" goto :loop1end
:: Get the next extension and try it.
for /f "delims=;" %%j in ("!mypathext!") do set myext=%%j
call :find_it %1!myext!
:: Remove the extension (not overly efficient but it works).
:loop2
if not "x!myext!"=="x" (
set myext=!myext:~1!
set mypathext=!mypathext:~1!
goto :loop2
)
if not "x!mypathext!"=="x" set mypathext=!mypathext:~1!
goto :loop1
:loop1end
:end
endlocal
goto :eof
:: Function to find and print a file in the path.
:find_it
for %%i in (%1) do set fullspec=%%~$PATH:i
if not "x!fullspec!"=="x" #echo. !fullspec!
goto :eof
It actually returns all possibilities but you can tweak it quite easily for specific search rules.
Under PowerShell, Get-Command will find executables anywhere in $Env:PATH.
$ Get-Command eventvwr
CommandType Name Definition
----------- ---- ----------
Application eventvwr.exe c:\windows\system32\eventvwr.exe
Application eventvwr.msc c:\windows\system32\eventvwr.msc
And since powershell let's you define aliases, which can be defined like so.
$ sal which gcm # short form of `Set-Alias which Get-Command`
$ which foo
...
PowerShell commands are not just executable files (.exe, .ps1, etc). They can also be cmdlets, functions, aliases, custom executable suffixes set in $Env:PATHEXT, etc. Get-Command is able to find and list all of these commands (quite akin to Bash's type -a foo). This alone makes it better than where.exe, which.exe, etc which are typically limited to finding just executables.
Finding executables using only part of the name
$ gcm *disk*
CommandType Name Version Source
----------- ---- ------- ------
Alias Disable-PhysicalDiskIndication 2.0.0.0 Storage
Alias Enable-PhysicalDiskIndication 2.0.0.0 Storage
Function Add-PhysicalDisk 2.0.0.0 Storage
Function Add-VirtualDiskToMaskingSet 2.0.0.0 Storage
Function Clear-Disk 2.0.0.0 Storage
Cmdlet Get-PmemDisk 1.0.0.0 PersistentMemory
Cmdlet New-PmemDisk 1.0.0.0 PersistentMemory
Cmdlet Remove-PmemDisk 1.0.0.0 PersistentMemory
Application diskmgmt.msc 0.0.0.0 C:\WINDOWS\system32\diskmgmt.msc
Application diskpart.exe 10.0.17... C:\WINDOWS\system32\diskpart.exe
Application diskperf.exe 10.0.17... C:\WINDOWS\system32\diskperf.exe
Application diskraid.exe 10.0.17... C:\WINDOWS\system32\diskraid.exe
...
Finding custom executables
Unlike UNIX, where executables are files with the executable (+x) bit set, executables on windows are files present in one of the directories specified in the $PATH env. variable whose filename suffixes are named in the $PATHEXT env. variable (defaults to .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.CPL).
As Get-Command also honours this env. variable, it can be extended to list custom executables. e.g.
$ $Env:PATHEXT="$Env:PATHEXT;.dll;.ps1;.psm1;.py" # temporary assignment, only for this shell's process
$ gcm user32,kernel32,*WASM*,*http*py
CommandType Name Version Source
----------- ---- ------- ------
ExternalScript Invoke-WASMProfiler.ps1 C:\WINDOWS\System32\WindowsPowerShell\v1.0\Invoke-WASMProfiler.ps1
Application http-server.py 0.0.0.0 C:\Users\ME\AppData\Local\Microsoft\WindowsApps\http-server.py
Application kernel32.dll 10.0.17... C:\WINDOWS\system32\kernel32.dll
Application user32.dll 10.0.17... C:\WINDOWS\system32\user32.dll
See Get-Command for more options and examples.
In Windows PowerShell:
set-alias which where.exe
If you have PowerShell installed (which I recommend), you can use the following command as a rough equivalent (substitute programName for your executable's name):
($Env:Path).Split(";") | Get-ChildItem -filter programName*
More is here:
My Manwich! PowerShell Which
The GnuWin32 tools have which, along with a whole slew of other Unix tools.
In Windows CMD which calls where:
$ where php
C:\Program Files\PHP\php.exe
Cygwin is a solution. If you don't mind using a third-party solution, then Cygwin is the way to go.
Cygwin gives you the comfort of *nix in the Windows environment (and you can use it in your Windows command shell, or use a *nix shell of your choice). It gives you a whole host of *nix commands (like which) for Windows, and you can just include that directory in your PATH.
In PowerShell, it is gcm, which gives formatted information about other commands. If you want to retrieve only path to executable, use .Source.
For instance: gcm git or (gcm git).Source
Tidbits:
Available for Windows XP.
Available since PowerShell 1.0.
gcm is an alias of Get-Command cmdlet.
Without any parameters, it lists down all the available commands offered by the host shell.
You can create a custom alias with Set-Alias which gcm and use it like: (which git).Source.
Official docs: https://technet.microsoft.com/en-us/library/ee176842.aspx
I have a function in my PowerShell profile named 'which'
function which {
get-command $args[0]| format-list
}
Here's what the output looks like:
PS C:\Users\fez> which python
Name : python.exe
CommandType : Application
Definition : C:\Python27\python.exe
Extension : .exe
Path : C:\Python27\python.exe
FileVersionInfo : File: C:\Python27\python.exe
InternalName:
OriginalFilename:
FileVersion:
FileDescription:
Product:
ProductVersion:
Debug: False
Patched: False
PreRelease: False
PrivateBuild: False
SpecialBuild: False
Language:
Go get unxutils from here: http://sourceforge.net/projects/unxutils/
gold on windows platforms, puts all the nice unix utilities on a standard windows DOS. Been using it for years.
It has a 'which' included. Note that it's case sensitive though.
NB: to install it explode the zip somewhere and add ...\UnxUtils\usr\local\wbin\ to your system path env variable.
If you can find a free Pascal compiler, you can compile this. At least it works and shows the algorithm necessary.
program Whence (input, output);
Uses Dos, my_funk;
Const program_version = '1.00';
program_date = '17 March 1994';
VAR path_str : string;
command_name : NameStr;
command_extension : ExtStr;
command_directory : DirStr;
search_dir : DirStr;
result : DirStr;
procedure Check_for (file_name : string);
{ Check existence of the passed parameter. If exists, then state so }
{ and exit. }
begin
if Fsearch(file_name, '') <> '' then
begin
WriteLn('DOS command = ', Fexpand(file_name));
Halt(0); { structured ? whaddayamean structured ? }
end;
end;
function Get_next_dir : DirStr;
{ Returns the next directory from the path variable, truncating the }
{ variable every time. Implicit input (but not passed as parameter) }
{ is, therefore, path_str }
var semic_pos : Byte;
begin
semic_pos := Pos(';', path_str);
if (semic_pos = 0) then
begin
Get_next_dir := '';
Exit;
end;
result := Copy(Path_str, 1, (semic_pos - 1)); { return result }
{ Hmm! although *I* never reference a Root drive (my directory tree) }
{ is 1/2 way structured), some network logon software which I run }
{ does (it adds Z:\ to the path). This means that I have to allow }
{ path entries with & without a terminating backslash. I'll delete }
{ anysuch here since I always add one in the main program below. }
if (Copy(result, (Length(result)), 1) = '\') then
Delete(result, Length(result), 1);
path_str := Copy(path_str,(semic_pos + 1),
(length(path_str) - semic_pos));
Get_next_dir := result;
end; { Of function get_next_dir }
begin
{ The following is a kludge which makes the function Get_next_dir easier }
{ to implement. By appending a semi-colon to the end of the path }
{ Get_next_dir doesn't need to handle the special case of the last entry }
{ which normally doesn't have a semic afterwards. It may be a kludge, }
{ but it's a documented kludge (you might even call it a refinement). }
path_str := GetEnv('Path') + ';';
if (paramCount = 0) then
begin
WriteLn('Whence: V', program_version, ' from ', program_date);
Writeln;
WriteLn('Usage: WHENCE command[.extension]');
WriteLn;
WriteLn('Whence is a ''find file''type utility witha difference');
Writeln('There are are already more than enough of those :-)');
Write ('Use Whence when you''re not sure where a command which you ');
WriteLn('want to invoke');
WriteLn('actually resides.');
Write ('If you intend to invoke the command with an extension e.g ');
Writeln('"my_cmd.exe param"');
Write ('then invoke Whence with the same extension e.g ');
WriteLn('"Whence my_cmd.exe"');
Write ('otherwise a simple "Whence my_cmd" will suffice; Whence will ');
Write ('then search the current directory and each directory in the ');
Write ('for My_cmd.com, then My_cmd.exe and lastly for my_cmd.bat, ');
Write ('just as DOS does');
Halt(0);
end;
Fsplit(paramStr(1), command_directory, command_name, command_extension);
if (command_directory <> '') then
begin
WriteLn('directory detected *', command_directory, '*');
Halt(0);
end;
if (command_extension <> '') then
begin
path_str := Fsearch(paramstr(1), ''); { Current directory }
if (path_str <> '') then WriteLn('Dos command = "', Fexpand(path_str), '"')
else
begin
path_str := Fsearch(paramstr(1), GetEnv('path'));
if (path_str <> '') then WriteLn('Dos command = "', Fexpand(path_str), '"')
else Writeln('command not found in path.');
end;
end
else
begin
{ O.K, the way it works, DOS looks for a command firstly in the current }
{ directory, then in each directory in the Path. If no extension is }
{ given and several commands of the same name exist, then .COM has }
{ priority over .EXE, has priority over .BAT }
Check_for(paramstr(1) + '.com'); { won't return if file is found }
Check_for(paramstr(1) + '.exe');
Check_for(paramstr(1) + '.bat');
{ Not in current directory, search through path ... }
search_dir := Get_next_dir;
while (search_dir <> '') do
begin
Check_for(search_dir + '\' + paramstr(1) + '.com');
Check_for(search_dir + '\' + paramstr(1) + '.exe');
Check_for(search_dir + '\' + paramstr(1) + '.bat');
search_dir := Get_next_dir;
end;
WriteLn('DOS command not found: ', paramstr(1));
end;
end.
Not in stock Windows but it is provided by Services for Unix and there are several simple batch scripts floating around that accomplish the same thing such this this one.
The best version of this I've found on Windows is Joseph Newcomer's "whereis" utility, which is available (with source) from his site.
The article about the development of "whereis" is worth reading.
None of the Win32 ports of Unix which that I could find on the Internet are satistactory, because they all have one or more of these shortcomings:
No support for Windows PATHEXT variable. (Which defines the list of extensions implicitely added to each command before scanning the path, and in which order.) (I use a lot of tcl scripts, and no publicly available which tool could find them.)
No support for cmd.exe code pages, which makes them display paths with non-ascii characters incorrectly. (I'm very sensitive to that, with the รง in my first name :-))
No support for the distinct search rules in cmd.exe and the PowerShell command line. (No publicly available tool will find .ps1 scripts in a PowerShell window, but not in a cmd window!)
So I eventually wrote my own which, that suports all the above correctly.
Available there:
http://jf.larvoire.free.fr/progs/which.exe
This batch file uses CMD variable handling to find the command that would be executed in the path. Note: that the current directory is always done before the path) and depending on which API call is used other locations are searched before/after the path.
#echo off
echo.
echo PathFind - Finds the first file in in a path
echo ======== = ===== === ===== ==== == == = ====
echo.
echo Searching for %1 in %path%
echo.
set a=%~$PATH:1
If "%a%"=="" (Echo %1 not found) else (echo %1 found at %a%)
See set /? for help.
You can first install Git from Downloading Git, and then open Git Bash and type:
which app-name
I am using GOW (GNU on Windows) which is a light version of Cygwin. You can grab it from GitHub here.
GOW (GNU on Windows) is the lightweight alternative to Cygwin. It uses
a convenient Windows installer that installs about 130 extremely
useful open source UNIX applications compiled as native win32
binaries. It is designed to be as small as possible, about 10 MB, as
opposed to Cygwin which can run well over 100 MB depending upon
options. - About Description(Brent R. Matzelle)
A screenshot of a list of commands included in GOW:
I have created tool similar to Ned Batchelder:
Searching .dll and .exe files in PATH
While my tool is primarly for searching of various dll versions it shows more info (date, size, version) but it do not use PATHEXT (I hope to update my tool soon).
Just have to post this Windows' one liner batch file:
C:>type wh.cmd
#for %%f in (%*) do for %%e in (%PATHEXT% .dll .lnk) do for %%b in (%%f%%e) do for %%d in (%PATH%) do if exist %%d\%%b echo %%d\%%b
A test:
C:>wh ssh
C:\cygwin64\bin\ssh.EXE
C:\Windows\System32\OpenSSH\\ssh.EXE
Not quite a one-liner if you wrap the code in setlocal enableextensions and endlocal.
For you Windows XP users (who have no where command built-in), I have written a "where like" command as a rubygem called whichr.
To install it, install Ruby.
Then
gem install whichr
Run it like:
C:> whichr cmd_here
TCC and TCC/LE from JPSoft are CMD.EXE replacements that add significant functionality. Relevant to the OP's question, which is a builtin command for TCC family command processors.
I have used the which module from npm for quite a while, and it works very well: https://www.npmjs.com/package/which
It is a great multi platform alternative.
Now I switched to the which that comes with Git. Just add to your path the /usr/bin path from Git, which is usually at C:\Program Files\Git\usr\bin\which.exe. The which binary will be at C:\Program Files\Git\usr\bin\which.exe. It is faster and also works as expected.
try this
set a=%~$dir:1
If "%for%"=="" (Echo %1 not found) else (echo %1 found at %a%)
It is possible to download all of the UNIX commands compiled for Windows, including which from this GitHub repository: https://github.com/George-Ogden/UNIX
Here is a function which I made to find executable similar to the Unix command 'WHICH`
app_path_func.cmd:
#ECHO OFF
CLS
FOR /F "skip=2 tokens=1,2* USEBACKQ" %%N IN (`reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\%~1" /t REG_SZ /v "Path"`) DO (
IF /I "%%N" == "Path" (
SET wherepath=%%P%~1
GoTo Found
)
)
FOR /F "tokens=* USEBACKQ" %%F IN (`where.exe %~1`) DO (
SET wherepath=%%F
GoTo Found
)
FOR /F "tokens=* USEBACKQ" %%F IN (`where.exe /R "%PROGRAMFILES%" %~1`) DO (
SET wherepath=%%F
GoTo Found
)
FOR /F "tokens=* USEBACKQ" %%F IN (`where.exe /R "%PROGRAMFILES(x86)%" %~1`) DO (
SET wherepath=%%F
GoTo Found
)
FOR /F "tokens=* USEBACKQ" %%F IN (`where.exe /R "%WINDIR%" %~1`) DO (
SET wherepath=%%F
GoTo Found
)
:Found
SET %2=%wherepath%
:End
Test:
#ECHO OFF
CLS
CALL "app_path_func.cmd" WINWORD.EXE PROGPATH
ECHO %PROGPATH%
PAUSE
Result:
C:\Program Files (x86)\Microsoft Office\Office15\
Press any key to continue . . .
https://www.freesoftwareservers.com/display/FREES/Find+Executable+via+Batch+-+Microsoft+Office+Example+-+WINWORD+-+Find+Microsoft+Office+Path

How to export and import environment variables in windows?

I found it is hard to keep my environment variables sync on different machines. I just want to export the settings from one computer and import to other ones.
I think it should be possible, but don't know how to do it. Can anyone help me? Thanks.
You can use RegEdit to export the following two keys:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
HKEY_CURRENT_USER\Environment
The first set are system/global environment variables; the second set are user-level variables. Edit as needed and then import the .reg files on the new machine.
I would use the SET command from the command prompt to export all the variables, rather than just PATH as recommended above.
C:\> SET >> allvariables.txt
To import the variablies, one can use a simple loop:
C:\> for /F %A in (allvariables.txt) do SET %A
To export user variables, open a command prompt and use regedit with /e
Example :
regedit /e "%userprofile%\Desktop\my_user_env_variables.reg" "HKEY_CURRENT_USER\Environment"
Combine #vincsilver and #jdigital's answers with some modifications,
export .reg to current directory
add date mark
code:
set TODAY=%DATE:~0,4%-%DATE:~5,2%-%DATE:~8,2%
regedit /e "%CD%\user_env_variables[%TODAY%].reg" "HKEY_CURRENT_USER\Environment"
regedit /e "%CD%\global_env_variables[%TODAY%].reg" "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
Output would like:
global_env_variables[2017-02-14].reg
user_env_variables[2017-02-14].reg
You can get access to the environment variables in either the command line or in the registry.
Command Line
If you want a specific environment variable, then just type the name of it (e.g. PATH), followed by a >, and the filename to write to. The following will dump the PATH environment variable to a file named path.txt.
C:\> PATH > path.txt
Registry Method
The Windows Registry holds all the environment variables, in different places depending on which set you are after. You can use the registry Import/Export commands to shift them into the other PC.
For System Variables:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
For User Variables:
HKEY_CURRENT_USER\Environment
My favorite method for doing this is to write it out as a batch script to combine both user variables and system variables into a single backup file like so, create an environment-backup.bat file and put in it:
#echo off
:: RegEdit can only export into a single file at a time, so create two temporary files.
regedit /e "%CD%\environment-backup1.reg" "HKEY_CURRENT_USER\Environment"
regedit /e "%CD%\environment-backup2.reg" "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
:: Concatenate into a single file and remove temporary files.
type "%CD%\environment-backup1.reg" "%CD%\environment-backup2.reg" > environment-backup.reg
del "%CD%\environment-backup1.reg"
del "%CD%\environment-backup2.reg"
This creates environment-backup.reg which you can use to re-import existing environment variables. This will add & override new variables, but not delete existing ones :)
Here is my PowerShell method
gci env:* | sort-object name | Where-Object {$_.Name -like "MyApp*"} | Foreach {"[System.Environment]::SetEnvironmentVariable('$($_.Name)', '$($_.Value)', 'Machine')"}
What it does
Scoops up all environment variables
Filters them
Emits the formatted PowerShell needed to recreate them on another machine (assumes all are set at machine level)
So after running this on the source machine, simply transfer output onto the target machine and execute (elevated prompt if setting at machine level)
A PowerShell script based on #Mithrl's answer
# export_env.ps1
$Date = Get-Date
$DateStr = '{0:dd-MM-yyyy}' -f $Date
mkdir -Force $PWD\env_exports | Out-Null
regedit /e "$PWD\env_exports\user_env_variables[$DateStr].reg" "HKEY_CURRENT_USER\Environment"
regedit /e "$PWD\env_exports\global_env_variables[$DateStr].reg" "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
Not being satisfied with answers from 12 years ago I've approached this a little differently. This approach could work with Win OS flavors older than Win 8 by using SET instead of SETX which is when SETX began being used.
NOTE:
Be sure to tune the RegEx for your preferred editor to achieve desired
results. For RegEx specific questions please seek help from various
sources including tutorials available from here. I'm using Sublime Text 4 for search and replace RegEx examples.
WARNING:
I would like to point out that following this process
blindly with copy and paste will most likely clobber existing settings
with the source data extracted. It DOES NOT merge the two sets of
data. That is your responsibility and I take no responsibility for
any damage that may result. Additionally, you should take time to
remove settings from the extracted env variables that pose issues or
no value such as changed paths and different hardware metrics such as
CPU core counts.
This approach avoids mixing System env variables with User env variables which a handful of previous answers are plagued with.
reg query "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment">>SystemEnvVariablesSourceMachine.txt
reg query "HKEY_CURRENT_USER\Environment">>UserEnvVariablesSourceMachine.txt
Clean up the files that were just created! Import success depends on this!
Use a RegEx capable editor and use the following search and replace:
NOTE: Some RegEx engines/tools require use of the $ character to
represent backreference in the Replace Pattern. If your not getting
the expected results in search and replace give that a try.
Search Pattern:
(?:\A\r?\n|^HKEY_CURRENT_USER\\Environment\r?\n?|^HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment\r?\n?|^\r?\n$|\r?\n\Z)
Replace Pattern (Literally Empty):
Literally Empty
and then
Search Pattern:
^\s+(.*?)\s{4}\w+\s{4}(.*?)$
Replace Pattern:
\1=\2
Its strongly advised you take a moment to do the same steps above on the destination machine using these file names:
SystemEnvVariablesDestinationMachine.txt
UserEnvVariablesDestinationMachine.txt
This also will serve as a backup for the upcoming import.
Once the DestinationMachine versions of the files are cleaned up its time to make a copy. Copy of each of the DestinationMachine files and name them something like:
SystemEnvVariablesFinalMerge.txt
UserEnvVariablesFinalMerge.txt
We're not done yet, that's just a version of the file you can feel safe to edit. Leave the DestinationMachine version of the files alone. Consider them a backup.
Next we will merge the SourceMachine files into the FinalMerge files. This provides a means to
manual review for cleanup of duplicates and bad data followed by a final output. There are plenty of ways
to do this, but the easiest way I've used is to prepare the data for comparison, then compare and merge, and then reassemble the data back
so that its importable.
Apply this search and replace RegEx pattern to each Source and FinalMerge file:
Search Pattern:
(^\w+=|.*?(?:;|$))
Replace Pattern:
\1\n
Then compare each Source to FinalMerge using a diff tool such as Beyond Compare 4, Meld, or Winmerge. My personal favorite is Beyond Compare 4. Keep in mind the data at this time may not be sorted
so you can take care at this time to sort the data taking care not to mix up variables from key to value structure. How to use those tools is out of scope here. Delete env variables that you do not wish to import at this time from the FinalMerge version of the file.
Once you're satisifed with the merge with cleanup applied save the changes in the FinalMerge files then restore the key to value mapping with the following RegEx pattern:
Search Pattern:
(.)$\r?\n
Replace Pattern:
\1
Then on the destination machine import the variables with powershell:
Get-Content .\UserEnvVariablesFinalMerge.txt | ForEach-Object {
$envVarDataSplit = $($_).split("=")
if($($envVarDataSplit).count -gt 0)
{
Write-Output "Key: $($envVarDataSplit[0]) ~ Value: $($envVarDataSplit[1])"
SETX $envVarDataSplit[0] "$($envVarDataSplit[1])"
}
}
NOTE:
Run powershell as administrator for this to succeed or you will
get an error.
Get-Content .\SystemEnvVariablesFinalMerge.txt | ForEach-Object {
$envVarDataSplit = $($_).split("=")
if($($envVarDataSplit).count -gt 0)
{
Write-Output "Key: $($envVarDataSplit[0]) ~ Value: $($envVarDataSplit[1])"
SETX $envVarDataSplit[0] "$($envVarDataSplit[1])" /M
}
}
NOTE:
If you encounter an error here its likely due to a need to
escape a character. You'll need to either manually enter that env
variable or figure out the proper escaped character sequence to get
around it.
If things have gone horribly wrong you should be able to revert to your DestinationMachine versions of the env variables using the previous command with the backup.

Resources