Пакетный файл Windows - найти строку в файле и добавить к нему - PullRequest
0 голосов
/ 04 декабря 2018

Так что я ОЧЕНЬ близко подошел к тому, чего хотел достичь, но, похоже, я не смог пройти те пункты, на которых я застрял.

Во-первых, у меня есть файл .ini сзаголовок вместе со списком элементов, разделенных запятой:

[Items]
ListOfItems = Item01, Item02, Item03

Что мне нужно сделать, это найти этот конкретный раздел ( ListOfItems ) и добавить к нему новый элемент, который не 't существует в справочном файле.

Вот что у меня есть до сих пор:

SET /P NewItem=
SET ListOfItems=
FOR /F delims^=^"^ tokens^=2 %%G IN ('type File.ini') DO  (set ListOfItems=%%G)
echo ListOfItems ^=^"%ListOfItems%, %NewItem%^">File.ini

Проблема, с которой я столкнулся, заключается в том, что вместо приглашения я использую подсказкуновая строка из файла ( List.txt ), потому что в прошлый раз, когда я пытался, он повторял одну и ту же строку снова и снова, вместо того, чтобы брать только одну из каждой строки, при условии, что она не является дубликатом.

Другая проблема, с которой я столкнулся, заключается в том, что при использовании приведенного выше кода он не сохраняет текущее состояние файла .ini.Таким образом, вы можете получить пример, как указано выше, но как только вы добавите новый элемент через приглашение, вы получите:

ListOfItems =", Item04"

Заголовок пропал, исходные значения пропали, и он начинается сзапятая, вместо добавления запятой после каждого значения.

Таким образом, ожидаемый результат был:

[Items]
ListOfItems = Item01, Item02, Item03, Item04

Почему он не сохраняет исходные данные при первом запуске, но последующие запуски идеально копируют исходные данные и добавляют к ним?

Как мы можем решить проблему с запятой, без предшествующих данных перед ней?

Как можноу нас есть список элементов для добавления из List.txt вместо того, чтобы вручную вводить их по одному?

РЕДАКТИРОВАТЬ: Мне удалось обратиться2-й выпуск со следующим:

(
    ECHO [Items]
    ECHO !ArchiveList! = !RequiredItems!
)>>File.ini

Где !RequiredItems! содержат базовые элементы Item01, Item02, Item03, которые я делаю, когда ListOfItems не заполнены, согласно предложению Джона Кенса.

Что касается третьего вопроса, я решил его следующим образом:

FOR /F "delims=" %%A IN (List.txt) DO (
    SET "ListAddition=!ListAddition!%%A, "
)
SET "ListAddition=!ListAddition:~0,-2!"

Какой тактивирует каждый элемент в файле List.txt , который находится в новой строке, а затем добавляет его в список, каждый элемент разделяется запятой.

1 Ответ

0 голосов
/ 04 декабря 2018

Хорошо, обо всем по порядку, ваш текущий for loop нуждается в некоторой настройке;Есть несколько вещей, которые мы можем сделать здесь.Если мы просто хотим получить то, что ListOfItems равно из второй строки, и игнорировать тот факт, что в этом файле могут быть другие объекты, то правильный способ получить эти данные будет следующим:

FOR /F "skip=1 tokens=2,*" %%A IN ('type File.ini') DO (set "ListOfItems=%%B")
  • skip=1 - Пропустит первую строку
  • tokens=2,* - Наличие 2, * вызовет%%A будет первыми двумя объектами, а %%B будет всем после 2.

Однако, чтобы быть более точным, правильным будет использование find /i для поиска ListOfItems объект в файле .ini.Давайте возьмем следующий текстовый файл ниже:

[Items]
ListOfItems = Item01, Item02, Item03
[Armor]
ListOfArmor = Iron Helmet, Iron Brestplate, Iron Pants
[Weapons]
ListOfWeapons = Sword, Dagger, Holy Water

Если бы мы использовали этот базовый цикл for в этом операторе, мы получили бы последнюю строку текстового файла:

Sword, Dagger, Holy Water

Сильфон используетоператор find вместе с другим циклом, мы можем объединить их для извлечения данных только после ListOfItems из всего документа.

Rem | Get .ini To String
FOR /F "tokens=*" %%A IN ('type File.ini') DO (

    Rem | Look For Line With Items "ListOfItems"
    FOR /F "tokens=2,*" %%B IN ('echo %%A^| find /i "ListOfItems"') DO (

        Rem | Echo Result
        echo %%C
    )
)

Однако для исправления только этой одной строки и добавленияновый объект до конца, это где это становится сложно!Имейте в виду, что пакет является примитивным и теряет поддержку, его ограничения хорошо ограничены по сравнению с его преемником PowerShell.В необработанном пакете нет настоящей команды для редактирования только одной строки в середине документа.Однако это не делает невозможным.

Чтобы обойти это, нам нужно будет взять весь файл .ini и использовать команду type, чтобы разбить документ построчно и сохранить его какстрока.Оттуда мы можем использовать syntax-replace , чтобы редактировать строку и сохранить ее в новом документе.Оттуда мы просто удаляем и переименовываем.

Для дальнейшего изучения этого вопроса нам нужно проверить, действительно ли заполнено ListOfItems.Базовое утверждение if exists будет отлично работать здесь.Теперь, поскольку в вашем утверждении в уравнении есть =, простой syntax-replace не будет работать без дальнейших осложнений.Из моего предыдущего редактирования я изменил простую функцию на скрипт, который мы тоже будем call.Этот скрипт будет называться Replace.bat.Все, что вам нужно сделать, это создать новый файл .bat и вставить его снизу.Этот файл никогда не потребуется изменять.

Ниже представлен весь проект, который должен решить все ваши проблемы:

Replace.Bat:

(Весь этотскрипт является эквивалентом одной 15-символьной команды в powershell lol!)

@echo off
setlocal EnableExtensions DisableDelayedExpansion

set "FILE_I=%~1"
set "SEARCH=%~2"
set "REPLAC=%~3"
set "FILE_O=%~4"
set "FLAG=%~5"
if not defined FILE_I exit /B 1
if not defined SEARCH exit /B 1
if not defined FILE_O set "FILE_O=con"
if defined FLAG set "FLAG=#"

for /F "delims=" %%L in ('
    findstr /N /R "^" "%FILE_I%" ^& break ^> "%FILE_O%"
') do (
    set "STRING=%%L"
    setlocal EnableDelayedExpansion
    set "STRING=!STRING:*:=!"
    call :REPL RETURN STRING SEARCH REPLAC %FLAG%
    >> "%FILE_O%" echo(!RETURN!
    endlocal
)

endlocal
exit /B


:REPL  rtn_string  ref_string  ref_search  ref_replac  flag
setlocal EnableDelayedExpansion
set "STR=!%~2!"
set "SCH=!%~3!"
set "RPL=!%~4!"
if not defined SCH endlocal & set "%~1=" & exit /B 1
set "SCH_CHR=!SCH:~,1!"
if not "%~5"=="" set "SCH_CHR="
if "!SCH_CHR!"=="=" set "SCH_CHR=" & rem = terminates search string
if "!SCH_CHR!"==""^" set "SCH_CHR=" & rem " could derange syntax
if "!SCH_CHR!"=="%%" set "SCH_CHR=" & rem % ends variable expansion
if "!SCH_CHR!"=="^!" set "SCH_CHR=" & rem ! ends variable expansion
call :LEN SCH_LEN SCH
call :LEN RPL_LEN RPL
set /A RED_LEN=SCH_LEN-1
set "RES="
:LOOP
call :LEN STR_LEN STR
if not defined STR goto :END
if defined SCH_CHR (
    set "WRK=!STR:*%SCH_CHR%=!"
    if "!WRK!"=="!STR!" (
        set "RES=!RES!!STR!"
        set "STR="
    ) else (
        call :LEN WRK_LEN WRK
        set /A DFF_LEN=STR_LEN-WRK_LEN-1,INC_LEN=DFF_LEN+1,MOR_LEN=DFF_LEN+SCH_LEN
        for /F "tokens=1,2,3 delims=," %%M in ("!DFF_LEN!,!INC_LEN!,!MOR_LEN!") do (
            rem set "RES=!RES!!STR:~,%%M!"
            if defined WRK set "WRK=!WRK:~,%RED_LEN%!"
            if "!STR:~%%M,1!!WRK!"=="!SCH!" (
                set "RES=!RES!!STR:~,%%M!!RPL!"
                set "STR=!STR:~%%O!"
            ) else (
                set "RES=!RES!!STR:~,%%N!"
                set "STR=!STR:~%%N!"
            )
        )
    )
) else (
    if "!STR:~,%SCH_LEN%!"=="!SCH!" (
        set "RES=!RES!!RPL!"
        set "STR=!STR:~%SCH_LEN%!"
    ) else (
        set "RES=!RES!!STR:~,1!"
        set "STR=!STR:~1!"
    )
)
goto :LOOP
:END
if defined RES (
    for /F delims^=^ eol^= %%S in ("!RES!") do (
        endlocal
        set "%~1=%%S"
    )
) else endlocal & set "%~1="
exit /B


:LEN  rtn_length  ref_string
setlocal EnableDelayedExpansion
set "STR=!%~2!"
if not defined STR (set /A LEN=0) else (set /A LEN=1)
for %%L in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
    if defined STR (
        set "INT=!STR:~%%L!"
        if not "!INT!"=="" set /A LEN+=%%L & set "STR=!INT!"
    )
)
endlocal & set "%~1=%LEN%"
exit /B

Main.Bat:

@ECHO OFF
@setlocal EnableDelayedExpansion

Rem | Configuration
set "CustomINI=File.ini"
set "ListHeader=[Items]"
set "Object=ListOfItems"
set "ReplaceScript=Replace.bat"
SET "ItemList=List.txt"

Rem | Check If "CustomINI" Exists
if not exist "%CustomINI%" (
    echo File "%CustomINI%" Not Found!
    pause
    goto :EOF
)

Rem | Check If "ItemList" Exists
if not exist "%ItemList%" (
    echo File "%ItemList%" Not Found!
    pause
    goto :EOF
)
goto StartFunction

:StartFunction

Rem | Generate the list of items from textfile
FOR /F "delims=" %%A IN (%ItemList%) DO (
    set "ListAddition=!ListAddition!%%A, "
)
set "ListAddition=!ListAddition:~0,-2!"

Rem | Get .ini To String
set HeaderFound=false
FOR /F "tokens=*" %%A IN ('type !CustomINI!') DO (

    Rem | First Find The Header "[Items]" & Extract "ListOfItems" Line Data
    for /f "tokens=*" %%B in ('echo %%A') do (
        set "item=%%B"
        if /i "!item!"=="!ListHeader!" (
            set HeaderFound=true
        ) else if not "!item!"=="!item:ListOfItems=!" if "!HeaderFound!"=="true" (

            Rem | Turn Items For Line "ListOfItems" To String
            for /f "tokens=2,*" %%C in ('echo %%B') do (

               Rem | Set String
               set "SEARCHTEXT=%%D"
            )
            set HeaderFound=false

        )
    )
)

Rem | Check If "ListOfItems" Is Actually Populated
If "%SEARCHTEXT%"=="" (
    Rem | Not Populated
    set "SEARCHTEXT=!Object! = "
    set "REPLACETEXT=!Object! = !ListAddition!"
    goto EditString
) ELSE (
    Rem | Populated
    set "REPLACETEXT=!SEARCHTEXT!, !ListAddition!"
    goto EditString
)

:EditString

Rem | Edit Only "ListOfItems" Line
Rem | Usage: call "1" "2" "3" "4" "5"
Rem | call - Calls external script
Rem | "1" - Name of External script
Rem | "2" - File to Edit
Rem | "3" - Text to replace ex: red apple
Rem | "4" - Text to replace to ex: green apple
Rem | "5" - Output file
call "%ReplaceScript%" "%CustomINI%" "%SEARCHTEXT%" "%REPLACETEXT%" "%CustomINI%.TEMP"

Rem | Delete Original File, Restore New
del "%CustomINI%"
rename "%CustomINI%.TEMP" "%CustomINI%"

goto :EOF

PS - Обратите внимание на следующее: Приведенный выше скрипт ожидает, что, когда ListOfItems = не заполнен, он имеет пробел после =.Если это не так, как в вашем .ini файле, то измените set "SEARCHTEXT=!OBJECT! = " на set "SEARCHTEXT=!OBJECT! =" с оператора for.


РЕДАКТИРОВАТЬ: Со времени последних запросов, Было обновлено следующее:

Во-первых, так как я не был уверен в том, что ОП означает, что ListOfItems = означает «пустой», я предположил, что он / она ссылался на это ListOfItems =.- Не то, чтобы его на самом деле не хватало на ListHeader.В приведенном ниже примере.

My Vision - File.ini:

[Items]
ListOfItems = 
[Armor]
ListOfArmor = Iron Helmet, Iron Brestplate, Iron Pants
[Weapons]
ListOfWeapons = Sword, Dagger, Holy Water

Видение OP - File.ini

[Items]

[Armor]
ListOfArmor = Iron Helmet, Iron Brestplate, Iron Pants

[Weapons]
ListOfWeapons = Sword, Dagger, Holy Water

С тех пор я обновил скрипт, чтобы найти [Items] (String), а затем добавил новую строку под ним.Это было сделано с помощью сценария Magoo .

Поскольку заменять нечего, мы просто можем просто "Добавить" в .ini, таким образом, мы вызываем другую функцию.

:EditMissingString

Rem | Export SearchString
echo !ListHeader!>> %~dp0ListHeader.txt

Rem | Add Text Under %ListHeader%
(
    FOR /f "delims=" %%i IN (ListHeader.txt) DO (
    SET AddAfter=%%i

    FOR /f "delims=" %%n IN ('findstr /n "^" %CustomINI%') DO (
        SET line=%%n
        SET line=!line:*:=!

        ECHO(!line!
            IF "!line!"=="!AddAfter!" ECHO(%AddTEXT%
        )
    )
)>>%CustomINI%.TEMP

Rem | Remove ListHeader.txt
DEL %~dp0ListHeader.txt

Rem | Delete Original File, Restore New
DEL %CustomINI%
REN %CustomINI%.TEMP %CustomINI%

goto :EOF

Поскольку мы больше не редактируем ListOfArmor =, а добавляем его, нам больше не нужен сценарий Replace.bat .Я также исправил исходный сценарий, чтобы правильно зарезервировать пустые строки!

Новая функция замены W / H Сохранение строки.

:EditExistingString

REM | Make sure we only edit the ListOfItems line.
FOR /F "delims=" %%n IN ('findstr /n "^" %CustomINI%') DO (
    SET line=%%n
    SET Modified=!line:%SearchText%=%ReplaceText%!
    SET Modified=!Modified:*:=!

    REM | Output the entire edited INI to a temporary file.
    >> %CustomINI%.TEMP ECHO(!Modified!
)

Rem | Delete Original File, Restore New
DEL %CustomINI%
REN %CustomINI%.TEMP %CustomINI%

goto :EOF

Результат в:

[Items]
ListOfItems = Item1, Item2, Item3

[Armor]
ListOfArmor = Iron Helmet, Iron Brestplate, Iron Pants

[Weapons]
ListOfWeapons = Sword, Dagger, Holy Water

Результат:

[Items]
ListOfItems = Item1, Item2, Item3, Item4, Item5, Item6

[Armor]
ListOfArmor = Iron Helmet, Iron Brestplate, Iron Pants

[Weapons]
ListOfWeapons = Sword, Dagger, Holy Water

Окончательный Пакетный сценарий:

@ECHO OFF
@setlocal EnableDelayedExpansion

Rem | Configuration
set "CustomINI=File.ini"
set "ListHeader=[Items]"
set "Object=ListOfItems"
SET "ItemList=List.txt"

Rem | Check If "CustomINI" Exists
if not exist "%CustomINI%" (
    echo File "%CustomINI%" Not Found!
    pause
    goto :EOF
)

Rem | Check If "ItemList" Exists
if not exist "%ItemList%" (
    echo File "%ItemList%" Not Found!
    pause
    goto :EOF
)
goto StartFunction

:StartFunction

Rem | Generate the list of items from textfile
FOR /F "delims=" %%A IN (%ItemList%) DO (
    set "ListAddition=!ListAddition!%%A, "
)
if "%ListAddition%"=="" (
    echo ERROR: File "%ItemList%" Is Empty!
    pause
    goto :EOF
) ELSE (set "ListAddition=!ListAddition:~0,-2!")

Rem | Get .ini To String
set HeaderFound=false
FOR /F "tokens=*" %%A IN ('type !CustomINI!') DO (

    Rem | First Find The Header "[Items]" & Extract "ListOfItems" Line Data
    for /f "tokens=*" %%B in ('echo %%A') do (
        set "item=%%B"
        if /i "!item!"=="!ListHeader!" (
            set HeaderFound=true
        ) else if "!HeaderFound!"=="true" (

            Rem | Turn Items For Line "ListOfItems" To String
            for /f "tokens=2,*" %%C in ('echo %%B') do (

               Rem | Set String
               set "SearchText=%%D"
            )
            Rem | Header Was Found, End Loop & goto HeaderContinue
            set HeaderFound=false
            goto HeaderContinue
        )

    )
)
Rem | Header Was Not Found
echo ERROR: The Header "%ListHeader%" Was Not Found!
pause
goto :EOF

:HeaderContinue

Rem | Check If "ListOfItems" Is Actually Populated
If "%SearchText%"=="" (
    Rem | Not Populated
    set "SearchText=!ListHeader!"
    set "AddTEXT=!Object! = !ListAddition!"
    goto EditMissingString
) ELSE (
    Rem | Populated
    set "REPLACETEXT=!SearchText!, !ListAddition!"
    goto EditExistingString
)

:EditExistingString

REM | Make sure we only edit the ListOfItems line.
FOR /F "delims=" %%n IN ('findstr /n "^" %CustomINI%') DO (
    SET line=%%n
    SET Modified=!line:%SearchText%=%ReplaceText%!
    SET Modified=!Modified:*:=!

    REM | Output the entire edited INI to a temporary file.
    >> %CustomINI%.TEMP ECHO(!Modified!
)

Rem | Delete Original File, Restore New
DEL %CustomINI%
REN %CustomINI%.TEMP %CustomINI%

goto :EOF

:EditMissingString

Rem | Add Text Under %ListHeader%
(
    FOR /f "delims=" %%i IN ('Echo !ListHeader!') DO (
    SET AddAfter=%%i

    FOR /f "delims=" %%n IN ('findstr /n "^" %CustomINI%') DO (
        SET line=%%n
        SET line=!line:*:=!

        ECHO(!line!
            IF "!line!"=="!AddAfter!" ECHO(%AddTEXT%
        )
    )
)>>%CustomINI%.TEMP

Rem | Delete Original File, Restore New
DEL %CustomINI%
REN %CustomINI%.TEMP %CustomINI%

goto :EOF

PS: Я знаю, что ваша команда find находится в другом месте или что-то еще, просто измените команду find на %WINDIR%\System32\FIND.exe в сценарии.

ОТЛАДКА/ ИЗМЕНЕНИЯ:

Scraped.


Для получения справки по любой из команд выполните следующее:

  • call /?
  • set /?
  • for /?
  • if /?
  • find /?
  • и т. Д.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...