web-dev-qa-db-ja.com

.iniファイルを読み取るWindowsバッチスクリプト

次の形式の.iniファイルを読み取ろうとしています。

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

基本的に、.iniファイルから特定の値、たとえばOtherSectionNameの下の合計に続いてAnotherSectionNameの合計を出力したいと思います。

23
Hintswen

コマンドファイル(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%
41
paxdiablo

私はパーティーに少し遅れていることを知っていますが、この質問に対処するために汎用のiniファイルユーティリティバッチスクリプトを書くことにしました。

このスクリプトを使用すると、iniスタイルファイルの値を取得または変更できます。検索では大文字と小文字が区別されず、iniファイル内の空白行が保持されます。本質的には、非常に基本的なデータベースのようなiniファイルを操作できます。

このスクリプトは、cmdインタープリターにとって特別な意味を持たない英数字の値または記号のみを読み書きする場合に正常に機能します。アンパサンド、パーセントなどを含む値を処理できるものが必要な場合は、以下のUpdateセクションを参照してください。

:: --------------------
:: 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

22
rojo

私は、Windowsバッチ(.bat)から現在のディレクトリにある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%
6
emil

config.ini

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

windows_batch.cmd

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

古い質問ですが、私は最近それを必要とし、@ 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の構文強調表示はありませんか?残念です.. ;-)

これが他の人にも役立つことを願っています。

1
Jay

うーん、多分これは誰かを助ける.. inifile.exeがトリックを使い果たしたので、それを構築しなければならなかった、そして、私が必要とするすべてが[セクション]の下のすべての値であるとき、ウェブのすべてのいまいましい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セットの変数をゼロ化しないという間違いを犯したようです。スクリプトの2回目のパスで問題が発生し始めました。

0
genetix