Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

modify a value in a file with batch

Tags:

batch-file

Good evening everyone,

I do not know anything about a batch file but I need it to do something.

Let me explain: what I want to do is to open a file.c and browse for this line of code

#define var ((uint32_t) 1000)

what I want after is to change the value 1000 by a value enter previously by the user, except that this value will not always be 1000, precisely every time the user enter a value for example 250000,
I want it to be replaced in my .c file and then I need to replace 250000 of

#define var ((uint32_t) 250000)

by another variable again and again, so I would like my program to replace what has after define var ((uint32_t) and replace with what the user has entered I hope you have a little understanding of what I want to do.

 I add my program, but it does not do really work, do you have any ideas pleaaaase?

@echo off
setLocal EnableDelayedExpansion
SET "file=(C:\Users\astre\Desktop\test\fichier.c)"
SET /p NouvelVal= Entrez la fréquence que vous souhaitez entre 4Mhz et 24 MHz:
SET WordToSearch=#define HSE_VALUE((uint32_t)25000000)
SET Replacement=#define HSE_VALUE((uint32_t)%NouvelVal%)
  
for /f "tokens=* delims= " %%a in %file% do (
    set str=%%a
    set str=!str:%WordToSearch%=%Replacement%!
    echo !str!>>test2.txt
)
type test2.txt>fichier.c
del test2.txt
pause
like image 919
Yas Sou Avatar asked May 05 '26 23:05

Yas Sou


1 Answers

Modifying text files by batch scripts is not that trivial, particularly when it is a format that can easily be damaged when even only a single character becomes added/changed/removed, like (C) source code.

Nevertheless I had to try to manage it with pure batch scripting -- see the explanatory remarks in the code.

The main challenges are:

  • to find the line that contains the string of interest #define var ((uint32_t) 1000), which may contain multiple white-spaces (SPACEs and TABs) at almost any position, except in the keywords and the number, of course;
  • to extract the numeric value and maintain the remainder; for this I rely on the position of the (case-insensitive) keyword uint32_t relative to the number;

So here it is:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define constants here:
set "_FILE=C:\Users\astre\Desktop\test\fichier.c" & rem // (path or name of input file)
set "_TEMP=%TEMP%\%~n0_%RANDOM%.tmp"              & rem // (path or name of temporary file)

rem // Gather TAB character:
for /F "delims=" %%T in ('forfiles /P "%~dp0." /M "%~nx0" /C "cmd /C echo/0x09"') do set "_T=%%T"
rem // Build white-space classes for search expressions:
set "_S=[ %_T%]" & set "_S0=[ %_T%]*" & set "_S1=[ %_T%][ %_T%]*"
rem // Build digit classes for search expressions:
set "_D=[0123456789]" & set "_D0=[0123456789]*" & set "_D1=[0123456789][0123456789]*"
rem // Define expression (word) before number to replace:
set "_SPLIT=uint32_t"
rem // Build search expression for `findstr`:
set "_SEARCH=^%_S0%#define%_S1%var%_S1%(%_S0%(%_S0%%_SPLIT%%_S0%)%_S1%%_D1%%_S0%)%_S0%$"

rem // Write output to temporary file:
> "%_TEMP%" (
    set "FLAG="
    rem /* Read input file line by line, prefixed by current line number,
    rem    so lines appear non-empty to `for /F`, which would be ignored: */
    for /F "delims=" %%L in ('findstr /N "^" "%_FILE%"') do (
        set "LINE=%%L"
        setlocal EnableDelayedExpansion
        set "LINE=!LINE:*:=!"
        rem // Check whether current line (without line number prefix) matches search expression:
        cmd /V /C echo(^^!LINE^^!| > nul findstr /R /I /C:"!_SEARCH!" && (
            rem /* Current line matches, so split it into two parts at certain expression (word);
            rem    the second part is the one that holds the number to replace later: */
            set "AFTER=!LINE:*%_SPLIT%=!"
            set "BEFORE=!LINE!|" & for /F "delims=" %%K in ("%_SPLIT%!AFTER!|") do set "BEFORE=!BEFORE:%%K=!"
            rem /* Toggle delayed expansion to avoid troubles with exclamation marks `!`;
            rem    this requires `setlocal`/`endlocal` pairs, which localise environments;
            rem    hence use `for /F` to transport some values over `endlocal` barrier;
            rem    the temporary underscores `_` ensure none of the tokens appear empty: */
            for /F "tokens=1* delims=| eol=|" %%G in ("_!BEFORE!|!AFTER!_") do (
                rem // Split the second part at number:
                for /F "tokens=1* delims=0123456789 eol=0" %%I in ("_!AFTER!_") do (
                    endlocal
                    set "FLAG=#"
                    set "BEFORE=%%G" & set "AFTER=%%H"
                    set "LEFT=%%I" & set "RIGHT=%%J"
                    setlocal EnableDelayedExpansion
                    rem // Append the part before the number to the first part:
                    set "BEFORE=!BEFORE:~1!%_SPLIT%!LEFT:~1!"
                    rem // Extract the number from the second part:
                    set "NUMBER=_!AFTER!|" & set "NUMBER=!NUMBER:*%%I=!" & set "NUMBER=!NUMBER:%%J|=!"
                    rem // Store the part after the number as the new second part:
                    set "AFTER=!RIGHT:~,-1!"
                )
            )
            rem // Prompt user for new number (keep old one as per default):
            set "ENTRY=!NUMBER!" & > con set /P ENTRY="Number (!ENTRY!): " && (
                rem // Verify that entry is purely numeric (with white-spaces ignored):
                set "ENTRY=!ENTRY: =!" & set "ENTRY=!ENTRY:%_T%=!"
                (for /F "delims=0123456789 eol=0" %%K in ("!ENTRY!") do rem/) && >&2 (
                    echo Number entry is not purely numeric; keeping former value !NUMBER!.
                ) || set "NUMBER=!ENTRY!"
            )
            rem // Return line with the number exchanged:
            echo(!BEFORE!!NUMBER!!AFTER!
        ) || (
            rem // Current line does not match search expression, so return it unedited:
            echo(!LINE:*:=!
        )
        endlocal
    )
)
rem // Move temporary file onto input file, but only if a search expression has been found:
if defined FLAG (
    > nul move /Y "%_TEMP%" "%_FILE%"
) else del "%_TEMP%"

endlocal
exit /B

There are some restrictions that come from the Windows Command Prompt, cmd:

  • the input text file must be ASCII/ANSI-encoded; even if a Unicode file was read correctly, the output file would change encoding to ASCII/ANSI; if this is acceptable for you, you will have to play around with code pages (you could try with 437, 850, 1252, 65001, to mention some widespread ones; see the chcp command) to find the suitable conversion result;
  • the input text file must contain pure ASCII characters; characters with codes above 0x7F may become converted to something else, depending on the code page; if this is acceptable for you, you can again change the code page adequately;
  • the input text file must contain DOS/Windows-style line-breaks, that is, a carriage-return (CR, code 0x0D), followed by a line-feed (LF, code 0x0A), in short: CR+LF; files with Unix-style line-breaks (LF) are probably read correctly, but the output file will contain DOS/Windows-style line-breaks; files with MAC-style line-breaks (CR) cannot be processed;
  • the input text file must not contain any line that is longer than approximately 8190 characters;
like image 100
aschipfl Avatar answered May 09 '26 15:05

aschipfl