web-dev-qa-db-de.com

Windows-Batch-Skript zum Lesen einer INI-Datei

Ich versuche, eine .ini-Datei mit dem folgenden Format zu lesen:

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

Grundsätzlich möchte ich bestimmte Werte aus der .ini-Datei ausdrucken, zum Beispiel die Summe unter OtherSectionName, gefolgt von der Summe aus AnotherSectionName.

21
Hintswen

Hier ist eine Befehlsdatei (ini.cmd), mit der Sie die relevanten Werte extrahieren können:

@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

Und hier ist ein Transkript, das es in Aktion zeigt (ich habe die Ausgabe manuell eingerückt, um das Lesen zu erleichtern):

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

Um dies tatsächlich in einer anderen cmd-Datei zu verwenden, ersetzen Sie einfach die echo %val%-Zeile unten durch das, was Sie damit machen wollen:

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

Ich weiß, ich bin ein wenig zu spät zur Party, aber ich entschied mich, ein allgemeines ini-Datei-Utility-Batch-Skript zu schreiben, um diese Frage zu beantworten.

Mit dem Skript können Sie Werte in einer ini-style-Datei abrufen oder ändern. Bei der Suche wird die Groß- und Kleinschreibung nicht berücksichtigt, und es bleiben leere Zeilen in der Ini-Datei erhalten. Im Wesentlichen können Sie mit einer ini-Datei als eine Art sehr rudimentäre Datenbank interagieren.

Dieses Skript funktioniert gut, wenn Sie nur alphanumerische Werte oder Symbole lesen/schreiben, die keine besondere Bedeutung für den cmd-Interpreter haben. Wenn Sie etwas benötigen, das Werte behandeln kann, die Et-Zeichen, Prozentangaben usw. enthalten, lesen Sie den Abschnitt Update unten.

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

Beispiel

Inhalte von 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

Testsitzung:

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

Aktualisieren

Offensichtlich verschluckt die pure Batch-Lösung , wenn sie auf Zeichen wie & (und wahrscheinlich % und andere) trifft. Hier ist ein robusteres Batch + JScript-Hybrid-Skript, das dieses Problem anspricht. Die Syntax und Ausgabe sind die gleichen (jedoch mit einem hinzugefügten /d-Schalter zum Löschen von item=value-Paaren).

Dieses Skript setzt %ERRORLEVEL%=0 für den Erfolg und bei einem Fehler %ERRORLEVEL%=1.

@if (@[email protected]) @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

20
rojo

Ich habe einen kurzen Vorschlag für die read config.ini-Datei im aktuellen Verzeichnis von Windows Batch (.bat):

Am Ende der Batchdatei fügen wir diesen Code ein:

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

Und am Anfang der Batchdatei nennen wir es:

@call:ini IniFieldName batchVarName
@echo IniFieldName is: %batchVarName%
5
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

Hier ist das Dokument. Ich hoffe es hilft!

1
mosg

Alte Frage, aber ich brauchte das erst vor kurzem und fand @paxdiablo eine Antwort ... Ich brauchte etwas mehr, also bereicherte ich seine Antwort und gebe jetzt zurück.

Was ich auch brauchte, war zu finden, welcher Schlüssel einen bestimmten Wert besaß. Außerdem unterstütze ich explizit den Root-Abschnitt (keinen Abschnittsnamen).

Hier ist mein Code, eine Funktion, die ich in eine Bibliothek (CMDLib-Variable) stecke, die ich anrufe, wenn ich sie brauche (unter anderen Funktionen).

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

Kein Syntax-Highlighting für CMD? Das ist eine Schande.. ;-)

Hoffe, das hilft auch anderen.

1
Jay

Hmm, vielleicht hilft das jemandem. Musste es bauen, da inifile.exe keine Tricks mehr zur Verfügung standen und jeder verdammte Ini-Parser im Web 'KEY' braucht, wenn ich alle Werte unter [section] brauche. Also hier ist Abschnitt drucken ..

@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

Ja, ja, ich weiß, dass es von hinten aussieht, aber es funktioniert, obwohl es nicht sicher ist, ob es mit Leerzeichen in Ordnern oder Leerzeichen in Dateien funktioniert. Es wurde so gebaut, dass es nur eine .ini-Datei im selben Ordner hat und von der Befehlszeile aus ausgeführt wurde.

Verwendung: Abschnitt genetix_ini.cmd input.ext output.ext

UPDATE # 2: Anscheinend habe ich einen Fehler gemacht, weil ich die beiden vars nicht gesetzt hatte. Was beim zweiten Durchlauf des Skripts zu einem Problem führte.

0
genetix