Пакетная замена и добавление 1 к числу между указанным текстом c в файле xml - PullRequest
0 голосов
/ 28 мая 2020

Хорошо, я пытаюсь написать пакет, чтобы «получить» серийный номер для пользователя. Серийный номер поступает из другой системы, и мы должны периодически вручную вводить серийный номер. Я хочу, чтобы пользователь запустил командный файл, который добавит 1 и заменит старый номер, а затем отобразит «Ваш серийный номер ##», где ## - новый номер. Я почти уверен, что смогу отработать часть Echo и запись во временный файл и перезапись старого файла, но это средняя часть, с которой я борюсь. Редактируемый файл xml:

<?xml version="1.0"?>
<BatchSettings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <SerialFormat>{0:yyMMdd}{1:00}</SerialFormat>
  <LastSerialDate>2020-05-27T00:00:00-05:00</LastSerialDate>
  <LastSerialNumber>10</LastSerialNumber>
</BatchSettings>

номер, который мне нужно изменить, - это ## между "lastserialnumber" <LastSerialNumber>10</LastSerialNumber>. Я пробовал несколько разных подходов, и этот первый близок, но точно не сигары.

@echo off
set /a "replace=10"
set /a "replaced=replace+1"

set "source=Test.txt"
set "target=Test2.txt"

setlocal enableDelayedExpansion
(
   for /F "tokens=1* delims=:" %%a in ('findstr /N "^" %source%') do (
      set "line=%%b"
      if defined line set "line=!line:%replace%=%replaced%!"
      echo(!line!
   )
) > %target%
endlocal

Проблема в том, что число не всегда будет «10», и я тоже хотите заменить все остальные экземпляры "10" в файле.

Я также пробовал

@Echo Off
Set "SrcFile=Test.txt"
Set "OutFile=Test2.txt"

@echo off 
    setlocal enableextensions disabledelayedexpansion 

    (for /f "delims=" %%a in (%SrcFile%
    ) do for /f "tokens=2 delims=1234567890 " %%b in ("%%~a"
    ) do if not "%%b"==":"  (
        echo(%%~a
    ) else for /f "tokens=1 delims=  <LastSerialNumber>:" %%c in ("%%~a"
    ) do (
        set /a "n=%%c+1"
        setlocal enabledelayedexpansion
        echo(<LastSerialNumber>!n!:
        endlocal
    )) > %OutFile%

, но это не увеличивает число и не обрезает оставшийся текст. а это просто беспорядок, который вообще не работает.

@Echo Off
Set "SrcFile=Test.txt"
Set "OutFile=Test2.txt"

setlocal EnableDelayedExpansion

(for /F "delims=" %%a in ('findstr /I /L "<LastSerialNumber>" %SrcFile%') do (
   set "line=%%a
   set "line=!line:*<LastSerialNumber>=!"
   for /F "delims=<" %%b in ("!line!") do echo %%b+1
)) > %oldnumber%

set "newnumber=%oldnumber%+1"

(
   for /F "tokens=1* delims=:" %%a in ('findstr /N "^" %SrcFile%') do (
      set "line=%%b"
      if defined line set "line=!line:%oldnumber%=%newnumber%!"
        echo(!line!
   )
) > %OutFile%
endlocal

Ответы [ 3 ]

0 голосов
/ 30 мая 2020

Хорошо, наконец-то у меня есть код, который работает так, как мне нужно. Просто нужно установить каталоги, которые я оставил здесь по понятным причинам. Большое спасибо aschipfl за помощь. Этот код индексирует серийный номер на единицу, записывает во временный файл, затем записывает обратно и удаляет временный файл. Затем отображается новый серийный номер.

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define constants here:
set "_FILE=BatchTest.xml" & rem // (input file; `%~1` is the first argument)
set "_TAG=<LastSerialNumber>" & rem // (opening tag)

rem // Loop over all (non-empty) lines of the input file:
(for /F "usebackq delims=" %%L in ("%_FILE%") do (
    rem // Store current line string:
    set "LINE=%%L"
    rem // Toggle delayed expansion to avoid troubles with `!`:
    setlocal EnableDelayedExpansion
    rem // Split off opening tag from line string:
    set "TEST=!LINE:*%_TAG%=!"
    rem // Check whether opening tag has been found:
    if not "!TEST!"=="!LINE!" (
        rem // Opening tag found, hence split off closing tag:
        for /F "tokens=1* eol=< delims=< " %%S in ("!TEST!") do (
            rem // Get extracted number and increment it:
            set /A "NUM=%%S+1"
        rem // Return rebuild line with incremented number:
            echo(  !_TAG!!NUM!^<%%T
        )
    ) else (
        rem // Opening tag not found, hence return original line:
        echo(!LINE!
    )
    endlocal
))>BatchTestTmp.xml 


copy /v /y "BatchTestTmp.xml" "BatchTest.xml" & cls

del "BatchTestTmp.xml"

set "LINE=4"
(for /f "usebackq delims=" %%a in (`more +%LINE% BatchTest.xml`) DO (
set result=%%a
setlocal EnableDelayedExpansion
echo Your Serial Number is !result:~20,2!
pause 
goto :leave
))
:leave
endlocal

exit /B
0 голосов
/ 26 июля 2020

Приятно видеть, что старая добрая Batch все еще может решить эту проблему. Очевидно, он не разработан с учетом поддержки XML, поэтому я бы порекомендовал инструмент, который изначально поддерживает его с помощью выражений XPath / XQuery, например .

«Часть ECHO» легко:

xidel -s input.xml -e "concat('Your serial number is ',//LastSerialNumber,' where ',//LastSerialNumber + 1,' is the new number')"
Your serial number is 10 where 11 is the new number

Редактирование немного сложнее:

xidel -s input.xml --xquery "serialize(transform(/,function($x){$x/(if (name()='LastSerialNumber') then <LastSerialNumber>{node() + 1}</LastSerialNumber> else .)}),{'omit-xml-declaration':false()})"
<?xml version="1.0" encoding="UTF-8"?>
<BatchSettings>
  <SerialFormat>{0:yyMMdd}{1:00}</SerialFormat>
  <LastSerialDate>2020-05-27T00:00:00-05:00</LastSerialDate>
  <LastSerialNumber>11</LastSerialNumber>
</BatchSettings>

- transform() повторяется по каждому элементу-узлу. Он оставляет каждый из них нетронутым, кроме случая, когда он находит <LastSerialNumber>. Затем он создает новый с исходным значением + 1. - serialize() «преобразовывает» вывод, а с {'omit-xml-declaration':false()} добавляет xml -объявление наверху.

serialize() просто выводит вывод (в стандартный вывод). Чтобы записать вывод в файл, используйте вместо него file:write():

xidel -s input.xml --xquery "file:write('input.xml',transform(/,function($x){$x/(if (name()='LastSerialNumber') then <LastSerialNumber>{node() + 1}</LastSerialNumber> else .)}),{'omit-xml-declaration':false()})"

Чтобы объединить это с «частью ECHO»:

xidel -s input.xml --xquery "concat('Your serial number is ',//LastSerialNumber,' where ',//LastSerialNumber + 1,' is the new number'),file:write('input.xml',transform(/,function($x){$x/(if (name()='LastSerialNumber') then <LastSerialNumber>{node() + 1}</LastSerialNumber> else .)}),{'omit-xml-declaration':false()})"

Предварительно заданный запрос (с необходимыми escape-символами):

xidel -s input.xml --xquery ^"^
  concat(^
    'Your serial number is ',^
    //LastSerialNumber,^
    ' where ',^
    //LastSerialNumber + 1,^
    ' is the new number'^
  ),^
  file:write(^
    'input.xml',^
    transform(^
      /,^
      function($x){^
        $x/(^
          if (name()='LastSerialNumber') then^
            ^<LastSerialNumber^>{node() + 1}^</LastSerialNumber^>^
          else^
            .^
        )^
      }^
    ),^
    {'omit-xml-declaration':false()}^
  )^
"

Это напечатает Your serial number is 10 where 11 is the new number и отредактирует (и перезапишет) 'input. xml' .

[редактировать] {'omit-xml-declaration':false()} работает только с Xidel 0.9.9 (начиная с xidel-0.9.9.20190104.6739.b64562007cb7). В Xidel 0.9.8 вы можете вручную добавить xml -объявление:

xidel -s input.xml --xquery "concat('Your serial number is ',//LastSerialNumber,' where ',//LastSerialNumber + 1,' is the new number'),file:write('input.xml',('<?xml version=\"1.0\" encoding=\"UTF-8\"?>',transform(/,function($x){$x/(if (name()='LastSerialNumber') then <LastSerialNumber>{node() + 1}</LastSerialNumber> else .)})))"

[/ edit]

0 голосов
/ 29 мая 2020

Сначала давайте кратко рассмотрим ваши попытки:

  1. терпит неудачу, так как он ищет только 10 и не учитывает теги <LastSerialNumber>.
  2. не удается, потому что вы пытаетесь определить строку для параметра delims= из for /F, хотя на самом деле он определяет набор символов.
  3. не удается, потому что вы пытаетесь перенаправить (>) в переменную, которая не может работать, можно только перенаправить на файлы. Кроме того, вы пытаетесь выполнить арифметику (+1), но это работает только при использовании set /A. И последний раздел имеет ту же проблему, что и подход 1.

Вот способ, которым вы могли бы это сделать:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define constants here:
set "_FILE=%~1" & rem // (input file; `%~1` is the first argument)
set "_TAG=<LastSerialNumber>" & rem // (opening tag)

rem // Loop over all (non-empty) lines of the input file:
for /F "usebackq delims=" %%L in ("%_FILE%") do (
    rem // Store current line string:
    set "LINE=%%L"
    rem // Toggle delayed expansion to avoid troubles with `!`:
    setlocal EnableDelayedExpansion
    rem // Split off opening tag from line string:
    set "TEST=!LINE:*%_TAG%=!"
    rem // Check whether opening tag has been found:
    if not "!TEST!"=="!LINE!" (
        rem // Opening tag found, hence split off closing tag:
        for /F "tokens=1* eol=< delims=< " %%S in ("!TEST!") do (
            rem // Get extracted number and increment it:
            set /A "NUM=%%S+1"
            rem // Return rebuild line with incremented number:
            echo(  !_TAG!!NUM!^<%%T
        )
    ) else (
        rem // Opening tag not found, hence return original line:
        echo(!LINE!
    )
    endlocal
)

endlocal
exit /B

Учитывая, что командный файл называется inc-serial.bat, вызовите его с входным XML файлом в качестве аргумента командной строки (скажем, test.xml в текущем каталоге):

inc-serial.bat "test.xml"

Для записи вывода в другой файл используйте перенаправление (> ):

inc-serial.bat "test.xml" > "test_NEW.xml"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...