在CMD shell中“漂亮打印”Windows %PATH%变量 - 如何按';'拆分

62
我想在Windows CMD提示符中运行一个简单的一行代码,以每行一个条目的方式打印我的%PATH%变量。
我尝试过这个代码:for /f "delims=;" %a in ("%path%") do echo %a,但它只打印第一个条目。
Z:\>for /f "delims=;" %a in ("%path%") do echo %a

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

正如您从上面的输出中所看到的,它也打印了echo %a命令以及输出。有没有办法停止这个?

如果我尝试类似的命令,我可以得到所有的条目,但仍然会得到echo %a输出。我不明白为什么以下命令可以打印出所有的条目,而我针对%PATH%的尝试却不行。我怀疑我不理解/F开关。

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

Z:\>echo 1
1

Z:\>echo 2
2

Z:\>echo 3
3

2
PowerShell:$env:path.split(";") - ROMANIA_engineer
1
@ROMANIA_engineer - 由于问题要求在CMD shell中使用命令,这里提供一种方法来使用您优雅的答案:powershell -Command ($env:Path).split(';')。为了使它更易读,您可以添加排序功能:powershell -Command ($env:Path).split(';') | sort - weir
13个回答

84

简单的方法是使用

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

对于路径中没有;的所有内容且没有单个元素周围的",此方法适用
已经测试并通过了path=C:\qt\4.6.3\bin;C:\Program Files;C:\documents & Settings

但是一个“总是”解决方案有点复杂
编辑:现在有一个工作变体

@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
)

我做了什么?
我试图解决一个主要的问题:在引号内部应该忽略分号,只有“普通”的分号应该被替换为";"

我使用批处理解释器本身来解决这个问题。

  • 首先必须使字符串“安全”,转义所有特殊字符。
  • 然后将所有;替换为^;^;
  • 然后以以下方式开始神奇的操作:
    set var=%var:"=""%"(缺少的引号是关键!)
    这样展开,所有转义字符将失去其脱字符号:
    var=foo & bar;;baz<>gak;;"semi^;^;colons^;^;^&embedded";;foo again!;;...
    但仅限于引号外部,因此现在在引号外部和内部之间存在分号的差异,即;;表示引号外部的分号,而^;^;表示引号内部的分号。
    这就是关键所在。


我目前使用的测试字符串是 foo bar;baz gak;"semi;colons;embedded";foo again;throw (in) some (parentheses);"unmatched ;-)";(too 但你的更长的解决方案却因为 "unmatched ;-)" 而出现了问题。虽然较短的方法值得加分,但看到它时显然很明显,可惜我没有想到。 - Joey
"%path:;=";"%"c:\bin;c:\another bin 转换为 "c:\bin";"c:\another bin"。你能看出这里发生了什么吗? - bobbogo
@Joey:你说对了,路径中的引号确实很难处理。 - jeb
@bobbogo:当然,我把所有的目录都命名为“程序和文件” :-) @Joey:目前我的解决方案需要20行,但是FSA到底是什么?(足球协会萨克森-安哈尔特?) - jeb
4
NTFS文件名中唯一无效的字符是/\0。用户级Windows API对此进行了进一步的限制,但这仅包括用于shell的字符(<>&|*?"\)。 - Joey
显示剩余11条评论

80

一个简单的一行代码将美化打印PATH环境变量:

一个简短的代码来漂亮地打印 PATH 环境变量:

ECHO.%PATH:;= & ECHO.%
如果你的PATH等于A;B;C,那么上述字符串替换将把它改为 ECHO.A & ECHO.B & ECHO.C 并一次性执行。句号可以防止出现“ECHO is on”消息。

2
cmd 中运行得非常好,但在 PowerShell 中失败。 - Armand
1
这太美了。 - Amit Naidu

15

对 Stephan Quan 的一行代码解决方案进行了更新:我遇到的问题是一个分号(或者两个连续的分号,即空路径元素)会导致消息“ECHO is on”出现。我通过在第二个 ECHO 语句之后立即插入一个句点来解决这个问题(这是抑制 ECHO 开/关消息的语法)。不过,这将导致多出一个空行:

ECHO %PATH:;= & ECHO.%

1
这是cmd.exe变量编辑/替换语法:%variable:StrToFind=NewStr%,因此给定PATH=A;B,前面的回显命令会扩展为ECHO A & ECHO.B。需要替换ECHO.以处理空的PATH元素。 - p_wiersig

12

我对jeb的“always”解决方案进行了一些小改进。目前,jeb的解决方案存在以下问题:

  1. 如果前导路径被引号包含,则第一个输出以""开头
  2. 如果尾部路径被引号包含,则最后一个输出以""结尾
  3. 如果任何路径包含无害但非功能性连续"",则输出保留""
  4. 如果var包含连续的;;分隔符,则输出ECHO is off

该解决方案修复了这些小问题,还使用了2个较少的替换。此外,我在循环内部消除了不必要的重复启用/禁用延迟扩展。(2011-10-30编辑简化了ENDLOCAL逻辑)

@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
)

如果您想在连续的;;分隔符导致每个空路径都显示一个空行,则FOR循环的最后一行可以简单地写成echo(%%~a
或者,也可以使用以下方式将空路径显示为“”:
if %%a=="" (echo "") else echo %%~a 这些不同的空路径修复方法同样适用于jeb的简单解决方案。

更新:这里有一个使用JREPL.BAT的简单一行命令

您可以使用我的JREPL.BAT正则表达式文本处理实用程序来实现一个简单、非常强大的解决方案。JREPL.BAT是纯脚本(混合JScript/batch),可在任何从XP开始的Windows机器上本地运行。

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

4

Stephen Quan的回答更简洁更好,但这里有一个Python解决方案:

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

将分号;替换为换行符\n


3

这段命令可以在Windows上使用Git Bash的cmd窗口中运行:

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

你也可以在.bash_profile中创建一个方便的别名:

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


2
@ROMANIA_engineer在评论中提出了一个PowerShell解决方案。由于问题要求在CMD shell中运行命令,因此以下是一种使用OP所需环境中优雅代码的方法:

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

为使其更易读,您可以添加排序:

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

来源: https://dev59.com/Pm435IYBdhLWcg3w9FHi#34920014

1
回到这个话题,如果你有node.js并受到Bob Stein答案python的启发,可以尝试一行代码。
node -e "console.log(process.env.path.replace(/;/g,'\n'))"

或者更长的方式:

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

我需要找到已安装 git 的行:

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

1
我知道这很老,但是无论出于何种原因,我总想要这个。一段时间以前,我写了一个脚本来做到这一点。我稍微改进了一下并在我的博客上发布了它。
随意使用。
它叫做 epath,并且该文件位于 inzi.com。它编译为EXE文件,易于使用(使用vbsedit):here 您可以在那里下载exe文件。如果您希望将其作为vbs脚本,请参考以下源代码。
    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

@Marble68,epath.exe 就是我正在寻找的东西——谢谢你。 - Setaa

1
这是一个有良好注释的脚本,用于解析以分号分隔的项目列表,考虑每个项目的可选引号,并允许包含分号的项目(在引号中时)。由于它使用了一个goto循环,所以可能不是最快的方法,但应该可以防止特殊字符:
@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define semicolon-separated list here (sample taken from https://dev59.com/yW035IYBdhLWcg3wW-pa#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

1
不错的解决方案,我喜欢只检测引号然后获取到下一个引号或分号的想法。 - jeb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接