1

Тема: CMD/BAT: Батник на 3 простых действия

Здравствуйте. Есть вопрос, который не могу решить вот уже 3 дня, как ни пытался самостоятельно.


Исходные данные
Есть каталог с файлами .txt. В каждом файле количество строк разное. Содержание каждого файла:

234324;Ссылка1
464565;Ссылка2
546456;Ссылка3
456456;Ссылка4
...
7899;СсылкаN


Что должен делать батник?
1. В каждом файле оставить только 3 первых строки, остальные удалить.
2. В каждом файле оставить только ссылки. Цифры, которые расположены перед ссылками и символ ";" удалить
3. Выбирать рандомно при каждом запуске 3 файла, склеивать данные и создавать новый файл с этими данными.


Что должно получиться в итоге?

При запуске батника должен получиться новый файл в таком виде:
Ссылка1
Ссылка2
Ссылка3

Ссылка4
Ссылка5
Ссылка6

Ссылка7
Ссылка8
Ссылка9




Всем заранее благодарен за помощь ибо своих сил и знаний не хватает для реализации.

2

Re: CMD/BAT: Батник на 3 простых действия

denisss, добрый день.
Вот, насколько понял по описанию задачи.

@echo off & title %~nx0 & SetLocal EnableExtensions EnableDelayedExpansion

rem Глобальные установки:
rem кол-во случайно обрабатываемых файлов
set /a nFileCount = 3
rem кол-во строк для выборки из файлов
set /a nLineCount = 3

rem --------------------------------------------------
rem 3.1. Выбирать рандомно при каждом запуске 3 файла.
rem --------------------------------------------------

rem ... подсчёт общего кол-ва файлов *.txt в текущей папке:
set /a nFileTotal = 0
for %%a in (*.txt) do set /a nFileTotal += 1
rem echo.Всего файлов: !nFileTotal!

if !nFileTotal! lss !nFileCount! echo.*** Недостаточно файлов.&goto :End

rem ... заполнение "массива" aFile "случайными" числами:
rem (на самом деле создаются переменные aFile[1], aFile[2], ...)

rem тут должен был быть цикл, но приходится использовать GOTO из-за некоторой повторяемости %RANDOM%:
rem for /l %%i in (1,1,!nFileCount!) do 
set /a iFile = 0
:loopRandom
  call set /a nRandom = %%RANDOM%% %%%% !nFileTotal! + 1
  rem Проверка на повторение "случайного" числа в заполненной части aFile:
  for /l %%i in (1,1,!iFile!) do (
    if !aFile[%%i]! equ !nRandom! (
      echo.повтор: aFile[%%i]=!aFile[%%i]! == !nRandom!
      goto :loopRandom
    )
  )
  set /a iFile += 1
  set /a aFile[!iFile!]=!nRandom!
  echo.aFile[!iFile!]=!nRandom!
  if !iFile! lss !nFileCount! goto :loopRandom

rem сейчас в aFile содержится набор 3-х случайных целых чисел, соответствующих порядковым номерам файлов в папке.
rem например, для 5 TXT-файлов возможен такой набор: [5, 1, 3].
echo. 

rem ... перезаполнение aFile именами случайных файлов (по значениям в aFile):
set /a iFile = 0
for %%a in (*.txt) do (
  set /a iFile += 1
  rem просмотр aFile на значение nFileTotal и если найдено, то aFile[%%i] присваиваем имя соответствующего .txt-файла. 
  for /l %%i in (1,1,!nFileCount!) do (
    if !aFile[%%i]! equ !iFile! (
      set  "aFile[%%i]=%%~a"
      echo."aFile[%%i]=!aFile[%%i]!"
    )
  )
)

rem сейчас в aFile содержатся имена 3-х файлов в папке.
rem например: [файл_05.txt, файл_01.txt, файл_03.txt].

rem -------------------------------------------------------------
rem 3.2. Склеивать данные и создавать новый файл с этими данными.
rem -------------------------------------------------------------
rem ... имя файла-результата (случайное временное имя):
set "sFileRes=%~n0_%RANDOM%.tmp"
echo. 

rem ... создание файла-результата:
for /l %%i in (1,1,!nFileCount!) do (

  rem ---------------------------------------------------------------------
  rem 1. В каждом файле оставить только 3 первых строки, остальные удалить.
  rem ---------------------------------------------------------------------
  set /a iLine = !nLineCount!
  for /f "usebackq delims=" %%A in ("!aFile[%%i]!") do if !iLine! gtr 0 (

    rem ---------------------------------------------------------------------------------------------------------
    rem 2. В каждом файле оставить только ссылки. Цифры, которые расположены перед ссылками и символ ";" удалить.
    rem ---------------------------------------------------------------------------------------------------------
    for /f "tokens=1,* delims=;" %%a in ("%%A") do (
      echo.%%b>>"!sFileRes!"
    )
    
    set /a iLine -= 1
  )
)

echo.Создан файл !sFileRes!. 

:End
pause
EndLocal
exit /b

3

Re: CMD/BAT: Батник на 3 простых действия

Под такие задачи лучше использовать нормальные языки, например, jvascript, vbscript, powershell.

1. получить список файлов
2. из файлов из п.1 выбрать 3 случайных файла
3. из каждого файла из п.2 прочитать 3 первые строки
4. из строк списка, полученного в п.3 выделить требуемые подстроки и вывести (на экран или в другой файл)

Возможны дальнейшие усложнения как то:что делать, если файлов меньше 3, или что делать, если строк в каждом файле меньше 3, или строка не соответствует определенным требованиям, или строка пустая. Ну и т.д.

( 2 * b ) || ! ( 2 * b )

4

Re: CMD/BAT: Батник на 3 простых действия

Rumata, вот на VBS решение.
Текста не меньше, чем в BAT получилось - не знаю, может я не оптимально написал.

' Можно требовать объявления переменных, раскомментировав строчку:
' Option Explicit

' В случае ошибки переходить к следующему оператору
On Error Resume Next
' Альтернатива - в случае ошибки прекращать выполнение программы:
' On Error GoTo 0

	' Полезные объекты для работы:
	Set goShellApp = CreateObject("Shell.Application")
	Set goWsShell  = CreateObject("WScript.Shell")
	Set goFSO      = CreateObject("Scripting.FileSystemObject")

	' Глобальные установки:
	nFileCount = 3 ' кол-во случайно обрабатываемых файлов
	nLineCount = 3 ' кол-во строк для выборки из файлов

	' --------------------------------------------------
	' 3.1. Выбирать рандомно при каждом запуске 3 файла.
	' --------------------------------------------------

	Set oFiles = goFSO.GetFolder(goWsShell.CurrentDirectory).Files
	' ... подсчёт общего кол-ва файлов *.txt в текущей папке, заполнение массива aFileTxt:
	nFileTotal = 0
	For Each oFile In oFiles
		If StrComp(goFSO.GetExtensionName(oFile.Path), "txt", vbTextCompare) = 0 Then
			ReDim Preserve aFileTxt(nFileTotal)
			aFileTxt(nFileTotal) = oFile.Name
			nFileTotal = nFileTotal + 1
		End If
	Next 'oFile

	If nFileTotal < nFileCount Then
		WScript.Echo "*** Недостаточно TXT-файлов!"
		WScript.Quit(1)
	End If

	' ... заполнение массива aFiles случайными числами:

	Randomize ' инициализировать генератор случайных чисел (с помощью стистемного таймера).

	ReDim aFile(nFileCount-1)
	iFile = -1
	Do While iFile < UBound(aFile)
		nRandom = Int((nFileTotal * Rnd) + 0) ' генерировать целое случайное число от 0 до nFileTotal-1.
		' Проверка на повторение "случайного" числа в заполненной части aFile:
		For j = 0 To iFile
			If aFile(j) = nRandom Then
				WScript.Echo "повтор: aFile(", j, ")=", nRandom
				nRandom = -1
				Exit For
			End If
		Next 'j
		If nRandom > 0 Then
			iFile = iFile + 1
			aFile(iFile) = nRandom
			WScript.Echo "aFile(", iFile, ")=", aFile(iFile)
		End If
	Loop 

	' Сейчас в aFile содержится набор 3-х случайных целых чисел, соответствующих порядковым номерам TXT-файлов в папке.
	' Например, для 5 TXT-файлов возможен такой набор: [5, 1, 3].
	' А в aFileTXT содержатся имена TXT-файлов в папке.
	' Далее будут в цикле обрабатываться значения aFileTxt(aFile(i)): [файл_05.txt, файл_01.txt, файл_03.txt].

	' -------------------------------------------------------------
	' 3.2. Склеивать данные и создавать новый файл с этими данными.
	' -------------------------------------------------------------
	' ... имя файла-результата (случайное временное имя):
	sFileRes = goFSO.GetTempName()
	sLineRes = "" ' буфер файла-результата

	' ... создание буфера файла-результата:
	For iFile = 0 To UBound(aFile)
		WScript.Echo "aFile(", iFile, ")=", aFileTxt(aFile(iFile))
		' ---------------------------------------------------------------------
		' 1. В каждом файле оставить только 3 первых строки, остальные удалить.
		' ---------------------------------------------------------------------
		With goFSO.OpenTextFile(aFileTxt(aFile(iFile)), 1) 'ForReading
			For iLine = 0 To nLineCount-1
				' ---------------------------------------------------------------------------------------------------------
				' 2. В каждом файле оставить только ссылки. Цифры, которые расположены перед ссылками и символ ";" удалить.
				' ---------------------------------------------------------------------------------------------------------
				aLine = Split(.ReadLine(), ";") 'строка "234324;Ссылка1" => массив [234324, Ссылка1]
				If UBound(aLine) > 0 Then ' проверка, что строка не пустая и есть хотя бы 1 разделитель ";"
					sLineRes = sLineRes & aLine(1) & vbCrLf '"Ссылка1"
				End If
			Next 'iLine
			.Close
		End With
	Next 'iFile

	' ... запись в файл-результат:
	With goFSO.OpenTextFile(sFileRes, 2, True) 'Write, Create
		.Write(sLineRes)
		.Close
	End With
	
	WScript.Echo "Создан файл", sFileRes

' Выход с кодом возврата
WScript.Quit(0)

5

Re: CMD/BAT: Батник на 3 простых действия

andypetr пишет:

заполнение массива aFiles случайными числами

Интересный подход! Но не проще ли так:

  • Создать список всех файлов

  • Повторить три раза следующие действия:

    • Получить рандомное число от 0 до максимального индекса в списке

    • Использовать файл с индексом, равным полученному числу

    • Удалить этот файл из списка

?
Хотя я не спец в vbs, может, какую-то глупость сказал!

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

6

Re: CMD/BAT: Батник на 3 простых действия

teadrinker, да, спасибо за идею!
Код упростился, не нужно беспокоиться о повторении случайных чисел.

On Error Resume Next

' Полезные объекты для работы:
Set goShellApp = CreateObject("Shell.Application")
Set goWsShell  = CreateObject("WScript.Shell")
Set goFSO      = CreateObject("Scripting.FileSystemObject")

' Глобальные установки:
	
nFileCount = 3 ' кол-во случайно обрабатываемых файлов
nLineCount = 3 ' кол-во строк для выборки из файлов
	
' Создать список всех файлов:

Set oFilesTxt = CreateObject("Scripting.Dictionary")
Set oFiles = goFSO.GetFolder(goWsShell.CurrentDirectory).Files
' ... заполнение словаря oFilesTxt:
For Each oFile In oFiles
	If StrComp(goFSO.GetExtensionName(oFile.Path), "txt", vbTextCompare) = 0 Then
		oFilesTxt.Add oFile.Name, oFile.Path
	End If
Next 'oFile

If oFilesTxt.Count < nFileCount Then
	WScript.Echo "*** Недостаточно TXT-файлов!"
	WScript.Quit(1)
End If

' Сейчас в oFilesTxt содержится список всех TXT-файлов в папке.
' Например, для 5 TXT-файлов: 
' oFilesTxt.Items = [файл_01.txt, файл_02.txt, файл_03.txt, файл_04.txt, файл_05.txt].

' ... имя файла-результата (случайное временное имя):
sFileRes = goFSO.GetTempName()
sLineRes = "" ' буфер файла-результата

' Повторить три раза следующие действия:
Randomize ' инициализировать генератор случайных чисел (с помощью стистемного таймера).

For iFile = 0 To nFileCount-1
	' Получить рандомное число от 0 до максимального индекса в списке:
	nRandom = Int((oFilesTxt.Count * Rnd) + 0) ' генерировать целое случайное число от 0 до oFilesTxt.Count-1.

	' Использовать файл с индексом, равным полученному числу:
	sFileTxt = oFilesTxt.Items()(nRandom)
	WScript.Echo "oFilesTxt(", nRandom, ")=", sFileTxt

	' Например, для 5 TXT-файлов и nRandom = 1: sFileTxt = "файл_02.txt".

	' ---------------------------------------------------------------------
	' 1. В каждом файле оставить только 3 первых строки, остальные удалить.
	' ---------------------------------------------------------------------
	With goFSO.OpenTextFile(sFileTxt, 1) 'ForReading
		For iLine = 0 To nLineCount-1
			' ---------------------------------------------------------------------------------------------------------
			' 2. В каждом файле оставить только ссылки. Цифры, которые расположены перед ссылками и символ ";" удалить.
			' ---------------------------------------------------------------------------------------------------------
			aLine = Split(.ReadLine(), ";") 'строка "234324;Ссылка1" => массив [234324, Ссылка1]
			If UBound(aLine) > 0 Then ' проверка, что строка не пустая и есть хотя бы 1 разделитель ";"
				sLineRes = sLineRes & aLine(1) & vbCrLf '"Ссылка1"
			End If
		Next 'iLine
		.Close
	End With

	' Удалить этот файл из списка:
	oFilesTxt.Remove oFilesTxt.Keys()(nRandom)

	' В нашем примере oFilesTxt.Items станет = [файл_01.txt, файл_03.txt, файл_04.txt, файл_05.txt].
	' Поскольку имя обработанного файла удалено из списка, можно не беспокоиться о повторении случайного числа.
Next 'iFile

' ... запись в файл-результат:
With goFSO.OpenTextFile(sFileRes, 2, True) 'Write, Create
	.Write(sLineRes)
	.Close
End With
	
WScript.Echo "Создан файл", sFileRes

' Выход с кодом возврата
WScript.Quit(0)