Компьютерный форум OSzone.net  

Компьютерный форум OSzone.net (http://forum.oszone.net/index.php)
-   Скриптовые языки администрирования Windows (http://forum.oszone.net/forumdisplay.php?f=102)
-   -   Сравнение строк текстовых файлов, найти разницу. (http://forum.oszone.net/showthread.php?t=349977)

Alexlook 07-11-2021 16:55 2971107

Сравнение строк текстовых файлов, найти разницу.
 
Всем привет. Нужна помощь.
Есть 2 текстовых файла, внутри каждого построчно списки файлов, 1 строка - 1 название файла. Задача найти файлы которые есть в первом файле и нет во втором и наоборот.

Вот файлы:
______________________________________________
files.txt
_________
test2.bat
test3.bat
LR.bat
LR1(‘а*ў*Ґ*ЁҐ f Ё f1 ў compare).bat
files_07.2021.txt
files_08.2021.txt
’‡ ‹ђ Ћ‘.txt
files.txt
files2.txt
files_10.2021.txt
dif.txt
test.bat
lists.txt
null
date.txt
0411.bat

________________
files2.txt
_______________
LR.bat
LR1(‘а*ў*Ґ*ЁҐ f Ё f1 ў compare).bat
files_07.2021.txt
files_08.2021.txt
’‡ ‹ђ Ћ‘.txt
files_10.2021.txt
dif.txt
test.bat
lists.txt
null
date.txt
0411.bat
0511.bat
Ђ«Ј®аЁв¬.txt
______________________________________



Пока написал вот такой код: он записывает имена файлов из файлов построчно в массивы и сравнивает, выводит если есть совпадение. Тут массивы дублируются псевдомассивами в каждой обработке файлов, это конечно можно убрать. Просто пытаюсь хоть как-то это победить. План был при совпадении строк заменять их например на 0. Затем перебрать массивы и вывести все значения что не 0 в одном списке и во втором.

Код:

@echo off
Setlocal EnableDelayedExpansion EnableExtensions
chcp 866
set /a k=0
set /a l=0
set /a j=0
set /a i=0

rem получаю массив строк файла files2.txt
for /f "usebackq delims=" %%a in ("files2.txt") do (       
        set /a k+=1         
        set "m!k!=%%a"
        set myar[%%j]=%%a
rem        echo !myar[%%j]!
)

rem получаю массиы строк файла files.txt при это каждую строку сравниваю с каждой из предыдущего  если совпадение меняю на 0
        for /f "usebackq delims=" %%b in ("files.txt") do (
                set /a l+=1
                set "n!l!=%%b"
                set myar2[%%i]=%%b
                if !myar[%%j]! == !myar2[%%i]! do (
                        echo !myar[%%j]!
                        set myar2[%%i]=0
                        echo !myar2[%%i]! >> myar.txt
                )
                rem echo !myar2[%%i]!
                echo !$l%!
                                       
        )
        rem echo !myar2[%%i]!
        rem echo !myar[%%j]!               
)
set /a df=12
echo %n12%
echo %m12%




Я нихрена не понимаю:

1. Почему я потом не могу циклом for /l перебрать массивы?
2. Как вывести элемент массива вне цикла не по конкретному индексу, а указывая индекс переменной. В цикле выводится а вне цикла нет.
3. Внутри цикла set myar2[%%i]=0 не меняет значения элемента. Как сделать чтоб менял?
4. Почему где-то операция в кавычках, где -то %%, где-то !!. Я конечно сам это написал но путем копипаста экспериментов и изучения кучи всяких документаций. Но по этому синтаксису внутри цикла так и не нашел ничего внятного. Если кто скажет где почитать буду признателен.
5. Если выводить переменную k или l внутри цикла она не меняет значения, только по окончании, как это работает? Где почитать?
6. Должен ли внутри цикла работать GOTO?

Если кто-то сможет с этим разобраться, возможно есть более простой способ, буду очень благодарен.

YuS_2 07-11-2021 18:56 2971119

Цитата:

Цитата Alexlook
возможно есть более простой способ »

есть, но не в cmd, а в powershell:
Код:

diff (gc files1.txt) (gc files2.txt)
результат:
Код:

InputObject  SideIndicator
-----------  -------------
0511.bat    =>
Алгоритм.txt =>
test2.bat    <=
test3.bat    <=
files.txt    <=
files2.txt  <=


megaloman 07-11-2021 19:44 2971124

Код:

@Echo Off
cls
        Set "File1=Z:\Box_In\files-1.txt"
        Set "File2=Z:\Box_In\files-2.txt"

        Echo ------- В файле "%File1%" есть а В файле "%File2%" нет
        FindStr /I /L /G:"%File2%" /V "%File1%"

        Echo ------- В файле "%File2%" есть а В файле "%File1%" нет
        FindStr /I /L /G:"%File1%" /V "%File2%"
pause
Exit /B

Тестовые данные и результат
Цитата:

Цитата Z:\Box_In\files-1.txt
test1.bat
test2.bat
test21.bat
test22.bat
test31.bat
test32.bat

Цитата:

Цитата Z:\Box_In\files-2.txt
test3.bat
test2.bat
test23.bat
test22.bat
test33.bat
test32.bat

Цитата:

Цитата Результат
------- В файле "Z:\Box_In\files-1.txt" есть а В файле "Z:\Box_In\files-2.txt" нет
test1.bat
test21.bat
test31.bat
------- В файле "Z:\Box_In\files-2.txt" есть а В файле "Z:\Box_In\files-1.txt" нет
test3.bat
test23.bat
test33.bat

Если файлы с именами в кодировке 1251 то после cls добавить строку
Код:

>nul chcp 1251
и сохранить батник в 1251 кодировке.

Iska 07-11-2021 22:11 2971139

Alexlook, глобальная цель какова? Тупо WinMerge не устроит?
Скрытый текст

Elven 08-11-2021 08:52 2971155

Цитата:

Цитата Alexlook
1. Почему я потом не могу циклом for /l перебрать массивы?
2. Как вывести элемент массива вне цикла не по конкретному индексу, а указывая индекс переменной. В цикле выводится а вне цикла нет.
3. Внутри цикла set myar2[%%i]=0 не меняет значения элемента. Как сделать чтоб менял?
4. Почему где-то операция в кавычках, где -то %%, где-то !!. Я конечно сам это написал но путем копипаста экспериментов и изучения кучи всяких документаций. Но по этому синтаксису внутри цикла так и не нашел ничего внятного. Если кто скажет где почитать буду признателен.
5. Если выводить переменную k или l внутри цикла она не меняет значения, только по окончании, как это работает? Где почитать?
6. Должен ли внутри цикла работать GOTO? »

1. Потому что cmd не умеет нормально в массивы, он не хранит их сам по себе, разве что спихнуть в файл и из него прочитать. Или создать пачку переменных, и в каждую записать свое значение - получится такой как бы фальшивый массив.
2. Именно так как описано в вопросе - никак, см. п.1
3. Чтобы меняло значение, нужно использовать не %% а !!. Читать в расширенную обработку команд -> тык
4, 5. Все та же ссылка, что и в п.3
6. Должен, но использовать его не нужно. Цикл подразумевает под собой обработку некоторого количества значений до выполнения некоторого условия, в то время как GOTO совершает безусловный переход. Это не есть правильно. Кроме того - добавляет головной боли при отладке.

Что же касается создания результирующего файла мне больше нравится Compare-Object , да и для обработки массивов лучше гонять powershell. Сказать обо что-нибудь подробнее можно будет только после озвучивания окончательной цели, а то может тут большая часть телодрыжества бесполезна...

Alexlook 08-11-2021 09:31 2971158

YuS_2,
Цитата:

Цитата YuS_2
Цитировать »

Спасибо, согласен в Power Shell более очевидно как это сделать. Но все же думал сделать в CMD.

Цитата:

Цитата megaloman
Цитировать »

Вот это, похоже то что нужно, я findstr и fc день крутил, но видимо не до конца.
Все думал столько команд неужели нужно изобретать что-то для такой задачи.

Спасибо большое, на выходных думаю доделаю!

Цитата:

Цитата Iska
Цитировать »

Глобальная цель КР по ОС:

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

WinMerge кажется не подойдет. Спасибо.

Цитата:

Цитата Elven
Цитировать »

Спасибо за ответы и ссылки, буду разбираться.

DJ Mogarych 08-11-2021 10:18 2971163

Цитата:

Цитата Alexlook
формировал бы ежемесячный
отчет об изменениях в рабочем каталоге (файлы созданные, удаленные).
Необходимо хранить список файлов в файле истории. »

Для этого уже давно придуман файловый аудит, события которого потом легко разбираются тем же Пауэршеллом.

megaloman 08-11-2021 10:57 2971168

Alexlook,
Цитата:

Почему ...
Не умеете :) Если нельзя но очень хочется, то можно (осторожно)
Код:

@Echo Off
cls
        Call :TxtToMass "Z:\Box_In\Файл первый.txt" "@@A" "NA" 10000

        Set "@@A"
        Echo NA=%NA%
        Echo @@A10005=%@@A10005%
        Echo.

        Call :TxtToMass "Z:\Box_In\Файл второй.txt" "@@B" "NB" 10000

        Set "@@B"
        Echo NB=%NB%
        Echo @@B10003=%@@A10003%
        Echo.

        For /L %%i In (10001,1,%NA%) Do Call Echo %%i %%@@A%%i%%
        Echo.

        For /F "usebackq tokens=1* delims==" %%i In (`Set "@@A"`) Do Echo %%i %%j
        Echo.

pause
Exit /B

:TxtToMass
        Set /A %~3=%~4
        For /F "usebackq delims=" %%i In (`2^>nul More %1`) Do (
                Call Set /A %~3+=1
                Call Set "%~2%%%~3%%=%%i"
        )
Exit /B

Пояснения
Цитата:

Цитата Elven
создать пачку переменных, и в каждую записать свое значение - получится такой как бы фальшивый массив. »

В моём примере так и реализовано. Только для того, чтобы при сортировке элементов массива по имени не нарушался порядок строк из текстового файла я нумерую элементы псевдомассива не 1, 2, 3 а, например, 10001, 10002, 10003. Если делать не так, то при текстовой сортировке имён получим что A100 будет отображаться раньше A2.
Я стараюсь избегать применения Setlocal EnableDelayedExpansion так как применение ! в текстовых литералах при этом приведёт к ошибке. Для Вашего случая, в имени файлов при таком SetLocal не должно быть "!".
Но вообще-то работать с текстами в CMD - надо очень осторожно, есть много служебных символов в CMD, которые, если встретятся в литерале, сломают любую вашу логику.
В примере я применил процедуру (кто-то меня поправит - псевдопроцедуру)
Для облегчения понимания её работы вот кусок для одного массива (псевдомассива, чтобы никого не раздражать) без процедуры.
Код:

@Echo Off
cls
        Set /A NA=10000
        For /F "usebackq delims=" %%i In (`2^>nul More "Z:\Box_In\Файл первый.txt"`) Do (
                Call Set /A NA+=1
                Call Set "@@A%%NA%%=%%i"
        )

        Set "@@A"
        Echo NA=%NA%
        Echo @@A10005=%@@A10005%
        Echo.

        For /L %%i In (10001,1,%NA%) Do Call Echo %%i %%@@A%%i%%
        Echo.

        For /F "usebackq tokens=1* delims==" %%i In (`Set "@@A"`) Do Echo %%i %%j
        Echo.
pause
Exit /B

Процедура удобнее, особенно если одно и то же надо проделать в нескольких местах.
Насчёт GoTo в цикле
Если GoTo ссылается на метку внутри цикла, у меня не получается. Я использую GoTo внутри цикла на метку вне цикла, чтобы выйти из цикла.
Лучше использовать If
Код:

@Echo Off
cls
        For /L %%i In (1,1,10) Do (
                If %%i LEQ 5 (
                        Echo %%i меньше или равно 5
                ) Else (
                        Echo %%i больше 5
                )
        )
pause
Exit /B


Sham 08-11-2021 11:01 2971169

Нету там никаких массивов. Квадратные скобки - часть имени переменной.

Iska 09-11-2021 00:47 2971253

Цитата:

Цитата Alexlook
Глобальная цель КР по ОС:
Разработать командный файл, который формировал бы ежемесячный
отчет об изменениях в рабочем каталоге (файлы созданные, удаленные).
Необходимо хранить список файлов в файле истории. »

Используйте код коллеги megaloman из сообщения №3.


Время: 23:45.

Время: 23:45.
© OSzone.net 2001-