简体   繁体   中英

Windows Batch file to read a file and replace a string

The file below contains 3 purchase orders. The first line of each purchase order starts with a constant record identifier (which I put in BOLD letters):

850 00000000000

I need to search for string " MC66F " in each PO (which is italicized and in BOLD). IF the string is found, then I need to replace the customer ID. However, the customer ID "ABC00" is in ALL purchases orders. The issue is I need to update the customer ID in ONLY that purchase order containing the string "MC66F" with "ABC04".

850 00000000000 123ABC45 PO ABC00 79 79 1 000056
850 BEG002000009 22123ABC45 20160909
850 CUR004000009 EUR
850 REF00400000CR MC66F
850 DTM0170000013720160909
850 DTM0170000063 20160915
850 DTM0170000064 20160909
850 N1 04700000BY 5450534000031
850 N1 04700000SU 0728658000004
850 N1 04700000DP 5450534002370
850 N1 04700000IV 5450534005821
850 PO1083000001 0000000000000002EA200000000000009118 SAB00D9KR6T6
850 PO1083000002 0000000000000012EA200000000000015294 SAB0058FAEAS
850 00000000000 456ABC45 PO ABC00 18132 18132 1 000056
850 BEG002000009 22456ABC45 20160909
850 CUR004000009 EUR
850 DTM0170000013720160909
850 DTM0170000063 20160914
850 DTM0170000064 20160909
850 N1 04700000BY 5450534000017
850 N1 04700000SU 608030938
850 N1 04700000DP 0534002349
850 N1 04700000IV 5450534005838
850 PO1083000001 0000000000000001EA200000000000010518 SAB00MYD9UP2
850 00000000000 789ABC45 PO ABC00 18133 18133 1 000056
850 BEG002000009 22789ABC45 20160909
850 CUR004000009 EUR
850 DTM0170000013720160909
850 DTM0170000063 20160914
850 DTM0170000064 20160909
850 N1 04700000BY 5450534000017
850 N1 04700000SU 608030938
850 N1 04700000DP 0534002332
850 N1 04700000IV 5450534005838
850 PO1083000001 0000000000000002EA200000000000010518 SAB00MYD9UP2

Some things I have tried based on research (and how I would do it in UNIX) is store the lines in an array and then do an IF condition searching for the string in the array. All I can do is get the array, but couldn't figure out how to utilize the IF condition.

    @echo off &setlocal enabledelayedexpansion
    for /F "delims=" %%a in (%INPUTFILE%) do (
      set /A count+=1
      set "array[!count!]=%%a"
    )
    for /L %%i in (1,1,%count%) do echo !array[%%i]! >> %OUTPUTFILE%

Any help would be greatly appreciated!

EDITS:

for %%b in (inputfile.*) do (

rem Process all input lines and create output file

(
setlocal EnableDelayedExpansion

for /F "delims=" %%a in (%%~fb) do (
   set "line=%%a"
   rem If new order starts
   if "!line:~0,17!" equ "850   00000000000" (
      rem show the previous one
      if defined firstLine (
         echo !firstLine!
         type thisOrder.txt
         del thisOrder.txt
     )
      rem and start new order
      set "firstLine=%%a"
   ) else (
      rem If the string was found in this order
      if "!line:%search%=!" neq "%%a" (
         rem replace the customer ID in first line
         set "firstLine=!firstLine:%old%=%new%!"
   )    
      rem Save this line in this order
      echo %%a>> thisOrder.txt
   )

)
echo !firstLine!
type thisOrder.txt
del thisOrder.txt

) > output.%RANDOM%.txt

)

I tried to generate a random number within the file since I thought it was just overwriting the file with each new iteration. But this didn't work. Seems it is outputting an extra "850 00000000000" segment at the beginning of the next PO.

I finally managed to do it.

Steps:

  • Reading the input file contents and storing it as environment variables (like you did it). It's one line per variable. The difference is that it uses some extra logic: it treats the file not like a sequence of lines, but as a sequence of orders, and each order is a sequence of records (a record is a line). So, the ( _ARRAY ) env var is 2 dimensional ( 0 based indexes):

    • 1st index is the order
    • 2nd index is the record (line) index inside the order

    The _OLENGTHS variable contains the last indexes (length - 1) of each order ( 12 10 10 in our case), and it's used in the next phase.

  • Processing the previously created env vars. It saves each order header (2nd index 0 ) into another var to be altered later. Then it iterates trough the rest of that order's records, searching for the customer. If it finds it, it replaces it in the header. For string/pattern matching I used findstr /? .

    Note : I had to use labels because some nested variables expansion; this is the only way I could do it.

  • Creating the output file and dumping the modified env vars content into it.

Notes:

  • The code might be highly inefficient. I didn't spend any time optimizing it.
  • It relies on the fact that each order starts with 850 00000000000 .
  • It also relies on the fact that the customer record has the following format: 850 REF followed by a sequence of digits and letters(upper + lower case), then a space and then the customer name.
  • It relies that in the order record, the customer (that needs to be replaced) is the 4th token.

Here's the code:

@echo off
setlocal enabledelayedexpansion
set _INPUTFILE=in.txt
set _OUTPUTFILE=out.txt
set _OIDX=-1
set _RIDX=0
set _OLENGTHS=
set _RECORD=
set _HEADER=
set _IDMARK=850 00000000000
set _REFMARK=850 REF
set _CUSTOMER=MC66F
set _NEWCUSTOMER=ABC04

:: Read the inoput file contents and store it in env vars
for /f "tokens=*" %%f in (%_INPUTFILE%) do (
    echo %%f | findstr /b /c:"%_IDMARK%" 1>nul
    if !errorlevel! equ 0 (
        set /a _OIDX+=1
        if "!_RECORD!" neq "" (
            set _OLENGTHS=!_OLENGTHS! !_RIDX!
        ) else (
            set _RECORD=_
        )
        set /a _RIDX=0
    ) else (
        set /a _RIDX+=1
    )
    set _ARRAY[!_OIDX!][!_RIDX!]=%%f
)
set _OLENGTHS=%_OLENGTHS% %_RIDX%

:: Do some processing on the env vars
set /a _OIDX=-1
for %%f in (%_OLENGTHS%) do (
    set /a _OIDX+=1
    call :set_header_var !_OIDX!
    for /l %%g in (1,1,%%f) do (
        call :process_record !_OIDX! %%g
    )
)

:: Dump all the env vars contents into the output file
copy nul %_OUTPUTFILE%
set /a _OIDX=-1
for %%f in (%_OLENGTHS%) do (
    set /a _OIDX+=1
    call :set_header_var !_OIDX!
    for /l %%g in (0,1,%%f) do (
        call :dump_var !_OIDX! %%g
    )
)

goto :eof

:process_record
    echo !_ARRAY[%1][%2]! | findstr /r /c:"^%_REFMARK%[0-9A-Za-z]* %_CUSTOMER%" 1>nul
    if !errorlevel! equ 0 (
        call :alter_header %1
    )
    goto :eof

:set_header_var
    set _HEADER=!_ARRAY[%1][0]!
    goto :eof

:alter_header
    for /f "tokens=1,2,3,4,*" %%h in ('echo !_HEADER!') do (
        set _ARRAY[%1][0]=%%h %%i %%j %_NEWCUSTOMER% %%l
    )
    goto :eof

:dump_var
    echo !_ARRAY[%1][%2]!>>%_OUTPUTFILE%
    goto :eof

After running it, the output file looks like the input file except for the 1st line which becomes:

850 00000000000123ABC45 PO ABC04 79 79 1 000056

@EDIT0 : I didn't carefully read the requirement, i was replacing the customer ( ABC00 ) by the string to search for ( MC66F ), instead of the new customer ( ABC04 ). Corrected.

@echo off
setlocal EnableDelayedExpansion

set "firstLine="
del thisOrder.txt 2>NUL

rem Process all input lines and create output file
(
for /F "delims=" %%a in (input.txt) do (
   set "line=%%a"
   rem If new order starts
   if "!line:~0,15!" equ "850 00000000000" (
      rem show the previous one
      if defined firstLine (
         echo !firstLine!
         type thisOrder.txt
         del thisOrder.txt
      )
      rem and start new order
      set "firstLine=%%a"
   ) else (
      rem If the search string was found in this order
      if "!line:MC66F=!" neq "%%a" (
         rem replace the customer ID in first line
         set "firstLine=!firstLine:ABC00=ABC04!"
      )
      rem Save this line in this order
      echo %%a>> thisOrder.txt
   )
)
echo !firstLine!
type thisOrder.txt
del thisOrder.txt
) > output.txt

All working parameters are written literally in order to make the code clearer, but you may specify their values in variables or in parameters. For example, you may start the code with these lines:

set "search=MC66F"
set "old=ABC00"
set "new=ABC04"

... and then adjust the corresponding lines in the code; for example:

rem If the search string was found in this order
if "!line:%search%=!" neq "%%a" (
   rem replace the customer ID in first line
   set "firstLine=!firstLine:%old%=%new%!"
)

try something like this

powershell -command "(gc -Delimiter %delim% %file%)
     -replace %regex0%, %replace0% 
     .....
     -replace %regexn%, %replacen%
     -join %delim% | Set-Content %newfile%"

*newline just for display

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM