【问题标题】:Windows batch script to read an .ini file用于读取 .ini 文件的 Windows 批处理脚本
【发布时间】:2011-02-21 09:46:45
【问题描述】:

我正在尝试读取具有以下格式的.ini 文件:

[SectionName]
total=4
[AnotherSectionName]
total=7
[OtherSectionName]
total=12

基本上我想从.ini 文件中打印出某些值,例如OtherSectionName 下的总数,然后是AnotherSectionName 下的总数。

【问题讨论】:

    标签: windows batch-file cmd ini


    【解决方案1】:

    这是一个命令文件 (ini.cmd),您可以使用它来提取相关值:

    @setlocal enableextensions enabledelayedexpansion
    @echo off
    set file=%~1
    set area=[%~2]
    set key=%~3
    set currarea=
    for /f "usebackq delims=" %%a in ("!file!") do (
        set ln=%%a
        if "x!ln:~0,1!"=="x[" (
            set currarea=!ln!
        ) else (
            for /f "tokens=1,2 delims==" %%b in ("!ln!") do (
                set currkey=%%b
                set currval=%%c
                if "x!area!"=="x!currarea!" if "x!key!"=="x!currkey!" (
                    echo !currval!
                )
            )
        )
    )
    endlocal
    

    这里有一个显示它的成绩单(我手动缩进了输出以使其更易于阅读):

    c:\src>type ini.ini
        [SectionName]
        total=4
        [AnotherSectionName]
        total=7
        [OtherSectionName]
        total=12
    c:\src>ini.cmd ini.ini SectionName total
        4
    c:\src>ini.cmd ini.ini AnotherSectionName total
        7
    c:\src>ini.cmd ini.ini OtherSectionName total
        12
    

    要在另一个 cmd 文件中实际使用它,只需将下面的 echo %val% 行替换为您想要使用的任何内容):

    for /f "delims=" %%a in ('call ini.cmd ini.ini AnotherSectionName total') do (
        set val=%%a
    )
    echo %val%
    

    【讨论】:

    • 有趣,但它不需要空格,至少变量和值之间没有空格。不支持“total = 12”
    • @Kar.ma,问题中似乎没有任何内容表明需要处理空格,因此,如果您需要,可能需要提出另一个问题。我会问 OP 他们是否真的想这样做,但由于他们自 2012 年以来就没有出现过,我对回复没有信心:-)
    • 延迟扩展前的 x 是做什么用的?例如x!currarea!?
    • @voger,它可以防止在变量设置为空字符串的情况下出错。
    【解决方案2】:

    我有一个简短的建议,即从 windows 批处理 (.bat) 的当前目录中读取 config.ini 文件:

    在批处理文件的末尾我们粘贴该代码:

    :ini    
    @for /f "tokens=2 delims==" %%a in ('find "%~2=" "%~1"') do @set %~3=%%a    
    @goto:eof
    

    在批处理文件的开头,我们称之为:

    @call:ini "config.ini" IniFieldName batchVarName
    @echo IniFieldName is: %batchVarName%
    

    【讨论】:

    • 正是我想要的。简单、简短且易读的设置键 => 文件中的值,只需使用 DoubleClick 即可编辑。
    【解决方案3】:

    我知道我参加聚会有点晚了,但我决定编写一个通用的 ini 文件实用程序批处理脚本来解决这个问题。

    该脚本将允许您检索或修改 ini 样式文件中的值。它的搜索不区分大小写,并且在 ini 文件中保留空白行。从本质上讲,它允许您将 ini 文件作为一种非常基本的数据库进行交互。

    如果您只读取/写入对cmd 解释器没有特殊含义的字母数字值或符号,则此脚本可以正常工作。如果您需要能够处理包含与号、百分比等值的东西,请参阅下面的更新部分。

    :: --------------------
    :: ini.bat
    :: ini.bat /? for usage
    :: --------------------
    
    @echo off
    setlocal enabledelayedexpansion
    
    goto begin
    
    :usage
    echo Usage: %~nx0 /i item [/v value] [/s section] inifile
    echo;
    echo Take the following ini file for example:
    echo;
    echo    [Config]
    echo    password=1234
    echo    usertries=0
    echo    allowterminate=0
    echo;
    echo To read the "password" value:
    echo    %~nx0 /s Config /i password inifile
    echo;
    echo To change the "usertries" value to 5:
    echo    %~nx0 /s Config /i usertries /v 5 inifile
    echo;
    echo In the above examples, "/s Config" is optional, but will allow the selection of
    echo a specific item where the ini file contains similar items in multiple sections.
    goto :EOF
    
    :begin
    if "%~1"=="" goto usage
    for %%I in (item value section found) do set %%I=
    for %%I in (%*) do (
        if defined next (
            if !next!==/i set item=%%I
            if !next!==/v set value=%%I
            if !next!==/s set section=%%I
            set next=
        ) else (
            for %%x in (/i /v /s) do if "%%~I"=="%%x" set "next=%%~I"
            if not defined next (
                set "arg=%%~I"
                if "!arg:~0,1!"=="/" (
                    1>&2 echo Error: Unrecognized option "%%~I"
                    1>&2 echo;
                    1>&2 call :usage
                    exit /b 1
                ) else set "inifile=%%~I"
            )
        )
    )
    for %%I in (item inifile) do if not defined %%I goto usage
    if not exist "%inifile%" (
        1>&2 echo Error: %inifile% not found.
        exit /b 1
    )
    
    if not defined section (
        if not defined value (
            for /f "usebackq tokens=2 delims==" %%I in (`findstr /i "^%item%\=" "%inifile%"`) do (
                echo(%%I
            )
        ) else (
            for /f "usebackq delims=" %%I in (`findstr /n "^" "%inifile%"`) do (
                set "line=%%I" && set "line=!line:*:=!"
                echo(!line! | findstr /i "^%item%\=" >NUL && (
                    1>>"%inifile%.1" echo(%item%=%value%
                    echo(%value%
                ) || 1>>"%inifile%.1" echo(!line!
            )
        )
    ) else (
        for /f "usebackq delims=" %%I in (`findstr /n "^" "%inifile%"`) do (
            set "line=%%I" && set "line=!line:*:=!"
            if defined found (
                if defined value (
                    echo(!line! | findstr /i "^%item%\=" >NUL && (
                        1>>"%inifile%.1" echo(%item%=%value%
                        echo(%value%
                        set found=
                    ) || 1>>"%inifile%.1" echo(!line!
                ) else echo(!line! | findstr /i "^%item%\=" >NUL && (
                    for /f "tokens=2 delims==" %%x in ("!line!") do (
                        echo(%%x
                        exit /b 0
                    )
                )
            ) else (
                if defined value (1>>"%inifile%.1" echo(!line!)
                echo(!line! | find /i "[%section%]" >NUL && set found=1
            )
        )
    )
    
    if exist "%inifile%.1" move /y "%inifile%.1" "%inifile%">NUL
    

    示例

    example.ini的内容:

    [SectionName]
    ; This is a comment.
    total=4
    
    [AnotherSectionName]
    # This is another comment.
    total=7
    
    [OtherSectionName]
    And it should work with non-standard comments as well.
    total=12
    

    测试会话:

    C:\Users\me\Desktop>ini /s AnotherSectionName /i total example.ini
    7
    
    C:\Users\me\Desktop>ini /s othersectionname /i Total /v f00 example.ini
    f00
    
    C:\Users\me\Desktop>type example.ini
    [SectionName]
    ; This is a comment.
    total=4
    
    [AnotherSectionName]
    # This is another comment.
    total=7
    
    [OtherSectionName]
    And it should work with non-standard comments as well.
    Total=f00
    

    更新

    当遇到&(可能还有% 等)这样的字符时,显然是pure batch solution chokes。所以这里有一个更强大的批处理 + JScript 混合脚本来解决这个问题。语法和输出相同(但添加了/d 开关以删除item=value 对)。

    此脚本将%ERRORLEVEL%=0 设置为成功,%ERRORLEVEL%=1 设置为错误。

    @if (@a==@b) @end /* -- batch / JScript hybrid line to begin JScript comment
    
    :: --------------------
    :: ini.bat
    :: ini.bat /? for usage
    :: --------------------
    
    @echo off
    setlocal enabledelayedexpansion
    
    goto begin
    
    :: color code by jeb -- https://stackoverflow.com/a/5344911/1683264
    :c
    set "param=^%~2" !
    set "param=!param:"=\"!"
    findstr /p /A:%1 "." "!param!\..\X" nul
    <nul set /p ".=%DEL%%DEL%%DEL%%DEL%%DEL%%DEL%%DEL%"
    exit /b
    :: but it doesn't handle slashes.  :(
    :s
    <NUL set /p "=/"&exit /b
    
    :usage
    for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do set "DEL=%%a"
    <nul > X set /p ".=."
    
    echo Usage:
    call :c 07 "   query:"
    call :c 0F " %~nx0 "&call :s&call :c 0F "i item ["&call :s&call :c 0F "s section] inifile"&echo;
    call :c 07 "   create or modify:"
    call :c 0F " %~nx0 "&call :s&call :c 0F "i item "&call :s&call :c 0F "v value ["&call :s&call :c 0F "s section] inifile"&echo;
    call :c 07 "   delete:"
    call :c 0F " %~nx0 "&call :s&call :c 0F "d item ["&call :s&call :c 0F "s section] inifile"&echo;
    echo;
    echo Take the following ini file for example:
    echo;
    echo    [Config]
    echo    password=1234
    echo    usertries=0
    echo    allowterminate=0
    echo;
    echo To read the "password" value:
    call :c 0F "   %~nx0 "&call :s&call :c 0F "s Config "&call :s&call :c 0F "i password inifile"&echo;
    echo;
    echo To modify the "usertries" value to 5:
    call :c 0F "   %~nx0 "&call :s&call :c 0F "s Config "&call :s&call :c 0F "i usertries "&call :s&call :c 0F "v 5 inifile"&echo;
    echo;
    echo To add a "timestamp" key with a value of the current date and time:
    call :c 0F "   %~nx0 "&call :s&call :c 0F "s Config "&call :s&call :c 0F "i timestamp "&call :s&call :c 0F "v ""%DEL%%%%%date%%%% %%%%time%%%%""%DEL% inifile"&echo;
    echo;
    echo To delete the "allowterminate" key:
    call :c 0F "   %~nx0 "&call :s&call :c 0F "s Config "&call :s&call :c 0F "d allowterminate inifile"&echo;
    echo;
    call :c 07 "In the above examples, "&call :s
    call :c 0F "s Config "
    echo is optional, but will allow the selection of
    echo a specific item where the ini file contains similar items in multiple sections.
    del X
    goto :EOF
    
    :begin
    if "%~1"=="" goto usage
    for %%I in (item value section found) do set %%I=
    for %%I in (%*) do (
        if defined next (
            if !next!==/i set "item=%%~I"
            if !next!==/v (
                set modify=true
                set "value=%%~I"
            )
            if !next!==/d (
                set "item=%%~I"
                set modify=true
                set delete=true
            )
            if !next!==/s set "section=%%~I"
            set next=
        ) else (
            for %%x in (/i /v /s /d) do if "%%~I"=="%%x" set "next=%%~I"
            if not defined next (
                set "arg=%%~I"
                if "!arg:~0,1!"=="/" (
                    1>&2 echo Error: Unrecognized option "%%~I"
                    1>&2 echo;
                    1>&2 call :usage
                    exit /b 1
                ) else set "inifile=%%~I"
            )
        )
    )
    for %%I in (item inifile) do if not defined %%I goto usage
    if not exist "%inifile%" (
        1>&2 echo Error: %inifile% not found.
        exit /b 1
    )
    
    cscript /nologo /e:jscript "%~f0" "%inifile%" "!section!" "!item!" "!value!" "%modify%" "%delete%"
    
    exit /b %ERRORLEVEL%
    
    :: Begin JScript portion */
    var inifile = WSH.Arguments(0),
    section = WSH.Arguments(1),
    item = WSH.Arguments(2),
    value = WSH.Arguments(3),
    modify = WSH.Arguments(4),
    del = WSH.Arguments(5),
    fso = new ActiveXObject("Scripting.FileSystemObject"),
    stream = fso.OpenTextFile(inifile, 1),
    
    // (stream.ReadAll() will not preserve blank lines.)
    data = [];
    while (!stream.atEndOfStream) { data.push(stream.ReadLine()); }
    stream.Close();
    
    // trims whitespace from edges
    String.prototype.trim = function() { return this.replace(/^\s+|\s+$/,'') }
    
    // trim + toLowerCase
    String.prototype.unify = function() { return this.trim().toLowerCase(); };
    
    // unquotes each side of "var"="value"
    String.prototype.splitEx = function(x) {
        for (var i=0, ret = this.split(x) || []; i<ret.length; i++) {
            ret[i] = ret[i].replace(/^['"](.*)['"]$/, function(m,$1){return $1});
        };
        return ret;
    }
    
    // splices a new element into an array just after the last non-empty element.  If first arg is a number, start at that position and look backwards.
    Array.prototype.cram = function() {
        for (var args=[], i=0; i<arguments.length; i++) { args.push(arguments[i]); }
        var i = (typeof args[0] == "number" && Math.floor(args[0]) == args[0]) ? args.shift() : this.length;
        while (i>0 && !this[--i].length) {};
        for (var j=0; j<args.length; j++) this.splice(++i, 0, args[j]);
    }
    
    function saveAndQuit() {
        while (data && !data[data.length - 1].length) data.pop();
        var stream = fso.OpenTextFile(inifile, 2, true);
        stream.Write(data.join('\r\n') + '\r\n');
        stream.Close();
        WSH.Quit(0);
    }
    
    function fatal(err) {
        WSH.StdErr.WriteLine(err);
        WSH.Quit(1);
    }
    
    if (section && !/^\[.+\]$/.test(section)) section = '[' + section + ']';
    
    if (modify) {
        if (section) {
            for (var i=0; i<data.length; i++) {
                if (data[i].unify() == section.unify()) {
                    for (var j=i + 1; j<data.length; j++) {
                        if (/^\s*\[.+\]\s*$/.test(data[j])) break;
                        var keyval = data[j].splitEx('=');
                        if (keyval.length < 2) continue;
                        var key = keyval.shift(), val = keyval.join('=');
                        if (key.unify() == item.unify()) {
                            if (del) data.splice(j, 1);
                            else {
                                data[j] = item + '=' + value;
                                WSH.Echo(value.trim());
                            }
                            saveAndQuit();
                        }
                    }
                    if (del) fatal(item + ' not found in ' + section + ' in ' + inifile);
                    data.cram(j ,item + '=' + value);
                    WSH.Echo(value.trim());
                    saveAndQuit();
                }
            }
            if (del) fatal(section + ' not found in ' + inifile);
            data.cram('\r\n' + section, item + '=' + value);
            WSH.Echo(value.trim());
            saveAndQuit();
        }
        else { // if (!section)
            for (var i=0; i<data.length; i++) {
                var keyval = data[i].splitEx('=');
                if (keyval.length < 2) continue;
                var key = keyval.shift(), val = keyval.join('=');
                if (key.unify() == item.unify()) {
                    if (del) data.splice(i, 1);
                    else {
                        data[i] = item + '=' + value;
                        WSH.Echo(value.trim());
                    }
                    saveAndQuit();
                }
            }
            if (del) fatal(item + ' not found in ' + inifile);
            data.cram(item + '=' + value);
            WSH.Echo(value.trim());
            saveAndQuit();
        }
    }
    else if (section) { // and if (!modify)
        for (var i=0; i<data.length; i++) {
            if (data[i].unify() == section.unify()) {
                for (var j=i + 1; j<data.length; j++) {
                    if (/^\s*\[.+\]\s*$/.test(data[j])) fatal(item + ' not found in ' + section + ' in ' + inifile);
                    var keyval = data[j].splitEx('=');
                    if (keyval.length < 2) continue;
                    var key = keyval.shift(), val = keyval.join('=');
                    if (key.unify() == item.unify()) {
                        WSH.Echo(val.trim());
                        WSH.Quit(0);
                    }
                }
            }
        }
        fatal(section + ' not found in ' + inifile);
    }
    else { // if (item) and nothing else
        for (var i=0; i<data.length; i++) {
            var keyval = data[i].splitEx('=');
            if (keyval.length < 2) continue;
            var key = keyval.shift(), val = keyval.join('=');
            if (key.unify() == item.unify()) {
                WSH.Echo(val.trim());
                WSH.Quit(0);
            }
        }
        fatal(item + ' not found in ' + inifile);
    }
    

    【讨论】:

    • 出色的工作!不过,我有一个拉取请求。在非更新版本的第 37、38 和 39 行添加波浪线以去除 args 中的引号。前任。 if !next!==/i set item=%%~I
    • 谢谢!这篇文章为我节省了很多时间!
    • 我需要解析 ini 文件,将部分名称转换为变量...之后我想与用户交互。为他们提供不同的值,然后在提供的脚本中使用选定的值……这可能吗?我不是批处理编码器
    • 如果 ini 文件的路径在 Program Files (x86) 内的某个位置是绝对路径,则第一个版本将不起作用。它会被括号弄糊涂。
    【解决方案4】:

    嗯,也许这对某人有帮助.. 不得不构建它,因为 inifile.exe 用尽了技巧,并且似乎 web 上的每个该死的 ini 解析器都需要“KEY”,而我只需要 [section] 下的所有值。所以,这里是部分打印..

    @echo off
    SETLOCAL DisableDelayedExpansion
    IF "%1"=="" (echo Usage: section input.ext output.ext & goto eof )
    IF "%2"=="" (echo Usage: section input.ext output.ext & goto eof )
    IF NOT EXIST "%2" (echo File does not exist. Usage: section input.ext output.ext & goto eof )
    IF "%3"=="" (echo Usage: section input.ext output.ext & goto eof )
    FOR /F "tokens=*" %%A IN ('findstr /I /N "\[.*\]" %2') DO (echo %%A>>LINE_START.DAT)
    FOR /F "tokens=1,2 delims=:" %%A IN ('findstr /I "\[%1\]" LINE_START.DAT') DO (
    SETLOCAL EnableDelayedExpansion
    set FIRSTLINE=%%A
    )
    set /a "FIRSTLINE+=1"
    FOR /F "tokens=1,2* delims=:" %%A IN ('findstr /I /N ".*" %2') DO (
    IF %%A GEQ !FIRSTLINE! (echo %%B>>LINE_END.DAT)
    )
    set ENDLINE=500
    FOR /F "tokens=1,2* delims=:" %%A IN ('findstr /I /N "\[.*\]" LINE_END.DAT') DO (
    IF %%A LSS !ENDLINE! (set /a "ENDLINE=%%A") ELSE echo %%A>nul
    )
    set /a "ENDLINE-=1"
    FOR /F "tokens=1,2* delims=:" %%A IN ('findstr /I /N ".*" LINE_END.DAT') DO (
    IF %%A LEQ !ENDLINE! (echo %%B>>%3) ELSE ENDLOCAL
    )
    set ENDLINE=0
    set FIRSTLINE=0
    ENDLOCAL
    DEL /Q LINE_*.DAT
    :end
    

    是的,是的,我知道它看起来像是从后面看的,但它确实有效,但不确定它是否适用于文件夹中的空格或文件中的空格。将其构建为基本上只有 .ini 文件位于同一文件夹并从命令行运行。

    用法:genetix_ini.cmd section input.ext output.ext

    更新#2: 似乎,我在没有将 2 组变量归零时犯了错误。这在脚本的第二次通过时开始导致问题。

    【讨论】:

      【解决方案5】:

      config.ini

      foo=string
      bar=123
      baz=spaces work too!
      

      windows_batch.cmd

      for /F "tokens=*" %%I in (config.ini) do set %%I
      

      【讨论】:

      • 但不能使用引号(例如foo="C:/Program Files/Test/"
      【解决方案6】:

      老问题,但我最近才需要它并找到@paxdiablo 答案。 我需要更多的东西,所以我丰富了他的答案,我现在正在回馈。

      我还需要找出哪个键具有特定值。 此外,明确支持根部分(无部分名称)。

      这是我的代码,一个我放入库中的函数(CMDLib 变量),我需要时调用它(以及其他函数)。

      :ReadINI
      REM ReadINI - Get value from [Section]; Key from an INI File.
      REM Arguments:
      REM   File    INI-file to read from
      REM   Key     Name of the entry
      REM   Section Name of the [Section] under which the Value is.
      REM     Optional, will find a value from the root section if empty.
      REM               For root section, set to "-" to also use "Value"
      REM   Value   If Key is set to "-", will find which Key has "Value"
      REM
      REM Returns: A string of text will be echoed, ready for logging.
      REM   An echo of the value.
      REM
      REM Call example:
      REM   for /f "delims=" %%a in ('Call "%CMDLib%" ReadINI "Inifile" Key Section') do ( set Value=%%a)
      REM
      REM Original: http://stackoverflow.com/a/2866328/151152
      rem ------- Function header -------
          Setlocal ENABLEDELAYEDEXPANSION
          :: Logging formatting
          if not defined nest (set /a nest=0) else set /a Nest=%nest%+1
          if %nest% GEQ 1 if not defined _tab (set _tab=    ) else for /l %%i in (0, %nest%,1) do set _tab=%_tab%    
      rem ------- Function body -------
          set file=%~1
          set key=%~2
          set Section=[%~3]
          if "%Section%"=="-" set Section=
          set value=%~4
          set currSection=
          Set RC=0
          for /f "usebackq delims=" %%a in ("%file%") do (
              set ln=%%a
              if "x!ln:~0,1!"=="x[" (
                  set currSection=!ln!
              ) else (
                  for /f "tokens=1,2 delims==" %%b in ("!ln!") do (
                      set currkey=%%b
                      set currval=%%c
                      if /i "x!Section!"=="x!currSection!" (
                          if /i "x!key!"=="x!currkey!" (
                              echo !currval!
                              if %_D% GEQ 2 echo %_tab%[%0 - RC:%RC%]
                              exit /b %RC%
                          ) Else if "x!key!"=="x-" (
                              if /i "x!value!"=="x!currval!" (
                                  echo !currkey!
                                  if %_D% GEQ 2 echo %_tab%[%0 - RC:%RC%]
                                  exit /b %RC%
                              )
                          )
                      )
                  )
              )
          )
          if %_D% GEQ 2 echo %_tab%[%0 - RC:%RC%]
          Exit /b %RC%
      rem ------- Function end -------
      

      CMD 没有语法高亮显示?真可惜.. ;-)

      希望这对其他人也有帮助。

      【讨论】:

      • 好的,我知道这是很老的答案,但我遇到了它,它似乎是我需要的解决方案。唯一的问题是您使用 %_D% var 并且我找不到初始化,因为它似乎是空的。你能告诉我它是干什么用的吗?
      • 我完全忘了解释这一点。 _D 只是一个调试输出变量,在我编写的整个脚本中都使用。它在函数中,因为我已经有了它。您可以将其设置在顶部以直观地验证输出或删除此类行,它们没有影响。
      【解决方案7】:

      再次感谢这个优秀的 INI 脚本和 2017 年 5 月 23 日发布的更新。仍然非常有用!

      我确实添加了一个 /q 开关,“保持安静”,从而抑制了由 fatal() 函数发出的消息。 这是必要的,因为我从 Windows CMD 脚本调用 INI 脚本,我发现 stdout (1>) 和 stderr (2>) 的重定向不会拦截 Windows 脚本主机发出的消息。

      一个例子:

      for /f "usebackq tokens=*" %%a in (`ini /s Section /i Entry File.ini`) do set go_drive=%%a 1> nul: 2> nul:
      

      ...不抑制错误消息,而...

      for /f "usebackq tokens=*" %%a in (`ini /q /s Section /i Entry File.ini`) do set go_drive=%%a 1> nul: 2> nul:
      

      ...正在抑制错误消息

      我已经发布了与此代码相邻的代码。

      【讨论】:

        【解决方案8】:
        @if (@a==@b) @end /* -- batch / JScript hybrid line to begin JScript comment
        
        :: ----------------------------------------------------------------------------------------------
        :: ini.bat
        :: ini.bat /? for usage
        ::
        :: Source: https://stackoverflow.com/questions/2866117/windows-batch-script-to-read-an-ini-file
        ::
        :: 2021-08-03: /q switch added, to suppress error messages
        :: 
        :: ----------------------------------------------------------------------------------------------
        
        @echo off
        setlocal enabledelayedexpansion
        
        goto begin
        
        :: color code by jeb -- https://stackoverflow.com/a/5344911/1683264
        :c
        set "param=^%~2" !
        set "param=!param:"=\"!"
        findstr /p /A:%1 "." "!param!\..\X" nul
        <nul set /p ".=%DEL%%DEL%%DEL%%DEL%%DEL%%DEL%%DEL%"
        exit /b
        :: but it doesn't handle slashes.  :(
        :s
        <NUL set /p "=/"&exit /b
        
        :usage
        for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do set "DEL=%%a"
        <nul > X set /p ".=."
        
        echo Usage:
        call :c 07 "   query:"
        call :c 0F " %~nx0 "&call :s&call :c 0F "i item ["&call :s&call :c 0F "s section] inifile"&echo;
        call :c 07 "   create or modify:"
        call :c 0F " %~nx0 "&call :s&call :c 0F "i item "&call :s&call :c 0F "v value ["&call :s&call :c 0F "s section] inifile"&echo;
        call :c 07 "   delete:"
        call :c 0F " %~nx0 "&call :s&call :c 0F "d item ["&call :s&call :c 0F "s section] inifile"&echo;
        call :c 07 "   quiet:"
        call :c 0F " %~nx0 "&call :s&call :c 0F "q "&echo;
        echo;
        echo Take the following ini file for example:
        echo;
        echo    [Config]
        echo    password=1234
        echo    usertries=0
        echo    allowterminate=0
        echo;
        echo To read the "password" value:
        call :c 0F "   %~nx0 "&call :s&call :c 0F "s Config "&call :s&call :c 0F "i password inifile"&echo;
        echo;
        echo To modify the "usertries" value to 5:
        call :c 0F "   %~nx0 "&call :s&call :c 0F "s Config "&call :s&call :c 0F "i usertries "&call :s&call :c 0F "v 5 inifile"&echo;
        echo;
        echo To add a "timestamp" key with a value of the current date and time:
        call :c 0F "   %~nx0 "&call :s&call :c 0F "s Config "&call :s&call :c 0F "i timestamp "&call :s&call :c 0F "v ""%DEL%%%%%date%%%% %%%%time%%%%""%DEL% inifile"&echo;
        echo;
        echo To delete the "allowterminate" key:
        call :c 0F "   %~nx0 "&call :s&call :c 0F "s Config "&call :s&call :c 0F "d allowterminate inifile"&echo;
        echo;
        call :c 07 "In the above examples, "&call :s
        call :c 0F "s Config "
        echo is optional, but will allow the selection of
        echo a specific item where the ini file contains similar items in multiple sections.
        del X
        goto :EOF
        
        :begin
        if "%~1"=="" goto usage
        for %%I in (item value section found) do set %%I=
        for %%I in (%*) do (
            if defined next (
                if !next!==/i set "item=%%~I"
                if !next!==/v (
                    set modify=true
                    set "value=%%~I"
                )
                if !next!==/d (
                    set "item=%%~I"
                    set modify=true
                    set delete=true
                )
                if !next!==/s set "section=%%~I"
        :
        :   /q switch added. Suppress error messages.
        :
                if !next!==/q set quiet=true
                set next=
            ) else (
                for %%x in (/i /v /s /d /q) do if "%%~I"=="%%x" set "next=%%~I"
                if not defined next (
                    set "arg=%%~I"
                    if "!arg:~0,1!"=="/" (
                        1>&2 echo Error: Unrecognized option "%%~I"
                        1>&2 echo;
                        1>&2 call :usage
                        exit /b 1
                    ) else set "inifile=%%~I"
                )
            )
        )
        for %%I in (item inifile) do if not defined %%I goto usage
        if not exist "%inifile%" (
            1>&2 echo Error: %inifile% not found.
            exit /b 1
        )
        
        cscript /nologo /e:jscript "%~f0" "%inifile%" "!section!" "!item!" "!value!" "%modify%" "%delete%" "%quiet%"
        
        exit /b %ERRORLEVEL%
        
        :: Begin JScript portion */
        var inifile = WSH.Arguments(0),
        section = WSH.Arguments(1),
        item = WSH.Arguments(2),
        value = WSH.Arguments(3),
        modify = WSH.Arguments(4),
        del = WSH.Arguments(5),
        quiet = WSH.Arguments(6),
        fso = new ActiveXObject("Scripting.FileSystemObject"),
        stream = fso.OpenTextFile(inifile, 1),
        
        // (stream.ReadAll() will not preserve blank lines.)
        data = [];
        while (!stream.atEndOfStream) { data.push(stream.ReadLine()); }
        stream.Close();
        
        // trims whitespace from edges
        String.prototype.trim = function() { return this.replace(/^\s+|\s+$/,'') }
        
        // trim + toLowerCase
        String.prototype.unify = function() { return this.trim().toLowerCase(); };
        
        // unquotes each side of "var"="value"
        String.prototype.splitEx = function(x) {
            for (var i=0, ret = this.split(x) || []; i<ret.length; i++) {
                ret[i] = ret[i].replace(/^['"](.*)['"]$/, function(m,$1){return $1});
            };
            return ret;
        }
        
        // splices a new element into an array just after the last non-empty element.  If first arg is a number, start at that position and look backwards.
        Array.prototype.cram = function() {
            for (var args=[], i=0; i<arguments.length; i++) { args.push(arguments[i]); }
            var i = (typeof args[0] == "number" && Math.floor(args[0]) == args[0]) ? args.shift() : this.length;
            while (i>0 && !this[--i].length) {};
            for (var j=0; j<args.length; j++) this.splice(++i, 0, args[j]);
        }
        
        function saveAndQuit() {
            while (data && !data[data.length - 1].length) data.pop();
            var stream = fso.OpenTextFile(inifile, 2, true);
            stream.Write(data.join('\r\n') + '\r\n');
            stream.Close();
            WSH.Quit(0);
        }
        
        function fatal(err) {
            if (!quiet) {
                WSH.StdErr.WriteLine(err);
            }
            WSH.Quit(1);
        }
        
        if (section && !/^\[.+\]$/.test(section)) section = '[' + section + ']';
        
        if (modify) {
            if (section) {
                for (var i=0; i<data.length; i++) {
                    if (data[i].unify() == section.unify()) {
                        for (var j=i + 1; j<data.length; j++) {
                            if (/^\s*\[.+\]\s*$/.test(data[j])) break;
                            var keyval = data[j].splitEx('=');
                            if (keyval.length < 2) continue;
                            var key = keyval.shift(), val = keyval.join('=');
                            if (key.unify() == item.unify()) {
                                if (del) data.splice(j, 1);
                                else {
                                    data[j] = item + '=' + value;
                                    WSH.Echo(value.trim());
                                }
                                saveAndQuit();
                            }
                        }
                        if (del) fatal(item + ' not found in ' + section + ' in ' + inifile);
                        data.cram(j ,item + '=' + value);
                        WSH.Echo(value.trim());
                        saveAndQuit();
                    }
                }
                if (del) fatal(section + ' not found in ' + inifile);
                data.cram('\r\n' + section, item + '=' + value);
                WSH.Echo(value.trim());
                saveAndQuit();
            }
            else { // if (!section)
                for (var i=0; i<data.length; i++) {
                    var keyval = data[i].splitEx('=');
                    if (keyval.length < 2) continue;
                    var key = keyval.shift(), val = keyval.join('=');
                    if (key.unify() == item.unify()) {
                        if (del) data.splice(i, 1);
                        else {
                            data[i] = item + '=' + value;
                            WSH.Echo(value.trim());
                        }
                        saveAndQuit();
                    }
                }
                if (del) fatal(item + ' not found in ' + inifile);
                data.cram(item + '=' + value);
                WSH.Echo(value.trim());
                saveAndQuit();
            }
        }
        else if (section) { // and if (!modify)
            for (var i=0; i<data.length; i++) {
                if (data[i].unify() == section.unify()) {
                    for (var j=i + 1; j<data.length; j++) {
                        if (/^\s*\[.+\]\s*$/.test(data[j])) fatal(item + ' not found in ' + section + ' in ' + inifile);
                        var keyval = data[j].splitEx('=');
                        if (keyval.length < 2) continue;
                        var key = keyval.shift(), val = keyval.join('=');
                        if (key.unify() == item.unify()) {
                            WSH.Echo(val.trim());
                            WSH.Quit(0);
                        }
                    }
                }
            }
            fatal(section + ' not found in ' + inifile);
        }
        else { // if (item) and nothing else
            for (var i=0; i<data.length; i++) {
                var keyval = data[i].splitEx('=');
                if (keyval.length < 2) continue;
                var key = keyval.shift(), val = keyval.join('=');
                if (key.unify() == item.unify()) {
                    WSH.Echo(val.trim());
                    WSH.Quit(0);
                }
            }
            fatal(item + ' not found in ' + inifile);
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-05-16
          • 2015-11-02
          • 2015-09-13
          • 2013-01-08
          • 2013-09-23
          • 1970-01-01
          • 2019-06-01
          相关资源
          最近更新 更多