'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:
- If the leading path is enclosed in quotes, then the first output starts with ""
- If the trailing path is enclosed in quotes, then the last output ends with ""
- If any path contains harmless but non-functional consecutive "", then the output preserves the ""
- 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'))"
精彩评论