批处理文件 - 回显所有在批处理文件中设置的变量

3

假设我有以下代码:

@echo off
set "var=value"
echo %var%

现在我的问题是:如何输出文件中设置的所有变量?我知道可以这样做:
set

它将显示所有变量。它会显示:

ALLUSERSPROFILE=C:\ProgramData
APPDATA=C:\Users\foma\AppData\Roaming
CommonProgramFiles=C:\Program Files\Common Files
CommonProgramFiles(x86)=C:\Program Files (x86)\Common Files
CommonProgramW6432=C:\Program Files\Common Files
...

我希望它显示:
var=value

我知道可以使用set var来显示所有以var开头的变量,但在我的情况下,所有的变量都是从另一个批处理文件中“导入”的,因此我不知道变量名从何处开始。 有什么想法吗?


清除环境中的所有变量,然后设置您需要的变量。关于这种技术有一些讨论,因为它在某些情况下有助于提高性能。 - Squashman
@Squashman 我不建议使用那种解决方案。它需要深入研究您将要使用的所有命令以及操作系统配置的所有环境变量。甚至只需遵循一种命名约定,并决定让脚本中的变量以给定模式开头,然后使用 findfindstr 查找该模式会更容易。但即使如此,已经存在的变量的更改也无法被检测到。也许 OP 可以提供更多规格说明。 - J.Baoby
1
@HackR_360 你会用它来做什么?只是为了撤销环境上的更改吗?你是否还需要检测脚本执行前存在的变量所做出的更改?这些问题的答案可能会导致更简单(一个命令)的解决方案。 - J.Baoby
@J.Baoby,我想让我的程序:1.回显被调用批处理文件中的所有变量到一个 .txt 文件;2.允许用户修改被调用批处理文件的变量;3.创建另一个包含被调用批处理文件中所有变量的批处理文件。 - HackR_360
3个回答

7
@echo off
setlocal
set>originalfile.txt
...
:: list environment changes
set|findstr /x /L /g:originalfile.txt

应该是有效的 - 拍摄起始值的快照,然后findstr行应该显示所有已经进行的更改(除了删除) - 至少在理论上,这只是一个即兴建议。


1
+1 - 这对大多数值都有效。但是如果原始文件 originalfile.txt 包含 \" 或 \,由于使用 g:file 时 FINDSTR 转义问题可能会失败。 - dbenham
originalfile.txt 中的所有 \ 都加倍,然后再通过一些代码将每个 " 替换为 \",这样就可以使代码在(几乎)所有情况下都能正常工作了,不是吗,@dbenham?此外,我也点赞! - aschipfl
1
我收到错误信息FINDSTR: Search string too long. 即使它能正常工作,这不会只打印所有未更改的变量吗? - Klitos Kyriacou
@compo - 这个想法很好,但是OP间接承认它没有经过测试,显然我们中的许多人也没有测试并错过了所需的简单更正来获得大部分功能解决方案。将所有反斜杠加倍并添加“/V”将基本解决问题,除了这个FINDSTR错误 - dbenham
@Compo - 你提出了有价值的观点。我们都不是绝对正确的。如果可以的话,我会撤回我的赞成票,但我的投票已经“锁定”了。我做了一些测试,发现FINDSTR存在太多的错误,使其在这个问题上不可行。除了已经注意到的之外,还有搜索字符串长度限制,正则表达式[charlist]计数限制。 - dbenham
显示剩余3条评论

3
这将比较原始的cmd环境(保存到文件中)和当前环境。如果需要不同的检查,您可以更改文件创建方式。
@echo off
    setlocal enableextensions disabledelayedexpansion

    rem We need a temporary file to store the original environment
    for %%f in ("%temp%\original_%random%%random%%random%.tmp") do (

        rem Retrieve the original environment to the temporary file
        start /i /wait /min "" "%comspec%" /c">""%%~ff"" set "

        rem We need two flag variables. Prepare two names that "should" not collide
        for /f "tokens=1,2" %%d in ("_&d&%random%%random%_ _&m&%random%%random%_") do (

            rem %%d will be used to determine if we will check for variable deletion 
            rem     on the first inner pass
            rem %%e will be used for matching variables between existing/original variables
            set "%%d="

            rem Retrieve the current environment contents
            for /f "delims= eol==" %%a in ('set') do (

                rem We have not found matching variables
                set "%%e="

                rem Search a match in original set of variables 
                for /f "usebackq delims= eol==" %%o in ("%%~ff") do (

                    rem If variables match, flag it, else check for variable 
                    rem deletion is this is the first loop over the original file
                    if %%a==%%o ( set "%%e=1" ) else if not defined %%d (
                        for /f "delims==" %%V in ("%%~o") do if not defined %%V (echo(%%V=)
                    )
                )

                rem If no match found, output changed value
                if not defined %%e (echo(%%a)

            rem Now all the variable deletion has been checked.
            ) & if not defined %%d set "%%d=1"

        rem Cleanup flag variables
        ) & set "%%d=" & set "%%e="

    rem Cleanup temporary file
    ) & del "%%~ff"

一旦我们去掉注释,代码量并不像看起来那么多。

@echo off
    setlocal enableextensions disabledelayedexpansion
    for %%f in ("%temp%\original_%random%%random%%random%.tmp") do (
        start /i /wait /min "" "%comspec%" /c">""%%~ff"" set "
        for /f "tokens=1,2" %%d in ("_&d&%random%%random%_ _&m&%random%%random%_") do (
            set "%%d="
            for /f "delims= eol==" %%a in ('set') do (
                set "%%e="
                for /f "usebackq delims= eol==" %%o in ("%%~ff") do (
                    if %%a==%%o ( set "%%e=1" ) else if not defined %%d (
                        for /f "delims==" %%V in ("%%~o") do if not defined %%V (echo(%%V=)
                    )
                )
                if not defined %%e (echo(%%a)
            ) & if not defined %%d set "%%d=1"
        ) & set "%%d=" & set "%%e="
    ) & del "%%~ff"

edited仅供完整性。正如dbenham所指出的,以前的代码无法处理变量名称或值中带有换行符的变量。这是一个混合的批处理文件(保存为例如checkEnvironment.cmd文件),可以处理这种情况。

@if (@This==@IsBatch) @then /* Batch zone ------------------------------------
@echo off
    setlocal enableextensions disabledelayedexpansion
    call :processCurrentEnvironment "%~f1"
    goto :eof

:processCurrentEnvironment 
    call "%systemroot%\system32\cscript.exe" //nologo //e:JScript "%~f0" /saved:"%~1"
    goto :eof

*/ @end
// Javascript zone -----------------------------------------------------------

var environment = WScript.CreateObject('WScript.Shell').Environment('PROCESS');
var savedFileName = WScript.Arguments.Named.Item('saved');


    if ( !savedFileName ){
        // If no saved file name present, output current environment
        for ( var e = new Enumerator(environment); !e.atEnd(); e.moveNext()){ 
            WScript.StdOut.Write( e.item() + '\0\r\n' );
        };

    } else {
        // Retrieve saved environment to compare against current one
        var savedEnvironment = retrieveSavedEnvironment( savedFileName );

        // For each element in the current environment
        for ( var e = new Enumerator(environment); !e.atEnd(); e.moveNext() ) {

            // retrieve { variable: , value: } representation of the variable 
            var buffer = splitEnvironmentVariable( e.item() );

            if ( buffer ) {
                // if the current variable does not exist in the saved version
                // or if the current value does not match the previous one
                // then dump the current variable
                if (
                    !  savedEnvironment[buffer.variable]
                    || savedEnvironment[buffer.variable].value !== buffer.value
                ) outputVariable( buffer );

                // in any case, this is a processed variable
                delete savedEnvironment[buffer.variable];
            }
        };

        // any saved variables still present are deleted variables, show them
        for (var e in savedEnvironment) outputVariable( e );
    };

// - Helper functions ----------------------------------------------------------

// Process the contents of the saved file to generate a { {variable: , value: }, ... }
// representation of the saved environment

function retrieveSavedEnvironment( savedFileName ) {
    var savedEnvironment = {};
    var buffer = readFile( savedFileName ).split('\0\r\n');
    for (var i in buffer) if ( buffer[i] = splitEnvironmentVariable(buffer[i]) ) {
        savedEnvironment[buffer[i].variable] = buffer[i];
    };
    return ( savedEnvironment );
};

// Read the contents of the saved file - FSO can not be used because we are 
// writing nulls (ascii 0x00 character) to separate variables

function readFile( savedFileName ){
    var buffer = "";
    if (
        WScript.CreateObject('Scripting.FileSystemObject').FileExists( savedFileName )
    ){
        var stream = WScript.CreateObject('ADODB.Stream');
        stream.Open();
        stream.Type = 2
        stream.Charset = 'ascii';
        stream.LoadFromFile( savedFileName );
        buffer = stream.ReadText();
        stream.Close();
    };
    return ( buffer );
};

// Convert variable=value to { variable: , value: }

function splitEnvironmentVariable( text ){
    text = (/^([\s\S][^=]*)=([\s\S]+)/).exec( text ); 
    return (
        text 
        ? { variable : text[1] , value : text[2] } 
        : text
    );
};

// Write the variable with its value or only the variable name 

function outputVariable( variable ){
    if ( typeof variable === 'object' ) {
        WScript.StdOut.WriteLine( variable.variable + '=' + variable.value );
    } else {
        WScript.StdOut.WriteLine( variable + '=' );
    };
};

将它放置在批处理文件的路径或同一文件夹中,您可以将其用作

@echo off
    setlocal enableextensions enabledelayedexpansion

(SET LF=^
%=this line is empty=%
)

    rem Variables to delete
    set "variableToDelete=1"
    set "my!lf!var1=my!lf!data"

    set "originalEnvironment=%temp%\original_%random%%random%%random%.tmp"

    rem save environment 
    >"%originalEnvironment%" call checkEnvironment.cmd

    rem Create new variable2
    set "thisIsNew=value"
    set "anotherOne=1"

    rem Create another problematic variable
    set "my!lf!var2=my!lf!data"

    rem Delete variables
    set "variableToDelete="
    set "my!lf!var1="

    rem Dump environment changes
    call checkEnvironment.cmd "%originalEnvironment%"

    rem Remove temporary file
    del /q "%originalEnvironment%"

获得退出的方法是

W:\>testEnv.cmd
anotherOne=1
my
var2=my
data
thisIsNew=value
my
var1=
variableToDelete=
W:\>

1
非常好。对我有效。当然,如果变量值(或名称!)包含换行符,则此方法将失败,但是我认为没有任何批处理解决方案可以处理这种情况。我相信OP想要探测最近的批处理文件所做的更改,而不是自命令会话启动以来所做的更改。但您承认文件创建可以根据需要更改。 - dbenham
@dbenham,我不知道如何从纯批处理中处理换行符。以防有人觉得有用,我包含了一个混合版本来处理它。 - MC ND

0

无论如何,最简单的方法:

@echo off
setlocal enabledelayedexpansion
set "oldfile=oldfile.txt"
set "newfile=newfile.txt"
del %oldfile% >nul 2>nul
del %newfile% >nul 2>nul
for /f %%a in ('set') do echo %%a>>%oldfile%
::---------------------set-vars-here---------------------
set "testone=testonestring"
set "testtow=12324"
::-------------------------------------------------------
for /f %%a in ('set') do echo %%a>>%newfile%
for /f %%a in (%newfile%) do (
find "%%a" %oldfile% >nul 2>nul
if !errorlevel! NEQ 0 (
set "string=%%a"
echo !string!
)
)
del %oldfile% >nul 2>nul
del %newfile% >nul 2>nul
pause

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