开发者

'Pretty print' windows %PATH% variable - how to split on ';' in CMD shell

I want to run a simple one-liner in the Windows CMD prompt to print my %PATH% variable, one entry per line.

I tried this: for /f "delims=;" %a in ("%path%") do echo %a but this only prints the first entry:

Z:\>for /f "delims=;" %a in ("%path%") do echo %a

Z:\>echo c:\python25\.
c:\python25\.

Also as you can see from the output above, this is also printing the echo %a command as well as the output. Is there any way to stop this?

If I try a similar command, I get all the entries, but still get the echo %a output spamming the results. I don't understand 开发者_运维问答why the following prints all entries, but my attempt on %PATH% doesn't. I suspect I don't understand the /F switch.

Z:\>for %a in (1 2 3) do echo %a

Z:\>echo 1
1

Z:\>echo 2
2

Z:\>echo 3
3


The simple way is to use

for %a in ("%path:;=";"%") do @echo %~a

This works for all without ; in the path and without " around a single element
Tested with path=C:\qt\4.6.3\bin;C:\Program Files;C:\documents & Settings

But a "always" solution is a bit complicated
EDIT: Now a working variant

@echo off
setlocal DisableDelayedExpansion
set "var=foo & bar;baz<>gak;"semi;colons;^&embedded";foo again!;throw (in) some (parentheses);"unmatched ;-)";(too"

set "var=%var:"=""%"
set "var=%var:^=^^%"
set "var=%var:&=^&%"
set "var=%var:|=^|%"
set "var=%var:<=^<%"
set "var=%var:>=^>%"

set "var=%var:;=^;^;%"
rem ** This is the key line, the missing quote is intended
set var=%var:""="%
set "var=%var:"=""%"

set "var=%var:;;="";""%"
set "var=%var:^;^;=;%"
set "var=%var:""="%"
set "var=%var:"=""%"
set "var=%var:"";""=";"%"
set "var=%var:"""="%"

setlocal EnableDelayedExpansion
for %%a in ("!var!") do (
    endlocal
    echo %%~a
    setlocal EnableDelayedExpansion
)

What did I do there?
I tried to solve the main problem: that the semicolons inside of quotes should be ignored, and only the normal semicolons should be replaced with ";"

I used the batch interpreter itself to solve this for me.

  • First I have to make the string safe, escaping all special characters.
  • Then all ; are replaced with ^;^;
  • and then the trick begins with the line
    set var=%var:"=""%" (The missing quote is the key!).
    This expands in a way such that all escaped characters will lose their escape caret:
    var=foo & bar;;baz<>gak;;"semi^;^;colons^;^;^&embedded";;foo again!;;...
    But only outside of the quotes, so now there is a difference between semicolons outside of quotes ;; and inside ^;^;.
    Thats the key.


A simple one liner to prettying printing the PATH environment variable:

ECHO.%PATH:;= & ECHO.%

If your PATH was equal to A;B;C the above string substitution will change this to ECHO.A & ECHO.B & ECHO.C and execute it all in one go. The full stop prevents the "ECHO is on" messages from appearing.


An update to Stephan Quan's very clever one-liner solution: The problem I encountered was that a trailing semi-colon - (and maybe two successive semi-colons, i.e. empty path element) would cause the message "ECHO is on" to appear. I solved this by inserting a period immediately after the second ECHO statement (which is the syntax to suppress ECHO is on/off messages). However, it will result in an extra empty line:

ECHO %PATH:;= & ECHO.%


I have minor improvements to jeb's clever "always" solution. Currently jeb's solution has the following issues:

  1. If the leading path is enclosed in quotes, then the first output starts with ""
  2. If the trailing path is enclosed in quotes, then the last output ends with ""
  3. If any path contains harmless but non-functional consecutive "", then the output preserves the ""
  4. If var contains consecutive ;; delimiters then outputs ECHO is off

This solution fixes the minor issues, plus it uses 2 fewer substitutions. Also I eliminated the unnecessary repeated enabling/disabling delayed expansion within the loop. (Edit on 2011-10-30 simplified the ENDLOCAL logic)

@echo off
setlocal DisableDelayedExpansion
set "var=%var:"=""%"
set "var=%var:^=^^%"
set "var=%var:&=^&%"
set "var=%var:|=^|%"
set "var=%var:<=^<%"
set "var=%var:>=^>%"
set "var=%var:;=^;^;%"
set var=%var:""="%
set "var=%var:"=""Q%"
set "var=%var:;;="S"S%"
set "var=%var:^;^;=;%"
set "var=%var:""="%"
setlocal EnableDelayedExpansion
set "var=!var:"Q=!"
for %%a in ("!var:"S"S=";"!") do (
  if "!!"=="" endlocal
  if %%a neq "" echo %%~a
)

If you want to see a blank line for each empty path resulting from consecutive ;; delimiters, then the last line of the FOR loop can simply read echo(%%~a instead.

Or perhaps it would be more obvious to display empty paths as "" using:
if %%a=="" (echo "") else echo %%~a

The various empty path fixes work for jeb's simple solution as well.


UPDATE: Here is a simple one-liner using JREPL.BAT

You can use my JREPL.BAT regular expression text processing utility to achieve a simple, very robust solution. JREPL.BAT is pure script (hybrid JScript/batch) that runs natively on any Windows machine from XP onward.

jrepl "([^;\q]+|\q.*?(\q|$))+" $0 /x /jmatch /s path


This works in cmd window using Git Bash on Windows:

echo -e ${PATH//:/\\n}

You can also make a handy alias in your .bash_profile:

alias showpath='echo -e ${PATH//:/\\n}'


@ROMANIA_engineer proposed a PowerShell solution in a comment. Since the question asks for a command that works in the CMD shell, here is a way to use that elegant code from the OP's desired environment:

powershell -Command ($env:Path).split(';')

To make it still more readable, you can add sorting:

powershell -Command ($env:Path).split(';') | sort

Credit: https://stackoverflow.com/a/34920014/704808


Stephen Quan's answer is shorter and better, but here's a Python solution:

python -c "import os; print(os.environ['PATH'].replace(';', '\n'));"

Turning ; semicolons into \n newlines.


I know this is old, but FWIW; I always run into wanting this for some reason or another. Some time ago, I wrote myself a script to do this. I put a little polish on it and posted it on my blog.

Feel free to use it.

It's called epath, and the file is at inzi.com. It's compiled as an EXE for easy use (using vbsedit): here

You can download the exe there. Here's the source code to the script if you want it as a vbs script.

    scriptname = Wscript.ScriptName 'objFSO.GetFileName(WScript.FullName)

    Function BubbleSort(arrData,strSort)
    'borrowed from here: http://vbscripter.blogspot.com/2008/03/q-how-do-i-sort-data-in-array.html

    'Input: arrData = Array of data.  Text or numbers.
    'Input: strSort = Sort direction (ASC or ascending or DESC for descending)
    'Output: Array
    'Notes: Text comparison is CASE SENSITIVE
    '        strSort is checked for a match to ASC or DESC or else it defaults to Asc


        strSort = Trim(UCase(strSort))
        If Not strSort = "ASC" And Not strSort = "DESC" Then
            strSort = "ASC"
        End If 

        For i = LBound(arrData) to UBound(arrData)
          For j = LBound(arrData) to UBound(arrData)
            If j <> UBound(arrData) Then
                If strSort = "ASC" Then
                  If UCase(arrData(j)) > UCase(arrData(j + 1)) Then
                     TempValue = arrData(j + 1)
                     arrData(j + 1) = arrData(j)
                     arrData(j) = TempValue
                  End If
                End If

                If strSort = "DESC" Then
                    If UCase(arrData(j)) < UCase(arrData(j + 1)) Then
                        TempValue = arrData(j + 1)
                        arrData(j + 1) = arrData(j)
                        arrData(j) = TempValue
                     End If        
                End If 
            End If
          Next
        Next

        BubbleSort = arrData

    End Function

    If Wscript.Arguments.Count>0 Then

        Set args = Wscript.Arguments

        bInLines = False
        bInAlphabetical = False
        bReverseSort = False
        bShowHelp = False

        For Each arg In args
            Select Case arg
                Case "-l"
                    bInLines = True
                Case "-a"
                    bInAlphabetical = True
                Case "-r"
                    bReverseSort = True
                Case Else
                    bShowHelp=True
            End Select  

        Next

        If bInLines = False Then
            bShowHelp=True
        End if

        If bShowHelp Then

                    sTxt = sTxt + "" & vbCrLf
                    sTxt = sTxt +  scriptname  & " Displays the system path in optionally friendly formats." & vbCrLf
                    sTxt = sTxt +  "ePath is helpful when viewing the system path and easily identifying folders therein." & vbCrLf
                    sTxt = sTxt + "" & vbCrLf
                    sTxt = sTxt + "EPATH [-l] [-a] [-r]" & vbCrLf
                    sTxt = sTxt + "" & vbCrLf
                    sTxt = sTxt + "Switches:" & vbCrLf
                    sTxt = sTxt + vbTab + "[-l]" + vbtab + "Show the path broken out in lines" & vbCrLf
                    sTxt = sTxt + vbtab + "[-a]" + vbTab + "Sort the path broken out in lines sorted alphabetically" & vbCrLf
                    sTxt = sTxt + vbtab + "[-r]" + vbTab + "Reverse the alphabetic sort [asc default] (ignored without -a)" & vbCrLf
                    sTxt = sTxt + "" & vbCrLf
                    sTxt = sTxt + vbTab + "Examples:" & vbCrLf
                    sTxt = sTxt +  vbTab + vbTab + scriptname  & vbTab & "(Show %PATH% normally)" & vbCrLf
                    sTxt = sTxt +  vbTab + vbTab + scriptname  & " -l" & vbCrLf
                    sTxt = sTxt +  vbTab + vbTab + scriptname  & " -l -a" & vbCrLf
                    sTxt = sTxt +  vbTab + vbTab + scriptname  & " -l -a -r" & vbCrLf
                    sTxt = sTxt +  vbTab + vbTab + scriptname  & " -? Display help (what you are seeing now)" & vbCrLf
                    sTxt = sTxt + "" & vbCrLf
                    sTxt = sTxt + "More info or questions at http://inzi.com" & vbCrLf


                    Wscript.Echo sTxt

                    WScript.Quit

        Else
            Set wshShell = CreateObject( "WScript.Shell" )
            sPath = wshShell.ExpandEnvironmentStrings( "%PATH%" )
            thePath = Split(sPath,";")

            If bInAlphabetical Then
                If bReverseSort Then
                    sDirection = "DESC"
                End If

                thePath = BubbleSort(thePath, sDirection)
            End if


            For Each item In thePath
                WScript.Echo item
            Next
            Set wshShell = Nothing
        End if
    Else
        'Nothing, echo the path.

        Set wshShell = CreateObject( "WScript.Shell" )
        WScript.Echo wshShell.ExpandEnvironmentStrings( "%PATH%" )
        Set wshShell = Nothing

    End If


Original Version

This is an approach that only performs several character substitutions, which can properly handle a semicolon-separated list of optionally quoted items, which, when quoted, may even contain semicolons on their own. Empty list items are also dealt with correctly. The key to success is to become able to distinguish between semicolons within quotes (which are part of a list item) and those not (which constitute the separators):

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define semicolon-separated list here (sample taken from https://stackoverflow.com/a/5472168):
set "BUFFER=foo & bar;baz<>gak;"semi;colons;^&embedded";foo again!;throw (in) some (parentheses);"unmatched ;-)";(too"
rem set "BUFFER="caret ^^";"bang !";"caret ^^ bang !""

set BUFFER & echo/

setlocal EnableDelayedExpansion
rem // Replace `^` by `^@` to avoid sequences of `^`:
set "BUFFER=!BUFFER:^=^@!"

rem // Escape special characters `^`, `&`, `<`, `>`, `|`:
set "BUFFER=!BUFFER:^=^^!"
set "BUFFER=!BUFFER:&=^&!"
set "BUFFER=!BUFFER:<=^<!"
set "BUFFER=!BUFFER:>=^>!"
set "BUFFER=!BUFFER:|=^|!"

rem // Escape `;`:
set "BUFFER=!BUFFER:;=^;!"

rem // Expand immediately, so escaping is processed for unquoted portions:
endlocal & set ^"BUFFER=%BUFFER%^"
setlocal EnableDelayedExpansion

rem // Escape `^`, `&`, `<`, `>`, `|`, hence quoted portions become double-escaped:
set "BUFFER=!BUFFER:^=^^!"
set "BUFFER=!BUFFER:&=^&!"
set "BUFFER=!BUFFER:<=^<!"
set "BUFFER=!BUFFER:>=^>!"
set "BUFFER=!BUFFER:|=^|!"

rem /* Replace `^^^` by `^` in order to resolve double-escaping;
rem    at this point, quoted `;` are represented by the sequence `^^;`: */
set "BUFFER=!BUFFER:^^^=^!"

rem // Escape `"`, so everything appears unquoted then:
set "BUFFER=!BUFFER:"=^^"!"

rem /* Expand immediately, so escaping is again processed;
rem    at this point, originally quoted `;` are represented by `^;`: */
set "BUFFER=!BUFFER:^=^^^!"
set ^"BUFFER=%BUFFER:!=^^!%^" !

rem // Remove all `"` from string:
set "BUFFER=!BUFFER:"=!^"

rem // Enclose whole string in `""` and replace each `;` by `";"`:
set ^"BUFFER="!BUFFER:;=";"!"^"

rem // Replace `^";"` (which are originally quoted `;`) by `;`:
set "BUFFER=!BUFFER:^";"=;!"

rem // Finally revert initial replacement of `^` by `^@`:
set "BUFFER=!BUFFER:^@=^!"

set BUFFER & echo/

rem // Eventually return the list items:
for %%I in (!BUFFER!) do (
    if "!!"=="" endlocal & rem // (technique taken from https://stackoverflow.com/a/7940444)
    echo(%%~I
)

endlocal
exit /B

Improved Version

Here is an improved approach that performs less substitutions:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define semicolon-separated list here:
set "BUFFER="caret ^^";"bang !";"caret ^^; ^& bang !";caret ^;bang !;caret ^@ & bang !"

set BUFFER & echo/

setlocal EnableDelayedExpansion
rem // Replace `^` by `^@` to avoid sequences of `^`:
set "BUFFER=!BUFFER:^=^@!"

rem // Double-escape special characters `^`, `&`, `<`, `>`, `|`:
set "BUFFER=!BUFFER:^=^^^^!"
set "BUFFER=!BUFFER:&=^^^&!"
set "BUFFER=!BUFFER:<=^^^<!"
set "BUFFER=!BUFFER:>=^^^>!"
set "BUFFER=!BUFFER:|=^^^|!"

rem // Replace `;` by `^^;`:
set "BUFFER=!BUFFER:;=^^;!"

rem // Expand immediately, so escaping is processed for unquoted portions:
endlocal & set ^"BUFFER=%BUFFER%^"
setlocal EnableDelayedExpansion

rem /* Replace `^^^` by `^` in order to resolve double-escaping; at this point,
rem    quoted `;` are represented by the sequence `^^;`, unquoted ones by `^;`: */
set "BUFFER=!BUFFER:^^^=^!"

rem // Escape `"`, so everything appears unquoted then:
set "BUFFER=!BUFFER:"=^^"!"

rem /* Expand immediately, so escaping is again processed;
rem    at this point, originally quoted `;` are represented by `^;`: */
set "BUFFER=!BUFFER:^=^^^!"
set ^"BUFFER=%BUFFER:!=^^!%^" !

rem // Remove all `"` from string:
set "BUFFER=!BUFFER:"=!^"

rem // Enclose whole string in `""` and replace each `;` by `";"`:
set ^"BUFFER="!BUFFER:;=";"!"^"

rem // Replace `^";"` (which are originally quoted `;`) by `;`:
set "BUFFER=!BUFFER:^";"=;!"

rem // Finally revert initial replacement of `^` by `^@`:
set "BUFFER=!BUFFER:^@=^!"

set BUFFER & echo/

rem // Eventually return the list items:
for %%I in (!BUFFER!) do (if "!!"=="" endlocal) & echo(%%~I

endlocal
exit /B


Here is a well commented script that parses a semicolon-separated list of items, regarding optional quotation of each item and allowing even items with semicolons in them (when quoted). Since it utilises a goto loop it might not be the fastest approach though, but it should be safe against special characters:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define semicolon-separated list here (sample taken from https://stackoverflow.com/a/5472168):
set "BUFFER=foo & bar;baz<>gak;"semi;colons;^&embedded";foo again!;throw (in) some (parentheses);"unmatched ;-)";(too"

setlocal EnableDelayedExpansion
echo BUFFER: !BUFFER!
echo/
endlocal

rem // Initialise items counter and clear (pseudo-)array variable:
set /A "IDX=0" & for /F "delims==" %%V in ('set $ITEM[ 2^> nul') do set "%%V="
rem // Utilise a `goto` loop to retrieve all items:
:PARSE_LOOP
rem // Check for availability of further items:
if defined BUFFER (
    rem // Increment items counter:
    set /A "IDX+=1"
    rem // Toggle delayed expansion to avoid troubles with `!`:
    setlocal EnableDelayedExpansion
    rem // Put current items counter into a `for` meta-variable:
    for %%I in (!IDX!) do (
        rem // Check whether current item begins with `"`, hence it is quoted:
        if "!BUFFER:~,1!" == ""^" (
            rem /* This point is reached when current item is quoted, hence
            rem    remove the opening `"` and split off everything past the
            rem    next one (that is the closing `"`) from the items buffer: */
            for /F tokens^=1*^ delims^=^"^ eol^=^" %%A in (";!BUFFER:~1!") do (
                endlocal
                rem // Store current item into array in unquoted manner:
                set "$ITEM[%%I]=%%A" & setlocal EnableDelayedExpansion
                for /F "delims=" %%C in ("$ITEM[%%I]=!$ITEM[%%I]:~1!") do (
                    endlocal & set "%%C" & if not defined $ITEM[%%I] set /A "IDX-=1"
                )
                rem /* Transfer remaining items beyond `endlocal` barrier;
                rem    note that a delimiters-only line still causes `for /F`
                rem    to iterate when there is just `tokens=*`: */
                for /F "tokens=* delims=;" %%D in (";%%B") do set "BUFFER=%%D"
                setlocal EnableDelayedExpansion
            )
        ) else (
            rem /* This point is reached when current item is not quoted,
            rem    hence split items string at next semicolon `;`: */
            for /F "tokens=1* delims=;" %%A in ("!BUFFER!") do (
                endlocal
                rem // Store current (unquoted) item into array:
                set "$ITEM[%%I]=%%A"
                rem // Transfer remaining items beyond `endlocal` barrier:
                set "BUFFER=%%B"
                setlocal EnableDelayedExpansion
            )
        )
    )
    endlocal
    rem // Loop back to retrieve next item:
    goto :PARSE_LOOP
)
rem // Return all retrieved items:
setlocal EnableDelayedExpansion
for /L %%I in (1,1,%IDX%) do echo ITEM %%I: !$ITEM[%%I]!
endlocal

echo/
echo INFO:   %IDX% items
echo ERROR:  %ErrorLevel%

endlocal
exit /B


This script will parse the current windows path into at txt file using JREPL.bat. It was inspired by the above post by dbenham.

@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION

:::: THIS SCRIPT WILL PARSE THE CURRENT WINDOWS PATH INTO AT TXT FILE ::::
:::: EACH FOLDER FOUND IN PATH WILL BE ON A SEPARATE LINE ::::

:::: SCRIPT INSTRUCTIONS ::::
:: PLACE JREPL.bat IN C:\WINDOWS\SYSTEM32 FOLDER OR CUSTOM LOCATION OF YOUR CHOOSING ::
:: IF PLACED IN CUSTOM FOLDER YOU MUST LINK IT TO WINDOWS PATH ::
:: YOU CAN ACCESS WINDOWS PATH BY RUNNING THE BELOW COMMAND IN CMD.EXE ::
:: Rundll32 sysdm.cpl,EditEnvironmentVariables ::
:: DOWNLOAD JREPL.bat https://www.dostips.com/forum/viewtopic.php?t=6044 ::

:: SET WORKING DIRECTORY ::
CD /D "C:\WINDOWS\SYSTEM32"

:: UNCOMMENT LINE BELOW AND SET YOUR JREPL.bat SAVED FOLDER PATH IF YOU HAVE A BACKUP COPY ::
:: SET JOUT=<FOLDER PATH>

:: SET OUTPUT FILE ::
    SET FOUT=%USERPROFILE%\DESKTOP\PARSE.TXT

:: SET FILE TO SEARCH FOR ::
:: THIS SEARCHES FOR JREPL.BAT IN THE CURRENT WORKING DIR ::
    SET I=JREPL.BAT

:: SET CONTROL FILE TO CHECK AGAINST ::
:: THIS IS FOR DEBUGGING PURPOSES AND SHOULD NOT BE CHANGED OTHERWISE ::
    SET J=JREPL.BAT

:::: START SCRIPT ::::
    SET RESULT=
    FOR /F "DELIMS=" %%A IN ('DIR /B /O:N ^| FINDSTR /S /I "%I%" %TMP_RESULT_FILE%') DO (
    SET RESULT=%%A
    )

IF /I !RESULT! EQU %J% (
ECHO !RESULT! ^^!^^!EXISTS^^!^^!
    TIMEOUT 3 >NUL
    CALL :FOUND
    GOTO END
) ELSE (
    GOTO NOTFOUND
)
    GOTO ERROR

:FOUND
    FOR %%G IN (%I%) DO (
    %%G "([^;\Q]+|\Q.*?(\Q|$))+" $0 /X /JMATCH /S PATH>>%FOUT%
    CLS && ECHO.
ECHO %I% ^^!^^!EXECUTED SUCCESSFULLY^^!^^!
    TIMEOUT /T 3 >NUL
    EXPLORER "%FOUT%"
    GOTO END
( ELSE )
    GOTO ERROR
)

:NOTFOUND
    ECHO %I% ^^!^^!NOT FOUND^^!^^!
    TIMEOUT 3 >NUL
    CLS && ECHO.
:: UNCOMMENT THE LINES BELOW TO OPEN JREPL.BAT SAVE FOLDER IF AVAILABLE ::
    :: ECHO ^^!^^!OPENING^^!^^! %I%'S SAVED FOLDER
  :: TIMEOUT 3 >NUL
    :: EXPLORER "%JOUT%"
  GOTO END
    ( ELSE )
    GOTO ERROR
    )

:ERROR
    CLS && ECHO.
    ECHO ^^!^^!ERROR RUNNING^^!^^! %I%
    TIMEOUT 3 >NUL
    CLS && ECHO.
    ECHO ^^!^^!PLEASE FIX SCRIPT^^!^^! ::::::::::::::::::
    TIMEOUT 3 >NUL

:END
    ENDLOCAL && EXIT /B


Returning to this, in the case you have node.js and inspired by Bob Stein answer for python one-liner.

node -e "console.log(process.env.path.replace(/;/g,'\n'))"

Or longer way:

node -e "console.log(process.env.path.split(';').join('\n'))"

I needed to find lines where git is installed so:

node -e "console.log(process.env.path.split(';').filter(str=>str.match(/git/i)).join('\n'))"
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜