Reputation: 85
I am attempting to use PowerShell to download the Python 3 installer from the Python website into a specific directory, then silently run/install the .exe file in that same directory and then add the appropriate directory to my system's PATH variable.
So far I have come up with:
start cmd /k powershell -Command "(New-Object Net.WebClient).DownloadFile('https://www.python.org/ftp/python/3.6.2/python-3.6.2.exe', 'C:/Tools/python-3.6.2.exe')" &&
c:\Tools\python-3.6.2.exe /quiet InstallAllUsers=1 PrependPath=1 Include_test=0 TargetDir=c:\Tools\Python362 &&
setx path "%PATH%;C:\Tools\Python362\" /M
Unfortunately, this does not work. The command window will open, and then immediately quit. I have run each of these commands separately, and when I do so they work. However, when I try to run them sequentially in the same file it does not work. How can I fix this?
Note:
I believe the problem stems from the use of &&
, because if I use &
the CMD prompt will persist, and execute. However, this does not help me since I need the second command to execute after the first one has finished or else there isn't any .exe file for the second command to run. I am hoping this is just a syntactic error as I am very new to creating batch files and working with the Windows command line.
Upvotes: 8
Views: 24124
Reputation: 1
Here's something I put together for Windows to check the latest Python interpreter version from endoflife.date/api. Download it on your C: drive and include the Python executable to your user's path:
@echo off
setlocal EnableDelayedExpansion
set url=https://endoflife.date/api/python.json
set "response="
for /f "usebackq delims=" %%i in (`powershell -command "& {(Invoke-WebRequest -Uri '%url%').Content}"`) do set "response=!response!%%i"
set "latest_py_version="
for /f "tokens=1,2 delims=}" %%a in ("%response%") do (
set "object=%%a}"
for %%x in (!object!) do (
for /f "tokens=1,* delims=:" %%y in ("%%x") do (
if "%%~y" == "latest" (
set "latest_py_version=%%~z"
)
)
)
)
echo %latest_py_version%
REM Set the minimum required Python version
set python_version=%latest_py_version%
REM Check if Python is already installed and if the version is less than python_version
echo Checking if Python %python_version% or greater is already installed...
set "current_version="
where python >nul 2>nul && (
for /f "tokens=2" %%v in ('python --version 2^>^&1') do set "current_version=%%v"
)
if "%current_version%"=="" (
echo Python is not installed. Proceeding with installation.
) else (
if "%current_version%" geq "%python_version%" (
echo Python %python_version% or greater is already installed. Exiting.
pause
exit
)
)
REM Define the URL and file name of the Python installer
set "url=https://www.python.org/ftp/python/%python_version%/python-%python_version%-amd64.exe"
set "installer=python-%python_version%-amd64.exe"
REM Define the installation directory
set "targetdir=C:\Python%python_version%"
REM Download the Python installer
echo Downloading Python installer...
powershell -Command "(New-Object Net.WebClient).DownloadFile('%url%', '%installer%')"
REM Install Python with a spinner animation
echo Installing Python...
start /wait %installer% /quiet /passive TargetDir=%targetdir% Include_test=0 ^
&& (echo Done.) || (echo Failed!)
echo.
REM Add Python to the system PATH
echo Adding Python to the system PATH...
setx PATH "%targetdir%;%PATH%"
if %errorlevel% EQU 1 (
echo Python has been successfully installed to your system BUT failed to set system PATH. Try running the script as administrator.
pause
exit
)
echo Python %python_version% has been successfully installed and added to the system PATH.
REM Cleanup
echo Cleaning up...
del %installer%
echo Done!
pause
Upvotes: 0
Reputation: 1096
I'd do it all in PowerShell, personally.
I'd be tempted to put it in a script, like this:
[CmdletBinding()] Param(
$pythonVersion = "3.6.2"
$pythonUrl = "https://www.python.org/ftp/python/$pythonVersion/python-$pythonVersion.exe"
$pythonDownloadPath = 'C:\Tools\python-$pythonVersion.exe'
$pythonInstallDir = "C:\Tools\Python$pythonVersion"
)
(New-Object Net.WebClient).DownloadFile($pythonUrl, $pythonDownloadPath)
& $pythonDownloadPath /quiet InstallAllUsers=1 PrependPath=1 Include_test=0 TargetDir=$pythonInstallDir
if ($LASTEXITCODE -ne 0) {
throw "The python installer at '$pythonDownloadPath' exited with error code '$LASTEXITCODE'"
}
# Set the PATH environment variable for the entire
# machine (that is, for all users) to include
# the Python install directory
[Environment]::SetEnvironmentVariable("PATH", "${env:path};${pythonInstallDir}", "Machine")
Then you can call the script from cmd.exe like this:
powershell.exe -File X:\Path\to\Install-Python.ps1
The Param()
block defines defaults for the Python version, the URL to download it from, the place to save it, and the place to install it to, but it lets you override these options if that ever becomes useful. You might pass those arguments like this:
powershell.exe -File X:\Path\to\Install-Python.ps1 -version 3.4.0 -pythonInstallDir X:\Somewhere\Else\Python3.4.0
That said, you absolutely can just do a one-liner in pure PowerShell as well. From your description, I don't think you should need to do the start cmd /k
prefix - you should be able to just call powershell.exe directly, like this:
powershell -command "(New-Object Net.WebClient).DownloadFile('https://www.python.org/ftp/python/3.6.2/python-3.6.2.exe', 'C:/Tools/python-3.6.2.exe'); & c:\Tools\python-3.6.2.exe /quiet InstallAllUsers=1 PrependPath=1 Include_test=0 TargetDir=c:\Tools\Python362; [Environment]::SetEnvironmentVariable('PATH', ${env:path} + ';C:\Tools\Python362', 'Machine')"
Upvotes: 7
Reputation: 1154
Do it all in PowerShell, including installing a library.
# This is the link to download Python 3.6.7 from Python.org
# See https://www.python.org/downloads/
$pythonUrl = "https://www.python.org/ftp/python/3.6.7/python-3.6.7-amd64.exe"
# This is the directory that the exe is downloaded to
$tempDirectory = "C:\temp_provision\"
# Installation Directory
# Some packages look for Python here
$targetDir = "C:\Python36"
# Create the download directory and get the exe file
$pythonNameLoc = $tempDirectory + "python367.exe"
New-Item -ItemType directory -Path $tempDirectory -Force | Out-Null
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
(New-Object System.Net.WebClient).DownloadFile($pythonUrl, $pythonNameLoc)
# These are the silent arguments for the install of Python
# See https://docs.python.org/3/using/windows.html
$Arguments = @()
$Arguments += "/i"
$Arguments += 'InstallAllUsers="1"'
$Arguments += 'TargetDir="' + $targetDir + '"'
$Arguments += 'DefaultAllUsersTargetDir="' + $targetDir + '"'
$Arguments += 'AssociateFiles="1"'
$Arguments += 'PrependPath="1"'
$Arguments += 'Include_doc="1"'
$Arguments += 'Include_debug="1"'
$Arguments += 'Include_dev="1"'
$Arguments += 'Include_exe="1"'
$Arguments += 'Include_launcher="1"'
$Arguments += 'InstallLauncherAllUsers="1"'
$Arguments += 'Include_lib="1"'
$Arguments += 'Include_pip="1"'
$Arguments += 'Include_symbols="1"'
$Arguments += 'Include_tcltk="1"'
$Arguments += 'Include_test="1"'
$Arguments += 'Include_tools="1"'
$Arguments += 'Include_launcher="1"'
$Arguments += 'Include_launcher="1"'
$Arguments += 'Include_launcher="1"'
$Arguments += 'Include_launcher="1"'
$Arguments += 'Include_launcher="1"'
$Arguments += 'Include_launcher="1"'
$Arguments += "/passive"
# Install Python
Start-Process $pythonNameLoc -ArgumentList $Arguments -Wait
Function Get-EnvVariableNameList {
[cmdletbinding()]
$allEnvVars = Get-ChildItem Env:
$allEnvNamesArray = $allEnvVars.Name
$pathEnvNamesList = New-Object System.Collections.ArrayList
$pathEnvNamesList.AddRange($allEnvNamesArray)
return ,$pathEnvNamesList
}
Function Add-EnvVarIfNotPresent {
Param (
[string]$variableNameToAdd,
[string]$variableValueToAdd
)
$nameList = Get-EnvVariableNameList
$alreadyPresentCount = ($nameList | Where{$_ -like $variableNameToAdd}).Count
if ($alreadyPresentCount -eq 0)
{
[System.Environment]::SetEnvironmentVariable($variableNameToAdd, $variableValueToAdd, [System.EnvironmentVariableTarget]::Machine)
[System.Environment]::SetEnvironmentVariable($variableNameToAdd, $variableValueToAdd, [System.EnvironmentVariableTarget]::Process)
[System.Environment]::SetEnvironmentVariable($variableNameToAdd, $variableValueToAdd, [System.EnvironmentVariableTarget]::User)
$message = "Enviromental variable added to machine, process and user to include $variableNameToAdd"
}
else
{
$message = 'Environmental variable already exists. Consider using a different function to modify it'
}
Write-Information $message
}
Function Get-EnvExtensionList {
[cmdletbinding()]
$pathExtArray = ($env:PATHEXT).Split("{;}")
$pathExtList = New-Object System.Collections.ArrayList
$pathExtList.AddRange($pathExtArray)
return ,$pathExtList
}
Function Add-EnvExtension {
Param (
[string]$pathExtToAdd
)
$pathList = Get-EnvExtensionList
$alreadyPresentCount = ($pathList | Where{$_ -like $pathToAdd}).Count
if ($alreadyPresentCount -eq 0)
{
$pathList.Add($pathExtToAdd)
$returnPath = $pathList -join ";"
[System.Environment]::SetEnvironmentVariable('pathext', $returnPath, [System.EnvironmentVariableTarget]::Machine)
[System.Environment]::SetEnvironmentVariable('pathext', $returnPath, [System.EnvironmentVariableTarget]::Process)
[System.Environment]::SetEnvironmentVariable('pathext', $returnPath, [System.EnvironmentVariableTarget]::User)
$message = "Path extension added to machine, process and user paths to include $pathExtToAdd"
}
else
{
$message = 'Path extension already exists'
}
Write-Information $message
}
Function Get-EnvPathList {
[cmdletbinding()]
$pathArray = ($env:PATH).Split("{;}")
$pathList = New-Object System.Collections.ArrayList
$pathList.AddRange($pathArray)
return ,$pathList
}
Function Add-EnvPath {
Param (
[string]$pathToAdd
)
$pathList = Get-EnvPathList
$alreadyPresentCount = ($pathList | Where{$_ -like $pathToAdd}).Count
if ($alreadyPresentCount -eq 0)
{
$pathList.Add($pathToAdd)
$returnPath = $pathList -join ";"
[System.Environment]::SetEnvironmentVariable('path', $returnPath, [System.EnvironmentVariableTarget]::Machine)
[System.Environment]::SetEnvironmentVariable('path', $returnPath, [System.EnvironmentVariableTarget]::Process)
[System.Environment]::SetEnvironmentVariable('path', $returnPath, [System.EnvironmentVariableTarget]::User)
$message = "Path added to machine, process and user paths to include $pathToAdd"
}
else
{
$message = 'Path already exists'
}
Write-Information $message
}
Add-EnvExtension '.PY'
Add-EnvExtension '.PYW'
Add-EnvPath 'C:\Python36\'
# Install a library using Pip
python -m pip install numpy
Upvotes: 3
Reputation: 1
# Set the version and download URL for Python
$version = "3.9.5"
$url = "https://www.python.org/ftp/python/$version/python-$version-amd64.exe"
# Download and install Python
$installPath = "$($env:ProgramFiles)\Python$version"
Invoke-WebRequest $url -OutFile python-$version.exe
Start-Process python-$version.exe -ArgumentList "/quiet", "TargetDir=$installPath" -Wait
# Add Python to the system PATH
$envVariable = [Environment]::GetEnvironmentVariable("Path", "Machine")
if ($envVariable -notlike "*$installPath*") {
[Environment]::SetEnvironmentVariable("Path", "$envVariable;$installPath", "Machine")
Write-Host "Added Python to PATH."
}
# Clean up
Remove-Item python-$version.exe
Upvotes: 0