用于读取.ini文件的Windows批处理脚本

时间:2010-05-19 13:55:54

标签: windows batch-file cmd ini

我正在尝试使用以下格式读取.ini文件:

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

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

9 个答案:

答案 0 :(得分:41)

这是一个命令文件(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%

答案 1 :(得分:22)

我知道我参加派对有点晚了,但我决定写一个通用的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);
}

ini.bat usage screen

答案 2 :(得分:5)

我对当前目录窗体批量(.bat)中的read config.ini文件提出了简短命题:

批处理文件的近端我们粘贴该代码:

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

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

@call:ini IniFieldName batchVarName
@echo IniFieldName is: %batchVarName%

答案 3 :(得分:5)

的config.ini

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

windows_batch.cmd

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

答案 4 :(得分:1)

Here is该文件。希望它有所帮助!

答案 5 :(得分:1)

老问题,但我最近才需要,并找到@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没有语法高亮显示?这真是遗憾..; - )

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

答案 6 :(得分:0)

嗯,也许这有助于某人..因为inifile.exe用完了所以不得不构建它,而且当我需要的所有值都是[section]时,web上的每个该死的ini解析器都需要'KEY'。所以,这是打印部分..

@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部分input.ext output.ext

更新#2: 似乎,我没有将2组变量归零。这在脚本的第二次传递中开始引起问题。

答案 7 :(得分:0)

再次感谢这个优秀 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 :(得分:0)

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