Pushing and Popping the PATH - like PUSHD/POPD.

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
thefeduke
Posts: 211
Joined: 05 Apr 2015 13:06
Location: MA South Shore, USA

Pushing and Popping the PATH - like PUSHD/POPD.

#1 Post by thefeduke » 09 Aug 2017 23:24

All of my previous attempts over the last couple of years to manipulate a flexible testing environment seem to have failed because the complexity of my solutions made them unmanageable. Managing the search order did not seem to be worth it.

PUSHP is to the PATH as PUSHD is to the Current Directory. POPP works like POPD. Once popped, it's gone. Keep Ii Simple.

Edit: marketing paragraph: PUSHD fails if you enter PUSHD garbage when garbage is not valid. If you enter PATH garbage, then that is what you get and you can't find anything that used to be on the PATH. A simple typing error on PATH MyFolder;%PATH%; appears to work but in effect might as well have been ;;%PATH%; and not what was expected. PUSHP validates any folder before adding it to the PATH and even saves the existing path, anyway.

So here they are. Most of the code is for documentation. Edit: POPP has been streamlined (including fixes ):

Code: Select all

@Echo Off
:PopP

Rem first run of POPP.bat will save the current PATH as baseline level 1.
    If NOT DEFINED pop0 (Set "pop0=1"
        set "pop1=%PATH%;"
    )
    set "pop1=%pop1:;;=;%"

Rem get saved path for current POPP level, inform and prepare level housekeeping
    Set "popName=pop%pop0%"
    Set "popLast=%pop0%"
    Call Set "popValue=%%%%%%popName%%%%%%"
    If "%pop0%" GTR "1" (
        Set "popDel=%popName%"
        Set /A "pop0-=1"
        Call Echo.Popping PATH from level %%popLast%% to %%pop0%%.
    ) Else (
        Set "popDel=popDel"
        Call Echo.Refreshing PATH to baseline level %%pop0%%.
    )

    Call PATH %popValue%

Rem purge POPPed variable and prevent leaked variables
    Set "%popDel%="
    Set "popDel="
    Set "popValue="
    Set "popName="
    Set "popLast="

Rem display HELP if reuqested
    If /I "%~1." NEQ "." (
::      Create temporary help file
        Call :TempFile "Help.txt"
        Type "%Temp%\~Scripts~\%~n0_Help.txt"
    )

    Exit /B


:TempFile NameExtValue[In]
    @echo Off & SetLocal EnableDelayedExpansion
    For %%E In ("%~1") DO (
        Set "fName=%%~nxE"
        Set "fExt=%%~xE"
    )
    If Not Exist "%Temp%\~Scripts~"         MkDir "%Temp%\~Scripts~"
    If Exist "%Temp%\~Scripts~\%~n0_%fName%" DEL "%Temp%\~Scripts~\%~n0_%fName%"

    For /f "delims=:" %%i in (
        'findstr /nir /c:"^goto[ ]*\:EndOf%fName%" /c:"^\:EndOf%fName%" "%~fs0"'
    ) Do Set "DataRange=!DataRange! %%i"
    For /f "tokens=1,2" %%i in ("%DataRange%") Do (Set /A "BeginData=%%i+1" & Set /A "EndData=%%j-1")
   (For /L %%i In (2 1 %BeginData%) Do Set /P "="
        For /L %%i In (!BeginData! 1 %EndData%) Do (
            Set "line=" &Set /P "line="
            Echo(!line!
        )
   ) < "%~f0"   >"%Temp%\~Scripts~\%~n0_%fName%"
    EndLocal
    Exit /B


GoTo :EndOfHelp.txtFile


POPP ["flag" | /?]

    This script restores the PATH to the PATH last saved by either PUSHP or POPP.


    "flag" is used to specify a request for help.

        It may have any value, but whether specified or not
        the POPP function is executed.

    USAGE Notes:

        If there is no saved PATH because neither PUSHP nor POPP has run, the
        first run of POPP will save the current PATH as baseline level 1.
        This stays available for the next and any subsequent POPP commands.

        POPP can be used to restore the PATH after a successful PUSHP
        or POPP even if the active PATH becomes corrupted by other means.

        Saved POPP levels are easily examined using the command: SET pop
:EndOfHelp.txtFile
Here is the code for PUSHP. An easy test example is PUSHP temp

Code: Select all

@Echo Off&SetLOCAL EnableDelayedExpansion &Rem.and keep environment uncluttered.

    Set "VarName=%~1"
    If NOT DEFINED VarName (
::      Create temporary help file
        Call :TempFile "Help.txt"
        Type "%Temp%\~Scripts~\%~n0_Help.txt"
        Exit /B
    ) Else (
        Call Set "VarValue=%%!VarName!%%"
rem     Echo !VarName!=!VarValue!
        If NOT DEFINED VarValue (
rem         Echo.Working with !VarName!
            Set "input=!VarName!"
        ) Else (
rem         Echo.Working with !VarValue!
            Set "input=!VarValue!"
        )
    )

    For /F "usebackq tokens=*" %%A In  ('"%input%"') Do (
        If Exist "%%A\" (
            Set "Arg1=%%~fA"
        ) Else (
            If Exist "%%A" (
Rem.            Save the containing folder whan argument is a file
                Set "Arg1=%%~dpA"
                Set "Arg1=!Arg1:~,-1!"
            ) Else (
                Set "Arg1="
            )
        )
    )

    If NOT DEFINED Arg1 (
        Echo.The file defined by '%input%' could not be found.
::      Create temporary help file
        Call :TempFile "Help.txt"
        Type "%Temp%\~Scripts~\%~n0_Help.txt"
        Exit /B
    ) Else (Endlocal&Set "push=%Arg1%")

    If NOT DEFINED pop0 (Set "pop0=1"
        set "pop1=%PATH%;"
    )
    set "pop1=%pop1:;;=;%"

    Set /A "pop0+=1"
    Echo.Pushing PATH to level %pop0%.
    Set "pop%pop0%=%PATH%"
    Set "VarName=%~2"
    If NOT DEFINED VarName GOTO :NoVar
    PATH %PATH%;%Push%;
    Echo.'%push%' appended to PATH.
    GOTO :Var
:NoVar
    PATH %push%;%PATH%;
    Echo.'%push%' now precedes PATH.
:Var
    Set "PATH=%PATH:;;=;%"

    Exit /B

:TempFile NameExtValue[In]
    @echo Off & SetLocal EnableDelayedExpansion
    For %%E In ("%~1") DO (
        Set "fName=%%~nxE"
        Set "fExt=%%~xE"
    )
    If Not Exist "%Temp%\~Scripts~"         MkDir "%Temp%\~Scripts~"
    If Exist "%Temp%\~Scripts~\%~n0_%fName%" DEL "%Temp%\~Scripts~\%~n0_%fName%"

    For /f "delims=:" %%i in (
        'findstr /nir /c:"^goto[ ]*\:EndOf%fName%" /c:"^\:EndOf%fName%" "%~fs0"'
    ) Do Set "DataRange=!DataRange! %%i"
    For /f "tokens=1,2" %%i in ("%DataRange%") Do (Set /A "BeginData=%%i+1" & Set /A "EndData=%%j-1")
   (For /L %%i In (2 1 %BeginData%) Do Set /P "="
        For /L %%i In (!BeginData! 1 %EndData%) Do (
            Set "line=" &Set /P "line="
            Echo(!line!
        )
   ) < "%~f0"   >"%Temp%\~Scripts~\%~n0_%fName%"
   EndLocal
Exit /B


GoTo :EndOfHelp.txtFile


PUSHP ["string"[ "flag"]]


    This script adds a folder to the current PATH and saves previous PATH
    information so that it can be restored using the POPP command.


    "string" is an argument used to specify the folder to be added:

        It may be a variable name or a value that can refer to a relative or
        absolute file or folder.  The containing folder is used in the
        case of a file.
        If neither is found this help is displayed. (PUSHP /? also works.)
        Absence of a first argument is treated as a request for help.

    "flag" is used to specify that folder is to be added after the Path:

        It may have any value, but if none is specified
        the folder will be added before the PATH.

    USAGE Notes:

        POPP can be used to restore the PATH after a successful PUSHP
        or POPP even if the active PATH becomes corrupted by other means.

        Saved POPP levels are easily examined using the command: SET pop
:EndOfHelp.txtFile

As my own devil's advocate, why store a backup path when you can get it by just opening a new prompt window?

My question is whether there is a disadvantage in using

Code: Select all

Set "PATH=Routines;%PATH%;"
rather than

Code: Select all

PATH Routines;%PATH%;
I found that the PATH command is somewhat unique in that it did not work for me in some constructs, like combined with ENDLOCAL, where a SET command would work. Edit: answered below.

John A.
Last edited by thefeduke on 11 Aug 2017 15:10, edited 1 time in total.

aGerman
Expert
Posts: 4678
Joined: 22 Jan 2010 18:01
Location: Germany

Re: Pushing and Popping the PATH - like PUSHD/POPD.

#2 Post by aGerman » 10 Aug 2017 12:11

thefeduke wrote:My question is whether there is a disadvantage in using

Code: Select all

Set "PATH=Routines;%PATH%;"
rather than

Code: Select all

PATH Routines;%PATH%;
It doesn't seem so. Out of my experiences the PATH command does nothing else than setting the %PATH% variable for the current process environment to a new value.

thefeduke wrote:I found that the PATH command is somewhat unique in that it did not work for me in some constructs, like combined with ENDLOCAL, where a SET command would work.
I can't confirm that. Changes of %PATH% (regardless if set by PATH or SET) are out of scope using ENDLOCAL as soon as you left the sub-environment where it was changed.

Steffen

thefeduke
Posts: 211
Joined: 05 Apr 2015 13:06
Location: MA South Shore, USA

Re: Pushing and Popping the PATH - like PUSHD/POPD.

#3 Post by thefeduke » 11 Aug 2017 15:33

aGerman wrote:I can't confirm that. Changes of %PATH% (regardless if set by PATH or SET) are out of scope using ENDLOCAL as soon as you left the sub-environment where it was changed.

Steffen
Thanks for answering. I now know that most of my problems came from not forcing substitution of variables when using the PATH command. I corrected this by using 'CALL PATH' instead of just 'PATH' and have altered POPP in the first post to avoid the sub-environment completely. A small correction also makes POPP pop as designed.

PUSHP/POPP eliminates my annoyance with the PATH command, that always works without complaint no matter how corrupt the operands.

Note that my first post has been edited.

John A.

Post Reply