Как заменить разделитель сегментов или развернуть однострочник с помощью командной строки Windows?

наша система будет перенесена с Linux на машину с Windows, поэтому я готовлю пакетный файл, эквивалентный нашему существующему сценарию. Я уже создал пакетный файл, но мне нужно сначала развернуть файл перед обработкой следующей строки кода.

Пример. Вот однострочный, в котором разделителем является "{". Примечание. Разделитель может быть любым или переменным символом, кроме разделителя элементов (в данном случае "~").

ISA~00~          ~00~          ~ZZ~SAMSUNGSND     ~14~181087842      ~130214~2300~U~00401~000000003~0~T~>{GS~FA~181087842TEST~SYNNTEST~20130214~2300~810~X~004010{ST~997~131250001{AK1~SC~1809{AK9~A~1~1~1{SE~4~131250001{GE~1~810{IEA~1~000000001

Мне нужно, чтобы он был развернут следующим образом (эквивалентно tr "{" "\n" ‹ FileName.txt ):

ISA~00~          ~00~          ~ZZ~SAMSUNGSND     ~14~181087842      ~130214~2300~U~00401~000000003~0~T~>
GS~FA~181087842TEST~SYNNTEST~20130214~2300~810~X~004010
ST~997~131250001
AK1~SC~1809
AK9~A~1~1~1
SE~4~131250001
GE~1~810
IEA~1~000000001

РЕДАКТИРОВАТЬ: после развертывания мне нужно найти фиксированные значения третьего поля, если они равны «1145837» в сегменте GS (2-я строка), и заменить его на «1119283» (что эквивалентно sed '/^GS/s/1145837/1119283 /').

Ниже мой командный файл. Мне нужно, чтобы код был вставлен где-то внутри подпрограммы :WriteToLogFile

@echo on

::This ensures the parameters are resolved prior to the internal variable
SetLocal EnableDelayedExpansion

rem Get current date and time as local time.
for /f "delims=" %%a in ('wmic OS Get localdatetime ^| %SystemRoot%\System32\Find.exe "."') do set dt=%%a

rem Reformat the date and time strong to wanted format.
set "YYYY=%dt:~0,4%"
set "MM=%dt:~4,2%"
set "DD=%dt:~6,2%"
set "HH=%dt:~8,2%"
set "Min=%dt:~10,2%"
set "Sec=%dt:~12,2%"
set "TimeStamp=%YYYY%-%MM%-%DD%_%HH%-%Min%-%Sec%"

rem Define name of the list file containing current date and time in name.
set "ListFile=FLIST_%TimeStamp%.lst"

rem Change directory (and drive).
cd /D "C:\VP"

rem Create the list file which is good here as the list of files changes
rem while running this batch file and therefore it is better to work with
rem a list file instead of running a FOR directly for each file in the
rem directory. The list file is not included in this list as it either does
rem not exist at all or it has wrong file extension as only *.txt files are
rem listed by command DIR. The log file EDI.log has also wrong file extension.
dir *.txt /A:-D /B /O:D >"C:\VP\TEST\%ListFile%"

rem It might be useful to delete the log file from a previous run.
if exist EDI.log del EDI.log

rem Process each file in the list file.
cd /D "C:\VP\TEST"
for /F "delims=" %%F in ( %ListFile% ) do call :ProcessFile "%%F"
cd /D "C:\VP"

::rem Delete the list file as not needed anymore. It could be also kept.
::del %ListFile%

rem Exit batch file.
endlocal
goto :EOF


:ProcessFile
rem The parameter passed from first FOR is the file name in double quotes.
set "FileName=%~1"

rem Ignore the files CNtable.txt and Dupfile.txt in same directory.
rem Command goto :EOF just exits here from subroutine ProcessFile.
if "%FileName%"=="CNtable.txt" goto :EOF
if "%FileName%"=="Dupfile.txt" goto :EOF
if "%FileName%"=="VanPointAS2in.bat" goto :EOF
if "%FileName%"=="VP.bat" goto :EOF

rem Get 7th, 9th and 14th element from first line of current file.
cd /D "C:\VP"
for /f "usebackq tokens=7,9,14 delims=~*^" %%a in ( "%FileName%" ) do (
   set "ISAsender=%%a"
   set "ISAreceiver=%%b"
   set "ISActrlnum=%%c"
   goto WriteToLogFile
)

:WriteToLogFile
rem Remove all spaces as ISAsender and ISAreceiver have
rem usually spaces appended at end according to example
rem text. Then write file name and the 3 values to log file.
set "ISAsender=%ISAsender: =%"
set "ISAreceiver=%ISAreceiver: =%"
set "ISActrlnum=%ISActrlnum: =%"
echo %FileName%,%ISAsender%,%ISAreceiver%,%ISActrlnum%>>"C:\VP\TEST\EDI.log"

set "FLAG=N"
if "%ISAsender%"=="APPLESND" (
    if "%ISAreceiver%"=="MANGO" (
        set "FLAG=Y"
        set "VW=AP"
        call :DupCheck
        echo %errorlevel%>>"C:\VP\TEST\EDI.log"
        if errorlevel 1 move /Y "%FileName%" "APPLE"
        echo Moved %FileName% to directory APPLE.
    )
)

if "%ISAsender%"=="APPLESND" (
    if "%ISAreceiver%"=="MANGOES" (
        set "FLAG=Y"
        set "VW=AP"
        call :DupCheck
        echo %errorlevel%>>"C:\VP\TEST\EDI.log"
        if errorlevel 1 move /Y "%FileName%" "APPLE"
        echo Moved %FileName% to directory APPLE.
    )
)

if "%ISAsender%"=="SAMSUNGSND" (
    if "%ISAreceiver%"=="MANGO" (
        set "FLAG=Y"
        set "VW=SS"
        call :DupCheck
        echo %errorlevel%>>"C:\VP\TEST\EDI.log"
        if errorlevel 1 move /Y "%FileName%" "SAMSUNG"
        echo Moved %FileName% to directory SAMSUNG.
    )
)

rem Move to directory BYPASS if all else not satisfied.
if "%FLAG%"=="N" (
    move /Y "%FileName%" "BYPASS"
    echo Moved %FileName% to directory BYPASS
)

rem Exit the subroutine WriteToLogFile.
goto :EOF


:DupCheck
rem Check for ISA control number in file %VW%_table.txt.
%SystemRoot%\System32\Findstr.exe /X /M /C:%ISActrlnum% "C:\VP\TEST\%VW%_table.txt" >nul
if errorlevel 1 goto NewControl

rem This ISA control number is already present in file %VW%_table.txt.
echo Duplicate control %ISActrlnum% found in file %FileName%.
echo %ISActrlnum%,%FileName%>>"C:\VP\TEST\Dupfile.txt"
move /Y "%FileName%" "DUPLICATES"
echo Moved %FileName% to directory DUPLICATES.
rem Exit the subroutine DupCheck.
goto :EOF

:NewControl
echo %ISActrlnum%>>"C:\VP\TEST\%VW%_table.txt"

Любая помощь приветствуется.


person Lordy    schedule 28.10.2014    source источник


Ответы (1)


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

Вероятно, вас больше всего порадует GnuWin32 — бесплатная коллекция Unix-утилит для Windows. Затем вы можете манипулировать содержимым файла с помощью знакомых инструментов.

Еще одна хорошая альтернатива (моя любимая - неудивительно, так как я ее написал) - использовать REPL.BAT - гибрид JScript/batch утилита, которая выполняет операцию поиска/замены регулярных выражений на стандартном вводе и записывает результат на стандартный вывод. Это чистый скрипт, который изначально будет работать на любом компьютере с Windows, начиная с XP. Полная документация встроена в сценарий.

Я рекомендую заменить разделитель строк на \r\n, а не на \n, так как это стандарт Windows для новых строк.

Предполагая, что REPL.BAT находится в вашем текущем каталоге или где-то в вашем PATH, следующие изменения внесут необходимые вам изменения:

set "file=fileName.txt"
type "fileName.txt" | repl "{" "\r\n" lx >"%file%.new"
move /y "%file%.new" "%file%" >nul

:GS_replace
<"%file%" call repl "^(GS~.*)1145837" "$11119283" >"%file%.new"
set "rtn=%errorlevel%"
move /y "%file%.new" "%file%" >nul
if %rtn% equ 0 goto GS_replace

Я обеспокоен тем, что ваша строка числовых цифр может быть встроена в большее число, что приведет к нежелательной подстановке. Вы можете уточнить условия поиска, чтобы предотвратить это.

Следующее заменит только все поле:

:GS_replace
<"%file%" call repl "^(GS~(?:.*~)*)1145837(~|$)" "$11119283$2" >"%file%.new"
set "rtn=%errorlevel%"
move /y "%file%.new" "%file%" >nul
if %rtn% equ 0 goto GS_replace

Следующее заменяет только целое число, которое может быть встроено в более крупную буквенно-цифровую строку:

:GS_replace
<"%file%" call repl "^(GS~(?:.*\D)*)1145837(\D|$)" "$11119283$2" >"%file%.new"
set "rtn=%errorlevel%"
move /y "%file%.new" "%file%" >nul
if %rtn% equ 0 goto GS_replace

В своем комментарии ниже вы говорите, что хотите ограничить изменение номера 3-м полем строк GS. (Это сильно отличается от того, что вы указали в своем первоначальном вопросе.) Это намного проще - цикл не требуется:

type "%file%" | repl "^(GS~(.*?~){2})1145837(~|$)" "$11119283$2" >"%file%.new"
move /y "%file%.new" "%file%" >nul
person dbenham    schedule 28.10.2014
comment
спасибо за ваш ответ, как мне сделать разделитель внутри переменной repl, поскольку файл может содержать другой разделитель сегментов, такой как звездочка или любой символ? Для части GS, если третье поле равно 1145837, просто замените его на 1119283, что неверно для этого случая, поскольку поле SYNNTEST. Здесь мне нужно найти полный файл с несколькими сегментами GS и заменить третье поле, если оно равно 1145837. - person Lordy; 28.10.2014
comment
Ммм... используйте переменную "%var%" или аргумент "%~1" в качестве строки поиска вместо константы "{". Я должен был подумать, что это было довольно очевидно. О каком третьем поле вы говорите? - в вашем вопросе никогда не говорится о замене строки в определенном поле. На самом деле, я не понимаю, как ваш опубликованный пакетный скрипт вообще относится к проблеме, которую вы описываете. Ваши примеры кода unix читаются из файла, но нигде :WriteToLogFile не читается из файла. - person dbenham; 28.10.2014
comment
РЕДАКТИРОВАТЬ. Упрощен цикл, который изменяет строки GS~, используя новые функции кода возврата REPL.BAT версии 5.0. - person dbenham; 29.10.2014
comment
РЕДАКТИРОВАТЬ. Добавлено решение, показывающее, как выборочно изменять только третье поле записей GS. - person dbenham; 29.10.2014
comment
Извините за путаницу, я обновил свой вопрос. Ваш последний набор кодов сработал. Хорошо, что вы создали REPL.bat работает как шарм. Правильно ли это для части разделителя сегментов, SET var=%~1, затем repl %var% \r\n lx ›%file%.new для замены переменных значений разделителя сегментов? - person Lordy; 29.10.2014
comment
@Lordy - это должно работать, если вы указываете разделитель в качестве аргумента при вызове скрипта. Но вам не нужна переменная VAR. Вы можете просто использовать repl "%~1" "\r\n" lx >"%file%.new" - person dbenham; 29.10.2014
comment
Пытался запустить это внутри каталога REPL.bat, но не заменил разделитель сегментов. C:\VP›тип FileName_3.txt | repl %~1 \r\n lx . Я что-то упускаю? - person Lordy; 29.10.2014
comment
Эта команда в скрипте? Вы предоставили аргумент значения разделителя для %1 при вызове скрипта? Если вы не можете в этом разобраться, то задайте новый вопрос, показав свой сценарий, как вы его называете и результат. Но это действительно базовая пакетная функциональность. Пакетные аргументы Google Windows - person dbenham; 29.10.2014
comment
Я забыл привести аргумент, Боже. Спасибо еще раз. - person Lordy; 30.10.2014