I want unnatended install of rust, so I did this little script:
Write-Host "Installing Rust..." -ForegroundColor Cyan
$exePath = "$env:TEMP\rustup-init.exe"
Write-Host "Downloading..."
(New-Object Net.WebClient).DownloadFile('https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe', $exePath)
Write-Host "Installing..."
cmd /c start /wait $exePath -y
Remove-Item $exePath
$addPath = "$env:USERPROFILE\.cargo\bin"
[Environment]::SetEnvironmentVariable('PATH', $env:PATH, $addPath)
but I get
virtualbox-iso: Cannot convert argument "target", with value: "C:\Users\vagrant\.cargo\bin", for "SetEnvironmentVariable" to type
virtualbox-iso: "System.EnvironmentVariableTarget": "Cannot convert value "C:\Users\vagrant\.cargo\bin" to type
virtualbox-iso: "System.EnvironmentVariableTarget". Error: "Unable to match the identifier name C:\Users\vagrant\.cargo\bin to a valid
it looks like a text cannot be converted to a PATH type? What does it mean?
CodePudding user response:
The immediate solution to your problem is that you need to call [Environment]::SetEnvironmentVariable()
as follows:
# Modify the user-level PATH definition.
# To modify the machine-level definition, use 'Machine' instead of user 'User',
# but you then need to run with ELEVATION (as admin).
# !! SEE CAVEATS BELOW.
[Environment]::SetEnvironmentVariable('PATH', ($env:PATH ";$addPath"), 'User')
That is, as Herohtar points out, the method expects only a complete, new value, as the second parameter, and its third parameter specifies the target scope for the persistent, registry-based environment-variable definition.
Caveats:
The process-level
$env:PATH
value is a composite value of the machine-level and user-level definitions in the registry, so with the command above you're in effect duplicating entries from the respective other scope.Additionally, redefining the variable this way replaces any entries in the value that are defined in terms of other environment variables with the expanded, literal values.
$env:PATH
contains expanded values to begin with, but even reading the unexpanded value directly from the registry won't work, because[Environment]::SetEnvironmentVariable()
invariably writes the given string as aREG_SZ
value (literal string) rather than as anREG_EXPAND_SZ
value (string that may contain%FOO%
-style references to other environment variables). Similarly,[Environment]::GetEnvironmentVariable()
only reports expanded values.
A proper solution requires quite a bit of extra work, as discussed in this answer, which contains helper function Add-Path
.
A pragmatic shortcut - if you're willing to accept that your registry definitions are converted to literal strings - is the following:
Read the current definition - for the target scope only - from the registry, via
[Environment]::GetEnvironmentVariable()
Add to this scope-specific value and save it back to the same scope.
Additionally, you may want to add the new entry to the in-process definition of
$env:PATH
so that the change takes immediate effect.
Here is the solution in the context of a streamlined version of your code:
Write-Host "Installing Rust..." -ForegroundColor Cyan
$exePath = "$env:TEMP\rustup-init.exe"
Write-Host "Downloading..."
Invoke-WebRequest 'https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe' -OutFile $exePath
Write-Host "Installing..."
& $exePath -y
Remove-Item $exePath
$addPath = "$env:USERPROFILE\.cargo\bin"
$scope = 'User' # Change to 'Machine', if needed, which then requires ELEVATION.
[Environment]::SetEnvironmentVariable(
'PATH',
([Environment]::GetEnvironmentVariable('PATH', $scope) ";$addPath"),
$scope
)
# Also update the current process' definition
$env:PATH = ";$addPath"