Компьютерный форум 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=351385)

xxx_RedDevil_xxx 16-06-2022 10:53 2986692

Поиск меток в файлах и выполнение действий.
 
Добрый день Товарищи!

Нужна ваша помощь в оптимизации одного процесса.

Есть директория "Source", в эту папку каждый день сохраняются обработанные txt файлы (файлов N количество) с названиями БД для дальнейшей обработки.
-----------------------------------------------
Пример содержимого одного корректного обработанного txt файла:
;_DELETE_Analysers SQC
;_DELETE_REUS
;_DELETE_RTOS
;_DELETE_test
;_DELETE_Water drain
;_DELETE_WELL
Configuration
FLERT
-----------------------------------------------
Есть директория "Recipient", в эту папку вручную (скриптом копирования) переношу автоматически сохраненные и обработанные txt файлы.
Вручную проверяю, потому что в момент запуска скрипта по сбору названий БД, сам SQL сервер может быть недоступен и в файл txt вместо списка БД запишеться ошибка подключения.
Нужно такие файлы пропустить, чтобы они не перезаписали уже ранее скопированные файлы с корректным содержимым.
-----------------------------------------------
Пример содержимого одного txt файла с ошибкой подключения:
Sqlcmd: Error: Microsoft ODBC Driver 13 for SQL Server : TCP Provider: No connection could be made because the target machine actively refused it.
.
Sqlcmd: Error: Microsoft ODBC Driver 13 for SQL Server : Login timeout expired.
Sqlcmd: Error: Microsoft ODBC Driver 13 for SQL Server : A network-related or instance-specific error has occurred while establishing a connection to SQL Server. Server is not found or not accessible. Check if instance name is correct and if SQL Server is configured to allow remote connections. For more information see SQL Server Books Online..
-----------------------------------------------
Далее скрипт обработки этих файлов подключается к директории "Recipient", обрабатывает каждый файл, создает каталоги по названиям, делает бэкап и т.д.
Т.е. директория "Recipient" является источником для других скриптов обслуживания.

Вопрос в том, как можно обработать txt файлы по содержимому, например проверять в каждом файле наличие метки "Sqlcmd: Error:" или просто "Sqlcmd:"
-----------------------------------------------
powershell:
Select-String -Path "E:\_Auto_Export_AF_DB_\Scripts\SQL_Check_Healthy\Source\*.txt" -Pattern "Sqlcmd:"
-----------------------------------------------
и пропускать файлы с такой ошибкой, не копировать их в директорию "Recipient", копировать только те файлы, которые не содержать указанной метки.
Чтобы файлы с ошибкой подключения, не перезаписывали в директории "Recipient" файлы ранее скопированные с таким же названием но с корректным содержимым (списком БД).

Например что-то вроде этого:
@echo off
set GLOBAL_PATH=E:\_Auto_Export_AF_DB_\Scripts\SQL_Check_Healthy
set SOURCE_PATH=%GLOBAL_PATH%\Source
set RECIPIENT_PATH=%GLOBAL_PATH%\Recipient
forfiles /P "%SOURCE_PATH%" /M "*.txt" /C "cmd /c powershell -Command (gc @file) -replace 'Sqlcmd:', ';Sqlcmd:' > "%RECIPIENT_PATH%\@fname.@ext""

Но вместо замены текста, сделать например условие:
if @file="Sqlcmd: Error:" или просто "Sqlcmd:" then "пропускать и переходить к проверки следующего файла" else "копировать файл в "Recipient" и переходить к проверки следующего файла".

Надеюсь задачу описал максимально понятно. Буду весьма признателен сообществу единомышленников за помощь!
Всем хорошего дня!

DJ Mogarych 16-06-2022 13:12 2986702

Powershell:
Код:

$source = "D:\temp\SQL\Source"
$recipient = "D:\temp\SQL\Recipient"

dir "$source\*.txt" |% {
    if (-not ((gc $_) -match 'Sqlcmd:')) {
        cp $_ "$recipient" -WhatIf
    }
}

Уберите -WhatIf, чтобы реально копировало.

Цитата:

Цитата xxx_RedDevil_xxx
Вручную проверяю, потому что в момент запуска скрипта по сбору названий БД сам SQL сервер может быть недоступен »

У Пауэршелла есть полезная команда Invoke-Sqlcmd (на серверах, где установлен MSSQL), ей можно проверить подключение, например:
Код:

Invoke-Command -ComputerName SRV-SQL1 -Command {Invoke-Sqlcmd -ServerInstance "localhost" -Database "users_db" -Query "SELECT users_column FROM users_table"}
Есть и другие способы, возможно, более экономичные.

xxx_RedDevil_xxx 16-06-2022 15:53 2986714

DJ Mogarych, спасибо вам огромное!
Вариант со скриптом Powershell отработал, удалил -WhatIf и все заработало.
Файлы с маской были проигнорированы, а файлы с корректным содержимым перенеслись в конечную директорию.
До адаптирую дальше под свои нужны!

Спасибо что уделили время для решения этой задачи.

Хорошего вам дня! :up

xxx_RedDevil_xxx 22-06-2022 14:10 2987094

Доброго дня!

Адаптировал и проверил работу данного скрипта в рабочей среде, все работает отлично.

Вопрос, подскажите пожалуйста как можно обыграть этот скрипт через CMD?

В основном скрипте уже есть объявленный набор переменных, например:

@echo off
set GLOBAL_PATH=E:\_Auto_Export_AF_DB_
set SELECT_PATH=E:\_Auto_Export_AF_DB_\Scripts\SQL_Select_AF_DB
set BACKUP_LIST_PATH=E:\_Auto_Export_AF_DB_\AF_DB_List

Сейчас выполняется ряд условий, затем переход к строке которая из cmd запускает скрипт powershell:
cmd:
powershell "%BACKUP_LIST_PATH%\SQL_Check_Healthy_and_Copy_to_Input.ps1"

SQL_Check_Healthy_and_Copy_to_Input.ps1:
$source = "E:\_Auto_Export_AF_DB_\AF_DB_List\_SQL_AF_DB_List_All_Rep_"
$recipient = "E:\_Auto_Export_AF_DB_\AF_DB_List"
dir "$source\*.txt" |% {if (-not ((gc $_) -match 'Sqlcmd:')) {cp $_ "$recipient"}}

И после проверки и копирования файлов cmd идет дальше.
Можно ли обыграть powershell скрипт в среде cmd, чтобы отдельно не объявлять переменные в powershell, а использовать уже объявленные в cmd?
Например:
forfiles /P "%SELECT_PATH%\SQL_AF_DB_List_All" /M "*.txt" /C "cmd /c powershell -Command (gc @file) -replace '^_DELETE_', ';_DELETE_' > "%SELECT_PATH%\SQL_AF_DB_List_All_Rep\@fname.@ext""

Только вместо -Command (gc @file) -replace '^_DELETE_', ';_DELETE_' > "%SELECT_PATH%\SQL_AF_DB_List_All_Rep\@fname.@ext" использовать условие проверки содержимого в источнике и копирования в директорию назначения dir "$source\*.txt" |% {if (-not ((gc $_) -match 'Sqlcmd:')) {cp $_ "$recipient"}}.
где $source в powershell это "%BACKUP_LIST_PATH%\_SQL_AF_DB_List_All_Rep_\" в cmd,
а $recipient в powershell это "%BACKUP_LIST_PATH%" в cmd.

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

Заранее благодарю! Хорошего дня!

DJ Mogarych 22-06-2022 14:48 2987096

Вместо скрещивания ежа с ужом лучше скрипт переделать целиком под Powershell

xxx_RedDevil_xxx 22-06-2022 15:29 2987107

DJ Mogarych, понимаю что полностью переделать на одной платформе с cmd на powershell было бы правильнее, но в данном случае, скрипт основной довольной большой и выполняет большое кол-во задач. На текущий момент тратить время на переделывание существующего скрипта трудоемко и нецелесообразно, никто не оценит. Возможно если в будущем заняться будет больше нечем)) Поэтому приходиться дописывать небольшие доработки и скрещивать мух с котлетами и ежей с ужами))
Если можете подсказать по поводу реализации задумки на cmd, буду весьма признателен.

Fors1k 22-06-2022 16:15 2987111

Цитата:

Цитата xxx_RedDevil_xxx
тратить время на переделывание существующего скрипта трудоемко и нецелесообразно »

У вас объем скрипта может сократиться в разы при переходе с устаревшего и немощного cmd на powershell. Лучше делайте сразу нормально, как вам и посоветовали выше.

megaloman 24-06-2022 07:49 2987215

xxx_RedDevil_xxx,
Цитата:

Цитата xxx_RedDevil_xxx
Если можете подсказать по поводу реализации задумки на cmd »

Код:

@Echo Off
        Set "Source=Z:\sqlwork\Source\*.txt"
        Set "Recipient=Z:\sqlwork\Recipient"
        Set "Error=Z:\sqlwork\Error"
        Set "KeyErr=Sqlcmd: Error:"
 
        2>nul "Md %Error%"
        For %%f In ("%Source%") Do (
                >nul Find "%KeyErr%" "%%f" ||>nul Move /Y "%%f" "%Recipient%\
                If Exist "%%f" (>nul Find "%KeyErr%" "%%f" &&>nul Move /Y "%%f" "%Error%\")
        )
Exit /B 0

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

xxx_RedDevil_xxx 27-06-2022 11:17 2987349

megaloman, спасибо вам огромное, адаптировал ваш код под свои нужды, работает отлично!

Цитата:

@echo off
set GLOBAL_PATH=E:\_Auto_Export_AF_DB_\Scripts\SQL_Check_Healthy
set SOURCE_PATH=%GLOBAL_PATH%\Source
set RECIPIENT_PATH=%GLOBAL_PATH%\Recipient
set BACKUP_PATH=%GLOBAL_PATH%\Backup
set ERROR_PATH=%GLOBAL_PATH%\Error
set "KeyErr=Sqlcmd: Error:"

2>nul "Md %ERROR_PATH%"
For %%f In ("%SOURCE_PATH%\*.txt") Do (
xcopy /y "%RECIPIENT_PATH%\AF_DB_List_*.txt" "%BACKUP_PATH%" && >nul Find "%KeyErr%" "%%f" ||>nul xcopy /Y "%%f" "%RECIPIENT_PATH%\"
If Exist "%%f" (>nul Find "%KeyErr%" "%%f" &&>nul xcopy /Y "%%f" "%ERROR_PATH%\")
)
Exit /B 0
Вы правы, копировать ошибочные файлы в отдельную папку удобно, сразу видно где была ошибка подключения.
Добавил только копирование файлов из Recipient в Backup, чтобы был бэкап последних рабочих файлов перед манипуляциями и изменил move на xcopy, пусть файлы остаются в Source.

Подскажите пожалуйста, можно ли делать перечисления якорей в переменной "KeyErr", если будет нужно в будущем по нескольким маскам проверять файлы?
Например: set "KeyErr="Sqlcmd: Error:",";_DELETE_WELL""

Serguei Kouzmine 27-06-2022 18:13 2987370

замените find.exe на findstr.exe
Код:

findstr.exe /?
Код:

Use spaces to separate multiple search strings unless the argument is prefixed
with /C.  For example, 'FINDSTR "hello there" x.y' searches for "hello" or
"there" in file x.y.


Код:

Использовать пробелы для разделения нескольких искомых строк, если аргумент не
имеет префикса /C. Например, 'FINDSTR "Привет мир" a.b' ищет "Привет" или
"мир" в файле a.b


xxx_RedDevil_xxx 28-06-2022 12:51 2987414

Serguei Kouzmine, спасибо большое за подсказку!

Попробовал, да действительно отрабатывает корректно:

Цитата:

@echo off
set GLOBAL_PATH=E:\_Auto_Export_AF_DB_\Scripts\SQL_Check_Healthy
set SOURCE_PATH=%GLOBAL_PATH%\Source
set RECIPIENT_PATH=%GLOBAL_PATH%\Recipient
set ERROR_PATH=%GLOBAL_PATH%\Error
set "KeyErr=Sqlcmd: Error: ;_DELETE_WELL"

2>nul "Md %ERROR_PATH%"
For %%f In ("%SOURCE_PATH%\*.txt") Do (
>nul findstr "%KeyErr%" "%%f" ||>nul xcopy /Y "%%f" "%RECIPIENT_PATH%\"
If Exist "%%f" (>nul findstr "%KeyErr%" "%%f" &&>nul xcopy /Y "%%f" "%ERROR_PATH%\"))
Exit /B 0
Благодарю всех за помощь и подсказки, все решения пригодились!

Всем хорошего дня и удачи в написании скриптов ;)

megaloman 28-06-2022 15:30 2987429

xxx_RedDevil_xxx, Мне не нравится Ваш код как по постановке, так и по исполнению.
Цитата:

Цитата xxx_RedDevil_xxx
Есть директория "Source", в эту папку каждый день сохраняются обработанные txt файлы »

То есть у Вас там копятся уйма файлов, которые Вы все при обработке просматриваете в For. Это затратно. Поэтому Xcopy, как Вы его применили, не вариант: однозначно нужен Move. А если хочется иметь копии всех пришедших файлов - флаг в руки: копируйте всё что пришло в Source в другую папку - тут уж нужен Xcopy /D. И, возможно, лучше их в дальнейшем заархивировать с удалением исходных.
И, по стилю, надежнее делать не, например:
set GLOBAL_PATH=E:\_Auto_Export_AF_DB_\Scripts\SQL_Check_Healthy
а
set "GLOBAL_PATH=E:\_Auto_Export_AF_DB_\Scripts\SQL_Check_Healthy"
это позволит избежать ошибок, если присутствуют конечные незаметные на экране конечные пробелы.
Не очень корректно использованы строки поиска в FindStr, так как в строке поиска "Sqlcmd: Error:" присутствует пробел.
Вот вариант:
Код:

@Echo Off
cls
        Set "GLOBAL_PATH=Z:\_Auto_Export_AF_DB_\Scripts\SQL_Check_Healthy"
        Set "SOURCE_PATH=%GLOBAL_PATH%\Source"
        Set "RECIPIENT_PATH=%GLOBAL_PATH%\Recipient"
        Set "ERROR_PATH=%GLOBAL_PATH%\Error"
        Set "Backup_PATH=%GLOBAL_PATH%\Backup"
        Set "Mask=*.txt"
        Set KeyErr=/C:"Sqlcmd: Error:" /C:";_DELETE_WELL"

        If Not Exist "%SOURCE_PATH%\" (Echo !!! Folder "%SOURCE_PATH%" not found &Pause &Exit /B 2)
        2>nul (Md "%ERROR_PATH%" &Md "%RECIPIENT_PATH%")
       
        >nul 2>&1 Xcopy /Y /D "%SOURCE_PATH%\%Mask%" "%Backup_PATH%\"
        For %%f In ("%SOURCE_PATH%\%Mask%") Do (
                >nul FindStr %KeyErr% "%%f" ||>nul Move /Y "%%f" "%RECIPIENT_PATH%\"
                If Exist "%%f" >nul FindStr %KeyErr% "%%f" &&>nul Move /Y "%%f" "%ERROR_PATH%\"
        )
pause
Exit /B


xxx_RedDevil_xxx 29-06-2022 15:47 2987501

megaloman, спасибо огромное за указание на проблемные места!

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

Спасибо большое за помощь!

Хорошего дня!


Время: 01:49.

Время: 01:49.
© OSzone.net 2001-