ninbura
ninbura

Reputation: 468

Restart environment and script during batch script

I've built a few FFmpeg powershell scripts for me and a few others to use and I'm attempting to make the setup and update process as easy as possible. The end goal is to be able to run 1 batch file that installs Chocolatey, FFmpeg, git, clones the github repo (for updates), and edits the Windows registry to add the actual FFmpeg powershell scripts / console programs to the Windows Explorer contextual menu. This way I just pass them the folder containing everything once and any time I change or add something to the project I can just tell them to run the batch file again, and presto everything is up to date.

However I'm struggling to find a way to install Chocolatey, then git with Chocolatey, and then run a git command with the execution of a single .bat file. From what I can tell after installing Chocolatey I need to restart the shell entirely before I can install git, and then I have to restart the shell again before I can use a git command. As of right now most of the actual processing is happening via Powershell scripts that are launched from the .bat file, and as each step is taken I update a txt file, attempt to restart the batch script, and read the txt file to pick up where I left off:

@echo off
echo Administrative permissions required. Detecting permissions...
echo.

net session >nul 2>&1
if %errorLevel% == 0 (
    echo Success: Administrative permissions confirmed.
    echo.
) else (
    echo Failure: Current permissions inadequate.

    PAUSE

    exit
)

set relativePath=%~dp0
set relativePath=%relativePath:~0,-1%

PowerShell -NoProfile -ExecutionPolicy Bypass -File "%relativePath%\Setup\CheckRequiredPackages.ps1" -relativePath "%relativePath%"

set /p step=<"%relativePath%\Setup\Step.txt"

if %step% == 1 (
    (echo 2) > "%relativePath%\Setup\Step.txt"

    PowerShell -NoProfile -ExecutionPolicy Bypass -File "%relativePath%\Setup\GetChocolatey.ps1"

    start "" "%relativePath%\RunMe.bat"

    exit
) 

if %step% == 2 (
    (echo 3) > "%relativePath%\Setup\Step.txt"

    PowerShell -NoProfile -ExecutionPolicy Bypass -File "%relativePath%\Setup\GetRequiredPackages.ps1"

    start "" "%relativePath%\RunMe.bat"

    exit
) 

if %step% == 3 (
    (echo 0) > "%relativePath%\Setup\Step.txt"

    PowerShell -NoProfile -ExecutionPolicy Bypass -File "%relativePath%\Setup\Update.ps1" -relativePath "%relativePath%"
) 

PAUSE
Exit

The problem is using the start command in the batch script doesn't seem to work, I'm guessing since that new process is spawned from the same process that handles the Chocolatey install it doesn't count as actually restarting the shell. Is there any way to actually restart the shell and somehow have the batch file start back up without user intervention?

Upvotes: 2

Views: 1329

Answers (2)

mklement0
mklement0

Reputation: 439822

Indeed, a start-launched process inherits the calling process' environment rather than reading possibly updated environment-variable definitions from the registry.

Chocolatey comes with batch file RefreshEnv.cmd (C:\ProgramData\chocolatey\bin\RefreshEnv.cmd, but C:\ProgramData\chocolatey\bin should be in the %PATH%) specifically to avoid having to start a new, independent session for environment updates to take effect.

Therefore, something like the following may work:

:: Assumes that Chocolatey was just installed to the default location.
call "%ProgramData%\chocolatey\bin\RefreshEnv.cmd"

:: If Chocolatey was *previously* installed and its installation directory
:: has already been added to %Path%, the following will do:
::   call RefreshEnv.cmd

call "%relativePath%\RunMe.bat" 

Since Chocolatey is only being installed during your script's execution and its binaries folder is therefore not yet in %Path%, you'll have to call RefreshEnv.cmd by its full path, as shown above - which assumes the default install directory.


Your own answer now shows how to refresh the $env:Path (%Path%) environment variable using .NET methods directly from PowerShell, which is a pragmatic solution.

Note, however, that RefreshEnv.cmd is more comprehensive in that it reloads all environment-variable definitions and therefore potentially newly added and modified ones.


Note that calling RefreshEnv.cmd from PowerShell does not work, because it then runs out of process (which means that it cannot update the calling process' environment).

However, Chocolatey offers an Update-SessionEnvironment PowerShell command (aliased to refreshenv), which you can make available immediately after a Chocolatey install as follows:

# Import the module that defines Update-SessionEnvironment aka refreshenv
Import-Module "$env:ProgramData\Chocolatey\helpers\chocolateyProfile.psm1"

# Refresh all environment variables.
Update-SessionEnvironment  # or: refreshenv

See this answer for a more robust approach that doesn't rely on assuming that the default location was installed to.


Upvotes: 3

ninbura
ninbura

Reputation: 468

I'm not sure why I didn't initially think of reloading the path environment variable but that's a whole lot more reasonable than restarting the script 4 times with an intermediary file.

Firstly I moved 99% of the heavy lifting from the .bat file to a Powershell script, as the only reason I'm using Batch is so the user can easily run the file by clicking it in Explorer. I couldn't get RefreshEnv to work, which is a feature of Chocolatey, but running this between each new package worked great:

$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")

So I have something like this now, and the Batch scrip just launches this Powershell Script:

Write-Host "Installing / updating required packages..."

Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = 
[System.Net.ServicePointManager]::SecurityProtocol -bor 3072; Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))

$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")

choco install ffmpeg -y
choco install git -y

$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") 

Write-Host "Deleting old files..."

Remove-Item -LiteralPath $relativePath -Force -Recurse
Start-Sleep 2

Write-Host "`nUpdating Files..."
git clone https://github.com/TheNimble1/FFmpegContextCommands.git $relativePath

Which installs Chocolatey, refreshes the path, installs FFmpeg & Git, refreshes the path, deletes the old files, and then clones the git to replace with new files.

Upvotes: 1

Related Questions