2012-10-26 4 views
0

J'ai passé un peu de temps à fouiller autour de stackoverflow et le net en général à la recherche d'une réponse bien décrite à ce problème.WindowsCommand Line> Modifier une chaîne spécifique (contenant un signe égal) à partir d'un fichier texte

J'ai un logiciel qui s'installe et lit un fichier .ini. Ce fichier .ini est de taille variable et de numéros de ligne variables. Le style de chaque ligne est quelque chose comme ce qui suit:

setting1=contents 
setting2=more,contents 
setting3=different type of contents 
setting4=youget/theidea 

je dois rechercher ce fichier texte pour quelques lignes spécifiques, disons que c'est la suivante:

Username=Tommy 
Servername=HAL2000 

Et puis remplacez les valeurs après le signe égal (c.-à-d. changer Tommy en Timmy). Mon problème est que tout le script que j'ai trouvé et essayé d'incorporer voit les valeurs ci-dessus comme une variable (ie nom d'utilisateur devient une variable avec la valeur "Tommy") ou quand je vais remplacer Tommy par timmy, le fichier .ini finit par remplacer CHAQUE ligne dans le fichier avec Username = Tommy.

J'ai depuis effacé ces deux scripts et j'ai continué, mais plus j'y pense, plus je veux revenir en arrière et faire ce script.

Seule la règle est qu'il doit être dans la ligne de commande Windows avec les commandes natives de XP. Pas de programmes tiers, pas de python, pas de perl, rien que la ligne de commande Windows qui peut être exécutée à partir d'un fichier .bat.

+1

* Aucun programme tiers * Oh boy, est-ce que je peux utiliser des commandes Linux comme 'awk'? http://gnuwin32.sourceforge.net/packages/gawk.htm – Pigueiras

Répondre

0

Cela va être maladroit, mais si le nombre de clés à remplacer est limitée, ce qui suit pourrait être suffisant pour vous:

@echo off 

set infile=foo.ini 
set outfile=newfile.ini 

rem create an empty output file 
echo. > %outfile% 

rem iterate over all properties 
for /f " usebackq eol=# tokens=1,2 delims== " %%i in ("%infile%") do (
    call :replace %%i %%j 
) 

rem terminate batch file 
goto :eof 

rem sub-program to do the replacing 
:replace 

    if "%1"=="Username" (
    echo Username=Timmy>>%outfile% 
    goto :eof 
) 

    if "%1"=="Servername" (
    echo Servername=HAL2001>>%outfile% 
    goto :eof 
) 
    echo %1=%2>>%outfile% 

    rem terminate sub-program 
    goto :eof 
+0

Clumsy fonctionnera parfaitement. Je vais tester cela et vous laisser savoir comment cela fonctionne. Le programme est pour un désordre délicieux qui utilise Microsoft Access ... Sauf que les enregistrements sont tous stockés dans une base de données SQL ... et le programme les convertit de SQL à Access ... Et annonce toujours comme étant basé sur SQL. .. Pas pertinent à cela, mais je pense que toute personne ayant des connaissances SQL sait à quel point c'est horrible. Il y a environ 2-4 variables à changer, donc oui - la portée est très limitée. –

+0

Je suis en train d'essayer de corriger cela moi-même, puisque vous m'avez donné beaucoup de travail, mais pour le moment le script ne fait que renvoyer 3 lignes du .ini qui n'ont rien à voir avec ce que je veux, et c'est ne change pas ce que je veux changer. Il vaut la peine d'expliquer plus loin que je ne veux pas changer la valeur du nom d'utilisateur seulement quand c'est Tommy. Je veux changer la valeur de Username CHAQUE FOIS. Idem pour Servername, pour continuer à utiliser l'exemple. Je veux changer le nom de serveur en HAL2001 quelle que soit sa valeur au moment où le script s'exécute. De plus, il n'enregistre pas les changements qu'il fait (le cas échéant). –

+0

@incongruouserudite Pour changer le nom du serveur et le nom d'utilisateur * tout * l'heure, il suffit de supprimer le si imbriqué (voir ma modification). Les modifications sont enregistrées dans un fichier différent (c'est la partie 'outfile = newfile.ini'). Vous ne pouvez pas écraser le fichier que vous lisez. –

1

La solution a_horse_with_no_name fonctionne parfaitement à vos besoins. Il peut être rendu un peu moins maladroit et plus efficace en éliminant les appels. La commande MOVE est utilisée pour écraser le fichier original avec les nouvelles données. Ce qui précède suppose que chaque ligne du fichier correspond au format que vous avez indiqué. Mais souvent, un fichier .INI contient également des lignes de commentaire qui ne respectent pas le format à conserver. La solution de boucle FOR peut être étendue pour supporter cela, mais elle devient encore plus compliquée, et plus lente.

Vous n'avez jamais déclaré explicitement que l'ordre des lignes est important - souvent l'ordre des lignes dans un fichier .INI n'est pas important. Voici une solution très simple qui utilise FINDSTR pour supprimer les lignes Username et Servername existantes, puis ajoute les nouvelles valeurs à la fin. Toutes les lignes non modifiées seront conservées, quel que soit le format. Les lignes modifiées apparaissent toujours à la fin.

@echo off 
>"test.ini.new" (
    findstr /v "^Username= ^Servername=" "test.ini" 
    echo Username=Timmy 
    echo Servername=HAL2001 
) 
move /y "test.ini.new" "test.new" 

Batch est vraiment une plate-forme médiocre pour le traitement de fichiers texte. C'est souvent lent et trop compliqué. Il semble que votre fichier est petit et vos besoins sont relativement simples. Mais beaucoup de demandes apparemment simples sont une bête à faire en pur lot. JScript est beaucoup mieux pour le traitement du texte, et il est natif de XP et au-delà. Il a un support complet de regex. J'ai écrit un script hybride batch/utilitaire JScript qui peut être utilisé pour effectuer des opérations de recherche et de remplacement sur le contenu des fichiers texte. C'est très rapide, puissant et simple à utiliser.Une solution à votre problème est mis en œuvre:

@echo off 
type "test.ini" | repl "^Username=.*$" "Username=Timmy" | repl "^Servername=.*$" "Servername=HAL2001" >"test.ini.new" 
move /y "test.ini.new" "test.new" 

ou un peu plus concise:

@echo off 
type "test.ini" | repl "^(Username=).*$" "$1Timmy" | repl "^(Servername=).*$" "$1=HAL2001" >"test.ini.new" 
move /y "test.ini.new" "test.new" 

est Ci-dessous le script utilitaire REPL.BAT. La documentation complète est intégrée dans le script. Vous pouvez également accéder à la documentation à partir de l'invite de commande en tapant REPL /?. Le script doit être dans votre répertoire actuel, ou ailleurs dans votre PATH.

@if (@X)==(@Y) @end /* Harmless hybrid line that begins a JScript comment 

::************ Documentation *********** 
::: 
:::REPL Search Replace [Options [SourceVar]] 
:::REPL /? 
::: 
::: Performs a global search and replace operation on each line of input from 
::: stdin and prints the result to stdout. 
::: 
::: Each parameter may be optionally enclosed by double quotes. The double 
::: quotes are not considered part of the argument. The quotes are required 
::: if the parameter contains a batch token delimiter like space, tab, comma, 
::: semicolon. The quotes should also be used if the argument contains a 
::: batch special character like &, |, etc. so that the special character 
::: does not need to be escaped with ^. 
::: 
::: If called with a single argument of /? then prints help documentation 
::: to stdout. 
::: 
::: Search - By default this is a case sensitive JScript (ECMA) regular 
:::   expression expressed as a string. 
::: 
:::   JScript syntax documentation is available at 
:::   http://msdn.microsoft.com/en-us/library/ae5bf541(v=vs.80).aspx 
::: 
::: Replace - By default this is the string to be used as a replacement for 
:::   each found search expression. Full support is provided for 
:::   substituion patterns available to the JScript replace method. 
:::   A $ literal can be escaped as $$. An empty replacement string 
:::   must be represented as "". 
::: 
:::   Replace substitution pattern syntax is documented at 
:::   http://msdn.microsoft.com/en-US/library/efy6s3e6(v=vs.80).aspx 
::: 
::: Options - An optional string of characters used to alter the behavior 
:::   of REPL. The option characters are case insensitive, and may 
:::   appear in any order. 
::: 
:::   I - Makes the search case-insensitive. 
::: 
:::   L - The Search is treated as a string literal instead of a 
:::    regular expression. Also, all $ found in Replace are 
:::    treated as $ literals. 
::: 
:::   E - Search and Replace represent the name of environment 
:::    variables that contain the respective values. An undefined 
:::    variable is treated as an empty string. 
::: 
:::   M - Multi-line mode. The entire contents of stdin is read and 
:::    processed in one pass instead of line by line.^anchors 
:::    the beginning of a line and $ anchors the end of a line. 
::: 
:::   X - Enables extended substitution pattern syntax with support 
:::    for the following escape sequences: 
::: 
:::    \\  - Backslash 
:::    \b  - Backspace 
:::    \f  - Formfeed 
:::    \n  - Newline 
:::    \r  - Carriage Return 
:::    \t  - Horizontal Tab 
:::    \v  - Vertical Tab 
:::    \xnn - Ascii (Latin 1) character expressed as 2 hex digits 
:::    \unnnn - Unicode character expressed as 4 hex digits 
::: 
:::    Escape sequences are supported even when the L option is used. 
::: 
:::   S - The source is read from an environment variable instead of 
:::    from stdin. The name of the source environment variable is 
:::    specified in the next argument after the option string. 
::: 

::************ Batch portion *********** 
@echo off 
if .%2 equ . (
    if "%~1" equ "/?" (
    findstr "^:::" "%~f0" | cscript //E:JScript //nologo "%~f0" "^:::" "" 
    exit /b 0 
) else (
    call :err "Insufficient arguments" 
    exit /b 1 
) 
) 
echo(%~3|findstr /i "[^SMILEX]" >nul && (
    call :err "Invalid option(s)" 
    exit /b 1 
) 
cscript //E:JScript //nologo "%~f0" %* 
exit /b 0 

:err 
>&2 echo ERROR: %~1. Use REPL /? to get help. 
exit /b 

************* JScript portion **********/ 
var env=WScript.CreateObject("WScript.Shell").Environment("Process"); 
var args=WScript.Arguments; 
var search=args.Item(0); 
var replace=args.Item(1); 
var options="g"; 
if (args.length>2) { 
    options+=args.Item(2).toLowerCase(); 
} 
var multi=(options.indexOf("m")>=0); 
var srcVar=(options.indexOf("s")>=0); 
if (srcVar) { 
    options=options.replace(/s/g,""); 
} 
if (options.indexOf("e")>=0) { 
    options=options.replace(/e/g,""); 
    search=env(search); 
    replace=env(replace); 
} 
if (options.indexOf("l")>=0) { 
    options=options.replace(/l/g,""); 
    search=search.replace(/([.^$*+?()[{\\|])/g,"\\$1"); 
    replace=replace.replace(/\$/g,"$$$$"); 
} 
if (options.indexOf("x")>=0) { 
    options=options.replace(/x/g,""); 
    replace=replace.replace(/\\\\/g,"\\B"); 
    replace=replace.replace(/\\b/g,"\b"); 
    replace=replace.replace(/\\f/g,"\f"); 
    replace=replace.replace(/\\n/g,"\n"); 
    replace=replace.replace(/\\r/g,"\r"); 
    replace=replace.replace(/\\t/g,"\t"); 
    replace=replace.replace(/\\v/g,"\v"); 
    replace=replace.replace(/\\x[0-9a-fA-F]{2}|\\u[0-9a-fA-F]{4}/g, 
    function($0,$1,$2){ 
     return String.fromCharCode(parseInt("0x"+$0.substring(2))); 
    } 
); 
    replace=replace.replace(/\\B/g,"\\"); 
} 
var search=new RegExp(search,options); 

if (srcVar) { 
    WScript.Stdout.Write(env(args.Item(3)).replace(search,replace)); 
} else { 
    while (!WScript.StdIn.AtEndOfStream) { 
    if (multi) { 
     WScript.Stdout.Write(WScript.StdIn.ReadAll().replace(search,replace)); 
    } else { 
     WScript.Stdout.WriteLine(WScript.StdIn.ReadLine().replace(search,replace)); 
    } 
    } 
} 
Questions connexes