I have an old notebook with Windows 7 64-bit that executes a PowerShell script perfectly every Sunday. Unfortunately it starts to crash as soon as the load increases and I decided to get a new PC. On this PC I previously installed Windows e;10 Pro 64-bit and even here the script was executed every Sunday. Due to the update policy of Microsoft I removed Windows 10 from the new PC and installed Windows 7 64-bit. But now the same script crashes as it does not find wget:
$wg = Start-Process wget.exe -wait -NoNewWindow -PassThru -ArgumentList $argList
Gnu Wget is installed correctly (I think). It is placed at:
C:\Program Files (x86)\GnuWin32\bin\wget.exe
It is even entered in the registry under HKEY_LOCAL_MACHINE → SOFTWARE → Wow6432Node → GnuWin32|Wget|1.11.4-1|setup|InstallPath: C:\Program Files (x86)\GnuWin32.
But despite this if I open the CMD console and enter wget (or wget.exe) I get:
The order "wget" is either misspelled or could not be found.
What do I have to do that PowerShell finds wget constantly even after a restart of the PC? Even e.g. Notepad++ cannot be found by the CMD console despite it is installed properly(?). What's wrong here?
If you want to be able to run a command without specifying its path you need to add the directory it resides in to the PATH environment variable. The install path in the SOFTWARE branch of the registry has nothing to do with it.
To add a directory to the PATH for the current and all future sessions you need to do something like this:
$dir = "${env:ProgramFiles(x86)}\GnuWin32\bin"
# set PATH environment variable for current session
$env:Path += ";${dir}"
# set PATH environment variable for future sessions
$path = [Environment]::GetEnvironmentVariable('PATH', 'Machine')
$path += ";{$dir}"
[Environment]::SetEnvironmentVariable('PATH', $path, 'Machine')
Note, however, that the second step (setting the variable for future sessions) only works correctly if there are no Windows environment variables (%something%) used in $path, because the method saves the value as a REG_SZ in the registry. Windows only expands environment variables in the PATH variable if it's stored as a REG_EXPAND_SZ value.
If you do have regular Windows environment variables somewhere in $path you must manually write the value to the registry with the correct type.
$key = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment'
Set-ItemProperty -Path $key -Name 'Path' -Value $path -Type ExpandString
Addendum:
All of the above applies only if you want to do this programmatically, of course. For a manual approach you can always edit the environment variables via the GUI and restart PowerShell.
Related
While creating a new pipeline on Azure DevOps to set up a CI for a .NET project, I set up the following PowerShell script to automate the .NET Core setup.
Here is the script:
$ErrorActionPreference="Stop"
$ProgressPreference="SilentlyContinue"
# $LocalDotnet is the path to the locally-installed SDK to ensure the
# correct version of the tools are executed.
$LocalDotnet=""
# $InstallDir and $CliVersion variables can come from options to the
# script.
$InstallDir = "./cli-tools"
$CliVersion = "1.0.1"
# Test the path provided by $InstallDir to confirm it exists. If it
# does, it's removed. This is not strictly required, but it's a
# good way to reset the environment.
if (Test-Path $InstallDir)
{
rm -Recurse $InstallDir
}
New-Item -Type "directory" -Path $InstallDir
Write-Host "Downloading the CLI installer..."
# Use the Invoke-WebRequest PowerShell cmdlet to obtain the
# installation script and save it into the installation directory.
Invoke-WebRequest `
-Uri "https://dot.net/v1/dotnet-install.ps1" `
-OutFile "$InstallDir/dotnet-install.ps1"
Write-Host "Installing the CLI requested version ($CliVersion) ..."
# Install the SDK of the version specified in $CliVersion into the
# specified location ($InstallDir).
& $InstallDir/dotnet-install.ps1 -Version $CliVersion `
-InstallDir $InstallDir
Write-Host "Downloading and installation of the SDK is complete."
# $LocalDotnet holds the path to dotnet.exe for future use by the
# script.
$LocalDotnet = "$InstallDir/dotnet"
When I try to run the build, I have got the following error:
and
I've already searched on Google for people who have the same problem and how to fix it. But I haven't found much information yet. The Azure DevOps forum doesn't help either.
As mentioned in the comment from above, all you have to do is install the appropriate version of PowerShell on the machine that Agent is running on. For example, PowerShell 7. Then you have to make sure that the environment variable path is set. This variable should point to the directory with PowerShell Core.
Windows
Just install PowerShell Core with the Windows Installer (.msi file from PowerShell Git repository). In this case, the path environment variable is automatically set or expanded so that there will be the path to the directory with pwsh.exe under this variable.
Linux
Install PowerShell Core that is supported by your distribution. Make sure that there is a path variable in your ~/.bashrc file and that path contains the path to the directory with PowerShell Core.
Note: If Azure Agent is already running, you have to restart it so that it sees the changes in the path variable. Hence, on Windows, just restart the agent if run interactively and restart the service if run as a service. On Linux, you can follow this guide in order to update the environment variables that were passed to the Agent.
I know you have already configured your script as a PowerShell Core script, but for completeness I add this: If you use a PowerShell task in your Azure pipeline, the Core version of PowerShell is not set for it by default. In order to run the task as the PowerShell Core script, add this to the YAML code of the task: pwsh: true. Otherwise, if you are still using the old graphical interface, check the "Use PowerShell Core" checkbox under the "Advanced" heading for the task.
My company uses a program that breaks when Java is updated. This is due to the program install (I assume) placing a static path to Java in the Path environment variable. For example, the current Path variable in question is C:\Program Files (x86)\Java\jre1.8.0_171\bin\client, but if Java is updated and the program is re-installed, the Path variable will update to include C:\Program Files (x86)\Java\jre1.8.0_181\bin\client.
I was able to find exactly what I needed (I think) here https://blogs.technet.microsoft.com/heyscriptingguy/2007/11/08/hey-scripting-guy-how-can-i-remove-a-value-from-the-path-environment-variable/, but that code is for Powershell 2.0 and doesn't work on Windows 10.
Is this still possible in Windows 10?
You can use the System.Environment class to modify your environment variables machine-wide:
# get the PATH and split it up
$PATH = [Environment]::GetEnvironmentVariable('PATH', 'Machine') -split ';'
# filter out the JRE paths
$PATH = $PATH -notmatch 'java\\jre'
# get any real JRE paths
$PATH += (Get-Item -Path "${Env:ProgramFiles(x86)}\Java\jre*\bin\client").FullName
$PATH = $PATH -join ';'
[Environment]::SetEnvironmentVariable('PATH', $PATH, 'Machine')
Note: You will need to run your shell elevated to execute these commands.
Java is not a Windows issue, and therefore TechNet will not help you. There is a fix below.
Windows 10 and Windows 8:
Open the Legacy Control Panel
Click the Advanced system settings link.
Click Environment Variables. In the section System Variables, find the PATH environment variable and select it.
Click Edit.
If the PATH environment variable does not exist, click New.
In the Edit System Variable (or New System Variable) window, specify the value of the PATH environment variable.
Click OK.
Close all remaining windows by clicking OK.
Reopen the PowerShell window, and run your Java code.
Source: https://www.java.com/en/download/help/path.xml
I am writing automated script for cloning GitHub source code to local machine.
I failed after installing Git in my script, it asked for close/open powershell.
So I am not able to clone code automatic after installing Git.
Here is my code:
iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
choco install -y git
refreshenv
Start-Sleep -Seconds 15
git clone --mirror https://${username}:${password}#$hostname/${username}/$Projectname.git D:\GitTemp -q 2>&1 | %{ "$_" }
Error:
git : The term 'git' is not recognized as the name of a cmdlet,
function, script file, or operable program.
Check the spelling of the name, or if a path was included,
verify that the path is correct and try again.
Please let me what should I put for reboot PowerShell without exiting?
You have a bootstrapping problem:
refreshenv (an alias for Update-SessionEnvironment) is generally the right command to use to update the current session with environment-variable changes after a choco install ... command.
However, immediately after installing Chocolatey itself, refreshenv / Update-SessionEnvironment themselves are only available in future PowerShell sessions, because loading these commands happens via code added to profile $PROFILE, based on environment variable $env:ChocolateyInstall.
That said, you should be able to emulate what Chocolatey does when $PROFILE is sourced in future sessions in order to be able to use refreshenv / Update-SessionEnvironment right away, immediately after installing Chocolatey:
iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
choco install -y git
# Make `refreshenv` available right away, by defining the $env:ChocolateyInstall
# variable and importing the Chocolatey profile module.
# Note: Using `. $PROFILE` instead *may* work, but isn't guaranteed to.
$env:ChocolateyInstall = Convert-Path "$((Get-Command choco).Path)\..\.."
Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
# refreshenv is now an alias for Update-SessionEnvironment
# (rather than invoking refreshenv.cmd, the *batch file* for use with cmd.exe)
# This should make git.exe accessible via the refreshed $env:PATH, so that it
# can be called by name only.
refreshenv
# Verify that git can be called.
git --version
Note: The original solution used . $PROFILE instead of Import-Module ... to load the Chocolatey profile, relying on Chocolatey to have updated $PROFILE already at that point. However, ferventcoder points out that this updating of $PROFILE doesn't always happen, so it cannot be relied upon.
NEW:
The old approach I originally answered with has a few quirks with environment variables that use a ; delimiter. I tried compensated by handling PATH seperately, but sometimes there are other such variables.
So this is the new approach; you might want to put it in a script or something. It has to nest a couple of powershell processes real quick, which isn't ideal, but it's the only reliable way I've found to escape the active environment and capture the output.
# Call a powershell process to act as a wrapper to capture the output:
& ([Diagnostics.Process]::GetCurrentProcess().ProcessName) -NoP -c (
# String wrapper to help make the code more readable through comma-separation:
[String]::Join(' ', (
# Start a process that escapes the active environment:
'Start-Process', [Diagnostics.Process]::GetCurrentProcess().ProcessName,
'-UseNewEnvironment -NoNewWindow -Wait -Args ''-c'',',
# List the environment variables, separated by a tab character:
'''Get-ChildItem env: | &{process{ $_.Key + [char]9 + $_.Value }}'''
))) | &{process{
# Set each line of output to a process-scoped environment variable:
[Environment]::SetEnvironmentVariable(
$_.Split("`t")[0], # Key
$_.Split("`t")[1], # Value
'Process' # Scope
)
}}
OLD:
Did my best to try and make it a one-liner, but since the PATH variable needs special handling, I couldn't do it without making a stupidly long line. On the plus side, you don't need to rely on any third-party modules:
foreach ($s in 'Machine','User') {
[Environment]::GetEnvironmentVariables($s).GetEnumerator().
Where({$_.Key -ne 'PATH'}) | ForEach-Object {
[Environment]::SetEnvironmentVariable($_.Key,$_.Value,'Process') }}
$env:PATH = ( ('Machine','User').ForEach({
[Environment]::GetEnvironmentVariable('PATH',$_)}).
Split(';').Where({$_}) | Select-Object -Unique ) -join ';'
The code is scoped to the process, so you don't have to worry about anything getting messed up (not that it would, I tested it).
Note: Neither approach removes uniquely-named environment variables that were created in your active environment, so if you defined $env:FOO = 'bar' and 'FOO' is not normally one of your environment variables, $env:FOO will still return bar even after any of the above code is ran.
You can try and use Update-SessionEnvironment:
Updates the environment variables of the current powershell session with
any environment variable changes that may have occured during a Chocolatey package install.
That will test if that change is still effective after the chocolatey call.
If not, one easy workaround would be at least to use an absolute path for calling git.
To call Git from Powershell:
new-item -path alias:git -value 'C:\Program Files\Git\bin\git.exe'
Then you can try:
git clone --mirror https://${username}:${password}#$hostname/${username}/$Projectname.git D:\GitTemp -q 2>&1 | %{ "$_" }
I go with the lo-tech solution:
$env:Path += ";C:\Program Files\Git\bin"
I'm working on automating an AppFabric installation using PowerShell, and I've run into a problem where the script is calling the installer, waiting for it to complete, but I am unable to import the installed modules after from the same context. i.e:
Start-Process "C:\provision\WindowsServerAppFabricSetup_x64.exe" -ArgumentList "/i /GAC" -Wait
Import-Module DistributedCacheConfiguration
# ...do configuration things...
Which errors: The specified module 'DistributedCacheConfiguration' was not loaded because no valid module file was found in any module directory.
If you close and re-open PowerShell, the script runs fine. Adding a Start-Sleep 60 between the installer and the configuration didn't help, so I tried calling it as though powershell were restarting:
C:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe C:\provision\appfabric_config.ps1
The same errors were thrown. How do I get PowerShell to recognize the newly installed modules?
PowerShell looks for modules in subdirectories of the directories listed in the PSModulePath environment variable. Environment variables are read from the registry key HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment when the session is initialized.
If the installer places the new module in a directory that's not already in PSModulePath and then adds that directory to the environment variable, it's modifying the environment variable in the registry, not in the current PowerShell console session's environment, so only PowerShell sessions started after the installation will have the updated PSModulePath.
You can manually update the value from the registry by adding the following line after the installation and before attempting to import the module:
$env:PSModulePath = (Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Session Manager\Environment' -Name PSModulePath).PSModulePath
Note that although it may appear redundant, the reason you need
(Get-ItemProperty -Path [...] -Name PSModulePath).PSModulePath
rather than just
Get-ItemProperty -Path [...] -Name PSModulePath
is that Get-ItemProperty doesn't return the data of the named registry value, it returns a PSCustomObject that contains information about the registry value, and the data is in a property of that PSCustomObject that has the name of the registry value (i.e. PSModulePath in this case). If you prefer, you could also do it this way:
$env:PSModulePath = Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Session Manager\Environment' -Name PSModulePath | select -ExpandProperty PSModulePath
(There's no practical difference, it's six or a half dozen.)
You can use the .NET System.Environment library to access your environment variables. Your new module is most likely added to your "User" environment variable. You can also experiment with specifying "Machine" and "Process" targets. See Environment.GetEnvironmentVariable for more info. Here is a good article on modifying your paths, including adding to and removing entries. It can be easily adapted to the PSModulePath environment variable.
This example adds the PSModulePath environment variable for the User to the end of the PSModulePath environment variable in your session. It will result in some duplicate entries, but should work just fine.
$env:PSModulePath = $env:PSModulePath+';'+[System.Environment]::GetEnvironmentVariable("PSModulePath","User")
Your code would now look like this:
Start-Process "C:\provision\WindowsServerAppFabricSetup_x64.exe" -ArgumentList "/i /GAC" -Wait
$env:PSModulePath = $env:PSModulePath+';'+[System.Environment]::GetEnvironmentVariable("PSModulePath","User")
Import-Module DistributedCacheConfiguration
# ...do configuration things...
Powershell is great for scripting. But when it comes to everyday use, certain things can be a huge PITA!!
so i thought it would be great if i could do something like this in my profile.ps1:
$env:path = "$($env:path);c:\cygwin\bin"
to get access to utilities like tar, zip, etc... but this doesn't work. The variable looks right when i do:
PS > $env:path
but when i try to do, say,
PS > unzip foo.zip
i get a command not found type error.
WTF PowerShell!?
edit: great answers! I looked at it with fresh eyes this morning and realized that I just needed to spell 'cygwin' correctly! now I don't have to switch back and forth between two consoles. It should be noted for anyone who uses this tip that your path in powershell is evaluated in order - if you put c:\cygwin\bin at the end of the $env:path variable, it will be searched last, so it won't interfere with existing powershell aliases / cmdlets.
It worked for me:
To set your profile:
$command = '$env:path = $env:path + ";C:\Program Files\Notepad++"'
$command | Out-File -FilePath $PROFILE -Append -Encoding UTF8
Or just the current shell:
$env:path = $env:path + ";C:\Program Files\Notepad++"
Using $env:path to add the cygwin bin to PATH should work as long as you are trying to use it in the same Powershell session. If you open a new console or if you close and open Powershell, it will not be persisted. Otherwise, what you are doing should work. Make sure you are indeed adding the correct path. If you want to persist the changes, add the line to your $profile.
Also, try using the Mingw / Msys / Msysgit utils. I find Mingw to be more lightweight than cygwin ( if you are using cygwin just to get some of these utils.)
PowerShell by default is only going to modify its local copy of PATH. When you run an external command, they aren't going to see the local environment variables.
Per this TechNet article, you can fall back to the .NET static method SetEnvironmentVariable to do this at the user level if you want this to be a permanent change:
[Environment]::SetEnvironmentVariable("TestVariable", "Test value.", "User")