Найти и заменить строку, используя /f с оператором if и переменными

Я написал командный файл, который я хочу перезаписать строки ключей строк из другого файла.txt.

в настоящее время он прекрасно копирует новый файл File.txt, но не заменяет строки строками из файла OldFile.txt.

пример строк в файле File.txt:

...

# Пароль
Pword =

# Имя пользователя
Счет =

# TownName
Город =

# Почтовый индекс
Почтовый индекс =

# LocationChangedDate
LocationChanged =

Пример строки в файле OldFile.txt, которую я хочу заменить:

...

# Пароль
Pword = ABC

# Имя пользователя
Счет = 123

# TownName
Город = LDN

# Почтовый индекс
Индекс = WS77TP

# LocationChangedDate
LocationChanged = 01/01/2015

Может кто-нибудь указать мне правильное направление или объяснить, где я допустил ошибку?

@echo off

setlocal disableDelayedExpansion

::Variables
set InputFile=F:\EXCHANGE\3\Machine\File.txt
set OutputFile=F:\EXCHANGE\3\File-New.txt
set CopyFile=F:\EXCHANGE\3\OldMachine\OldFile.txt

set _strFindPword=Pword=.*
for /F "delims=" %%A in ('findstr /x "Pword=.*" %CopyFile%') do set _strInsertPword=%%A

echo.%_strInsertPword%

set _strFindAccount=Account=.*
for /F "delims=" %%B in ('findstr /x "Account=.*" %CopyFile%') do set _strInsertAccount=%%B

echo.%_strInsertAccount%

set _strFindTown=Town=.*
for /F "delims=" %%C in ('findstr /x "Town=.*" %CopyFile%') do set _strInsertTown=%%C

echo.%_strInsertTown%

set _strFindLocationChanged=LocationChanged=.*
for /F "delims=" %%D in ('findstr /x "LocationChanged=.*" %CopyFile%') do set _strInsertLocationChanged=%%D

echo.%_strInsertLocationChanged%

set _strFindPostcode=Postcode=.*
for /F "delims=" %%E in ('findstr /x "Postcode=.*" %CopyFile%') do set _strInsertPostcode=%%E

echo.%_strInsertPostcode%


(
  for /F "delims=" %%L in ('findstr /n "^" "%InputFile%"') do (
    set "line=%%L"
    setlocal EnableDelayedExpansion
    set "line=!line:*:=!"
    if "%%L" equ "_strFindPword" (echo.!_strInsertPword!) else (
       if "%%L" equ "%_strFindAccount%" (echo.!_strInsertAccount!) else (
          if "%%L" equ "%_strFindTown%" (echo.!_strInsertTown!) else (
             if "%%L" equ "%_strFindLocationChanged%" (echo.!_strInsertLocationChanged!) else (
                if "%%L" equ "%_strFindPostcode%" (echo.!_strInsertPostcode!) else (echo.!line!)
             )
          )
       )
    )
    endlocal
  )
) > "%OutputFile%"

del %InputFile% 

ren %OutputFile% File.txt



pause

4 ответа

Решение

Я думаю, что наконец-то понял...

Что оно делает:

  • Он просматривает содержимое OldFile.txt в поисках маркеров, если они найдены, они хранятся в переменных окружения, которые будут использоваться на этапе размещения (например, для _PWD маркер (переменная), которая имеет значение Pword=, это создаст _PWDCONTENTS переменная с содержанием Pword=ABC).
  • Он просматривает содержимое File.txt, ищет те же маркеры, если один маркер найден, соответствующий CONTENTS переменная выгружается в OutFile.txt, иначе исходная строка. Потому что это происходит во внутреннем for цикл, я должен был добавить дополнительную логику (_WROTE var) чтобы не писать одни и те же строки более одного раза.

Примечания:

  • Предполагается (ну, помимо того, что он должен делать) "настраиваемым" (код сложен, он движется в сторону мета:), если хотите), что означает, что если есть изменения между маркерами, код не должен меняться (ну, будут изменения кода, но не в функциональной части только в определениях переменных). Позвольте мне подробно:

    • Если вам больше не нужно заменять Town= строка, то все, что вам нужно сделать, это удалить _TOWN от _ALL: set _ALL=_PWD _ACCT _POST _LOC,
    • Обратное: если вы хотите добавить какой-либо другой тег (назовем его Name), вам нужно создать новую переменную окружения: set _NAME=Name= и добавить его в _ALL: set _ALL=_PWD _ACCT _TOWN _POST _LOC _NAME,
  • Как косвенное следствие, я не сосредоточился на производительности, поэтому она может работать медленно. В любом случае я пытался свести доступ к диску (который мучительно медленный) к минимуму (один пример, когда 2 for зацикливает ту, которая выполняет итерации содержимого файла - при условии, что каждая итерация требует доступа к диску; это может быть не так, и Win имеет буферизацию ввода-вывода - это внешняя).

  • Я "закомментировал" последнюю строку в файле, чтобы избежать перезаписи исходного файла. Если такое поведение необходимо, просто удалите rem в начале.

Вот код партии:

@echo off
setlocal enabledelayedexpansion

set _INFILE="File.txt"
set _OUTFILE="NewFile.txt"
set _OLDFILE="OldFile.txt"


set _PWD=Pword=
set _ACCT=Account=
set _TOWN=Town=
set _POST=Postcode=
set _LOC=LocationChanged=
set _ALL=_PWD _ACCT _TOWN _POST _LOC

echo Parsing old file contents...

for /f "tokens=*" %%f in ('type !_OLDFILE!') do (
    for %%g in (!_ALL!) do (
        echo %%f | findstr /b /c:!%%g! 1>nul
        if "!errorlevel!" equ "0" (
            set %%gCONTENTS=%%f
        )
    )
)

copy nul %_OUTFILE%
echo Merging the old file contents into the new file...
set _WROTE=0

for /f "tokens=*" %%f in ('findstr /n "^^" !_INFILE!') do (
    set _TMPVAR0=%%f
    set _TMPVAR0=!_TMPVAR0:*:=!
    for %%g in (!_ALL!) do (
        echo !_TMPVAR0! | findstr /b /c:!%%g! 1>nul
        if "!errorlevel!" equ "0" (
            echo.!%%gCONTENTS!>>!_OUTFILE!
            set _WROTE=1
        )
    )
    if "!_WROTE!" equ "0" (
        echo.!_TMPVAR0!>>!_OUTFILE!
    ) else (
        set _WROTE=0
    )
)

rem copy /-y %_OUTFILE% %_INFILE%

@ EDIT0: Используя предложение @StevoStephenson (как часть фрагмента вопроса), я заменил (2-й) внешний for цикл до ('findstr /n "^^" !_INFILE!') для того, чтобы включить пустые строки, поэтому третье замечание больше не применяется (удаление). Также были внесены некоторые небольшие изменения, чтобы разрешить файлам, которые содержат пробелы в своих путях.

Это не идеально, но это может быть хорошо для вас:

@Echo Off
Setlocal EnableExtensions DisableDelayedExpansion

(Set InputFile=F:\EXCHANGE\3\Machine\File.txt)
(Set OutputFile=F:\EXCHANGE\3\File-New.txt)
(Set CopyFile=F:\EXCHANGE\3\OldMachine\OldFile.txt)

For /F "Delims=" %%I In (
    'FindStr/B "Pword= Account= Town= LocationChanged= Postcode=" "%CopyFile%"'
    ) Do Set %%I

(For /F "Tokens=1-2* Delims=]=" %%I In ('Find /V /N ""^<"%InputFile%"') Do (
    Echo(%%J|FindStr/B # || (If Defined %%J (Call Echo=%%J=%%%%J%%) Else (
            If "%%J" NEq "" (Echo=%%J=%%K) Else (Echo=)))))>%OutputFile%
Timeout -1
EndLocal
Exit/B

Я оставил удаление и переименование для вас, чтобы добавить в конце.

Это решение должно быть намного быстрее, чем другие решения.
Это также сохранит пустые строки и строки, содержащие ! а также ^,

Нужен только один findstr призыв к сбору старых значений для всех слов.
Второй findstr определяет все строки (по номеру строки) в infile который нуждается в обновлении.

@echo off
setlocal EnableDelayedExpansion

set "_INFILE=File.txt"
set "_OUTFILE=NewFile.txt"
set "_OLDFILE="OldFile.txt"

set "_WORDS=Pword= Account= Town= Postcode= LocationChanged="

REM *** get all values for the key words
for /F "tokens=1,* delims==" %%L in ('findstr "!_WORDS!" "!_OLDFILE!"') do (
    for /F %%S in ("%%L") do (
        set "word[%%S]=%%M"
    )
)

REM *** Find all lines which needs an update
set wordIdx=0
for /F "tokens=1,2,* delims=:= " %%1 in ('findstr /n "!_WORDS!" "!_INFILE!"') do (
    set "lines[!wordIdx!].line=%%1"
    set "lines[!wordIdx!].word=%%2"
    set "replace=!word[%%2]!"
    set "lines[!wordIdx!].replace=!replace!"
    set /a wordIdx+=1
)

REM *** copy the infile to the outfile
REM *** Replace only the lines which are marked by line numbers
echo Parsing old file contents...
set nextWordIdx=0
set /a searchLine=lines[!nextWordIdx!].line
set lineNo=0
setlocal DisableDelayedExpansion
(
    for /f "tokens=*" %%L in ('findstr /n "^" "%_INFILE%"') do (
        set "line=%%L"
        set /a lineNo+=1
        setlocal EnableDelayedExpansion
        set "line=!line:*:=!"
        if !lineNo! equ !searchLine! (
            (echo(!line!!lines[0].replace!)
            set /a nextWordIdx+=1
            for /F %%R in ("!nextWordIdx!") do (
                endlocal
                set /a nextWordIdx=%%R
                set /a searchLine=lines[%%R].line
            )
        ) ELSE (
            (echo(!line!)
            endlocal
        )
    )
) > "!_OUTFILE!"

Может быть, это работает так

set CopyFile=oldfile.txt
set InputFile=newfile.txt

set str_search="Pword"
for /f "delims=" %%i in ('findstr %str_search% %copyfile%') do set str_replace=%%i
set str_replace="%str_replace%"
echo %str_search%
echo %str_replace%
pause

CALL :far %InputFile% %str_search% %str_replace%
EXIT /B 0

:far
setlocal enableextensions disabledelayedexpansion

set "search=%2"
set "replace=%3"
::remove quotes
set search=%search:"=%
set replace=%replace:"=%
echo %search%
echo %replace%

set "textFile=%1"

for /f "delims=" %%i in ('type "%textFile%" ^& break ^> "%textFile%" ') do (
    set "line=%%i"
    setlocal enabledelayedexpansion
    set "line=!line:%search%=%replace%!"
    >>"%textFile%" echo(!line!
    endlocal
)
EXIT /B 0

В for /f "delims=" %%i in ('findstr %str_search% %copyfile%') do set str_replace=%%i Вы пишете строку с переменной, которая имеет необходимую информацию, в str_replace. После этого ваша программа вызывает встроенную функцию поиска и замены (:far) который я без стеснения украл из пакетного скрипта, чтобы найти и заменить строку в текстовом файле, не создавая дополнительного выходного файла для хранения измененного файла. Эта функция находит строку "Pword" и заменяет ее строкой find в старом файле.

Внимание: это не решит вашу проблему полностью, так как ваш новый файл должен быть s.th вот так.

#Password
Pword

так что если вы потеряете = это работает иначе, это не так. Я надеюсь, что это поможет вам с вашей проблемой.

Другие вопросы по тегам