web-dev-qa-db-fra.com

Script batch Windows pour lire un fichier .ini

J'essaie de lire un fichier .ini au format suivant:

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

En gros, je veux imprimer certaines valeurs du fichier .ini, par exemple le total sous OtherSectionName suivi du total de AnotherSectionName.

21
Hintswen

Voici un fichier de commande (ini.cmd) que vous pouvez utiliser pour extraire les valeurs pertinentes:

@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

Et voici une transcription le montrant en action (j'ai manuellement mis en retrait la sortie pour faciliter la lecture):

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

Pour réellement utiliser cela dans un autre fichier cmd, remplacez simplement la ligne echo %val% ci-dessous par ce que vous voulez en faire):

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

Je sais que je suis un peu en retard à la fête, mais j'ai décidé d'écrire un script de traitement par lots à usage général pour les fichiers ini afin de répondre à cette question.

Le script vous permettra de récupérer ou de modifier des valeurs dans un fichier de style ini. Ses recherches ne font pas la distinction entre les majuscules et les minuscules et conserve les lignes vides dans le fichier ini. En substance, il vous permet d’interagir avec un fichier ini comme une sorte de base de données très rudimentaire.

Ce script fonctionnera correctement si vous ne lisez/écrivez que des valeurs alphanumériques ou des symboles n’ayant pas de signification particulière pour l’interprète cmd. Si vous avez besoin de quelque chose capable de gérer des valeurs contenant des esperluettes, des pourcentages, etc., voir la section Update ci-dessous.

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

Exemple

Contenu de 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

Session de test:

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

Mettre à jour

Apparemment, la solution de traitement par lots pure lorsqu'elle rencontre des caractères tels que & (et probablement % et autres). Voici donc un script hybride batch + JScript plus robuste qui résout ce problème. La syntaxe et la sortie sont identiques (mais avec un commutateur /d ajouté pour supprimer les paires item=value).

Ce script définit %ERRORLEVEL%=0 en tant que succès et %ERRORLEVEL%=1 en cas d'erreur.

@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

20
rojo

J'ai la proposition courte pour lire le fichier config.ini dans le lot en cours de Windows (.bat):

Vers la fin du fichier batch, nous collons ce code:

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

Et près du début du fichier batch, nous l'appelons par:

@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

Une vieille question, mais j'avais récemment besoin de ça et j'ai trouvé la réponse de @paxdiablo .J'ai besoin de quelque chose de plus alors j'ai enrichi sa réponse et je me rends maintenant.

Ce dont j'avais également besoin, c'était de trouver quelle clé contenait une valeur spécifique .

Voici mon code, une fonction que je mets dans une bibliothèque (variable CMDLib) que j'appelle quand j'en ai besoin (parmi d'autres fonctions).

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

Pas de coloration syntaxique pour CMD? C'est une honte.. ;-)

J'espère que cela aide les autres aussi.

1
Jay

Hmm, peut-être que cela aide quelqu'un… Il fallait le construire car inifile.exe manquait d'astuces et semblait avoir tous les fous de l'analyseur d'internet sur le Web ayant besoin de 'KEY' alors que tout ce dont j'avais besoin, ce sont toutes les valeurs de [section]. Alors, voici la section d'impression ..

@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

Ouais, ouais je sais que ça a l'air d'être de l'arrière, mais ça marche, bien que, je ne suis pas sûr que ça va marcher avec des espaces dans des dossiers ou des espaces dans des fichiers. Il a été construit de manière à ce que le fichier .ini se trouve dans le même dossier et s’exécute depuis la ligne de commande.

Utilisation: genetix_ini.cmd section input.ext output.ext

UPDATE # 2: Semble, je me suis trompé en ne mettant pas à zéro les 2 vars définis. Ce qui a commencé à causer un problème lors de la deuxième passe du script.

0
genetix