Merge binary files based on dates in the filenames - windows

I have multiple files in a directory with the formatL: data_yyyy_mm_dd_ss.info.dat
I am trying to write a batch script that will ask for user start/end date input (and assuming the user's input will match the format of the files yyyy_mm_dd) I then want the script to do a merge of all relevant files.
Ex Files: data_2013_12_01_32.info.dat; data_2013_12_02_44.info.dat; data_2013_12_03_22.info.dat; data_2013_12_04_33.info.dat; data_2013_12_05_52.info.dat; data_2013_12_06_12.info.dat
Ex Script:
#echo off
set /p start= Start Date:
set /p end= End Date:
copy /b ????
Ex User Input: 2013_12_02 and 2013_12_05
Ex Output: Merge files 2013_12_02 through 2013_12_05.
Thanks for any help in advance.

I'll assume you know what you are doing, and it is valid to concatenate your binary files.
The following should work, as long as all files that match the FOR filter are candidates for merging, and as long as the user always enters dates in the correct format (yyyy_mm_dd):
#echo off
setlocal
set /p start= Start Date:
set /p end= End Date:
set "out=output.info.dat"
for %%F in (data_????_??_??_??.info.dat) do (
for /f "tokens=1-3 delims=_" %%A in ("%%F") do (
if "%%A_%%B_%%C" geq "%start%" if "%%A_%%B_%%C" leq "%end%" (
copy /b "%out%"+"%%F" "%out%" /b
)
)
)
As an alternative to using COPY, you should be able to redirect the output of FINDSTR and get the same result.
#echo off
setlocal
set /p start= Start Date:
set /p end= End Date:
>output.info.dat (
for %%F in (data_????_??_??_??.info.dat) do (
for /f "tokens=1-3 delims=_" %%A in ("%%F") do (
if "%%A_%%B_%%C" geq "%start%" if "%%A_%%B_%%C" leq "%end%" (
findstr "^" "%%F"
)
)
)
)
I'm not sure which is faster, COPY or FINDSTR

It has no checks on almost anything. Be careful.
Ask for start and end date. Generate min and max files based on data input and iterate over the ordered list of files. If a file is found, it is readed with findstr (first option was type command, but it's 4 to 5 times slower) and all output redirected to final merged file.
#echo off
setlocal enableextensions disabledelayedexpansion
set /p "start=Start date :"
set /p "end=End date :"
set "start=data_%start%_00.info.dat"
set "end=data_%end%_99.info.dat"
>merged.info.dat (
for /f "tokens=*" %%f in ('dir /on /b "data_????_??_??_??.info.dat"') do (
if /i "%%f" geq "%start%" if /i "%%f" leq "%end%" findstr "^" "%%f"
)
)
For a long list of files, search can be abreviated. In this moment i don't know how to skip the initial non matching part of the list of files, but the final part can be easyly skipped
#echo off
setlocal enableextensions disabledelayedexpansion
set /p "start=Start date:"
set /p "end=End date:"
set "start=data_%start%_00.info.dat"
set "end=data_%end%_99.info.dat"
>output.info.dat (
for /f "tokens=*" %%f in ('dir /on /b "data_????_??_??_??.info.dat"') do (
if /i "%%f" geq "%start%" if /i "%%f" leq "%end%" ( findstr "^" "%%f" ) else ( goto :endMerge )
)
)
:endMerge

Related

Use a Batch File to list files and allow the user to select which file to copy into a new destination

I am a newbie to Windows Scripting.
I am trying to list some txt files in several sub directories & want to copy a user selected file to a new destination. Please note that the file name is unique in different locations.
I got the first part to work (Listing out the files & locations) using the following script, but I am unable to copy the selected file to the new location.
#ECHO OFF
SET index=1
SETLOCAL ENABLEDELAYEDEXPANSION
SET FFPath="C:\Scripts - Backup Server\DKXpress_bkp"
SET NewPath=C:\DKServer
ECHO Recursively searching %FFPath%
echo.
FOR /F "delims=" %%f in ('DIR %FFPath%\*.txt /a:-d /s /b') DO (
SET file!index!=%%f
ECHO !index! - %%f
SET /A index=!index!+1
)
SETLOCAL DISABLEDELAYEDEXPANSION
SET /P selection="select file by number:"
SET file%selection% >nul 2>&1
IF ERRORLEVEL 1 (
ECHO invalid number selected
EXIT /B 1
)
SET NewFile=file%selection%
ECHO Copying %NewFile% to %NewPath%
ECHO.
COPY /Y "%NewFile%" "%NewPath%"
ECHO.
PAUSE
I think I am doing this part wrong
SET NewFile=file%selection%
Thank you all in advance
You don't need to set an index variable or delayed expansion, if you let Find do the work for you:
#Echo Off
Set "FFPath=C:\Scripts - Backup Server\DKXpress_bkp"
Set "NewPath=C:\DKServer"
Echo Recursively searching %FFPath%
Echo=
For /F "Delims==" %%A In ('"Set File[ 2>Nul"') Do Set "%%A="
For /F "Tokens=1* Delims=]" %%A In (
'"Dir /B/S/A-D-S-L "%FFPath%\*.txt" 2>Nul|Find /N /V """') Do (
Echo %%A] %%B
Set "File%%A]=%%B"
)
Echo=
Set /P "#=Select file by number: "
Echo=
For /F "Tokens=1* Delims==" %%A In ('"Set File[%#%] 2>Nul"') Do (
Echo Copying %%B to %NewPath%&Echo=
Copy /Y "%%B" "%NewPath%"
GoTo :End
)
Echo Invalid number selected
:End
Echo=
Pause
You need to use delayed expansion to get the file name assigned to the variable correctly.
SET NewFile=!file%selection%!
Remove the setlocal to disable delayed expansion.
You can try something like that :
#ECHO OFF
:Main
cls
SET index=1
SETLOCAL ENABLEDELAYEDEXPANSION
SET FFPath="C:\Scripts - Backup Server\DKXpress_bkp"
SET "NewPath=C:\DKServer"
ECHO Recursively searching %FFPath%
echo.
FOR /F "delims=" %%f in ('DIR %FFPath%\*.txt /a:-d /s /b') DO (
SET filepath[!index!]=%%f
ECHO [!index!] - %%~nxf - %%f
SET /A index=!index!+1
)
echo(
echo select file by number :
set /p Input=""
For /L %%i in (1,1,%index%) Do (
If "%INPUT%" EQU "%%i" (
ECHO Copying "!filepath[%%i]!" to "!NewPath!"
COPY /Y "!filepath[%%i]!" "!NewPath!"
)
)
echo Copying another file ? (Y = Yes or N = No) ?
set /p input2=""
If /I "!input2!"=="Y" (
goto :Main
) else (
goto :eof
)

Batch file to output lines after string match, filename

I have a directory of thousands of text files that begin with tape* (they're output files from an old Fortran program) and I need to extract 4 lines from each file as well as the filename that they were extracted from. The 4 lines begin 4 lines down from a predictable string, but we can use "Header" for this example:
tape1:
First line
...
Header
Trash1
Trash2
Trash3
Data1
Data2
Data3
Data4
...
I don't care about anything before or after those 4 data lines, but I also want the filename (i.e. "tape1") output after each Data line, like so:
Data1 tape1
Data2 tape1
Data3 tape1
Data4 tape1
Data5 tape2
Data6 tape2
...
Any thoughts on an easy Windows batch file to do this on all tape* files in a directory?
#echo off
setlocal EnableDelayedExpansion
for /F "tokens=1,2 delims=:" %%a in ('findstr /N "Header" tape*') do (
(for /L %%i in (-2,1,%%b) do set /P "="
for /L %%i in (1,1,4) do set /P "line=" & echo !line! %%a
) < "%%a"
)
#echo off
cls
rem get a list of file names sorted alphabetically by name
dir /b tape* /ON>.\Files.txt
rem delete any existing output from any previous run
if exist .\FoundData.txt del .\FoundData.txt
SET DataFileName=
SET LineData=
SET FoundData=
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /F %%A IN (.\Files.txt) DO (
SET DataFileName=%%A
FOR /F "tokens=1,2* " %%B IN (!DataFileName!) DO (
rem use the following line to get just the first word on the line
rem SET LineData=%%B
rem use the following line to get the entire line
SET LineData=%%B %%C %%D
rem determine if the line contains "DATA" (case insensitive) and if so capture it
if "%FoundData%"=="" ( ECHO !LineData!|FIND /I "DATA">Nul && (SET FoundData=!LineData! ) || (SET FoundData=) )
rem if the line contained the data we want output it to the output file
IF NOT "!FoundData!"=="" (
(ECHO !FoundData! !DataFileName!>>.\FoundData.txt)
)
)
SET LineData=
SET FoundData=
)
set DataFileName=
)
ENDLOCAL
if not exist .\FoundData.txt echo No data found>.\FoundData.txt
start notepad .\FoundData.txt
You could try the following:
#echo off
for %%F in (".\tape*.txt") do (
for /F "delims=:" %%N in ('findstr /N /X /C:"Header" "%%~F"') do (
call :SUB "%%~F" %%N
)
)
exit /B
:SUB
set /A "SKIP=%~2+3"
for /F "skip=%SKIP% usebackq delims=" %%L in ("%~1") do (
echo(%%L %~n1
)
exit /B
Here is an alternative approach:
#echo off
for %%F in (".\tape*.txt") do (
for /F %%C in ('^< "%%~F" find /C /V ""') do (
set /A "POS=0" & set "NAME=%%~nF"
< "%%~F" (
setlocal EnableDelayedExpansion
for /L %%I in (1,1,%%C) do (
set "LINE=" & set /P LINE=""
if "!LINE!"=="Header" (
set /A "POS=%%I+3"
)
if !POS! GTR 0 if %%I GTR !POS! (
echo(!LINE! !NAME!
)
)
endlocal
)
)
)

Scraping all file names with their creation and modification date from a directory

I am running this bat script, fileNames.bat > print.txt, to get all file Names.
#echo off
setlocal disableDelayedExpansion
:: Load the file path "array"
for /f "tokens=1* delims=:" %%A in ('dir /s /b^|findstr /n "^"') do (
set "file.%%A=%%B"
set "file.count=%%A"
)
:: Access the values
setlocal enableDelayedExpansion
for /l %%N in (1 1 %file.count%) do echo !file.%%N!
However, I would also like to get in the output the creation and modification date for each file path.
My current output looks like that:
C:\Programs\FirefoxPortable\fileNames.bat
C:\Programs\FirefoxPortable\Firefox
C:\Programs\FirefoxPortable\print.txt
C:\Programs\FirefoxPortable\Firefox\App
C:\Programs\FirefoxPortable\Firefox\Data
Any suggestions, how to get the creation and modification date next to each file path?
I appreciate your replies!
Following .bat script produces a csv-like output with | vertical line delimited values of next pattern:
type|creation datetime|modification datetime|full path|
Delayed expansion kept disabled as there are file names containig ! exclamation mark(s): see the call set trick in Variables: extract part of a variable (substring) and in CALLing internal commands.
Note that retrieving creation date and time discriminates between folders and files.
%%G tokenization could vary for another regional settings!
#ECHO OFF
#SETLOCAL enableextensions disableDelayedExpansion
:: Set the working directory and store the previous folder/path
pushd d:\test
:: Load the file path "array"
set "file.count=0"
for /f "tokens=1* delims=:" %%A in ('dir /s /b^|findstr /n "^"') do (
set "file.%%A=%%B"
set "file.count=%%A"
)
:: Restore path/folder most recently stored by the PUSHD command
popd
:: Access the values (keep )
set /A "ii=0"
:loop
if %ii% GEQ %file.count% goto :loopend
set /A "ii+=1"
call set "file.curr=%%file.%ii%%%"
if exist "%file.curr%\" (
rem folder
for %%g in ("%file.curr%") do (
set "defined="
for /F "skip=5 tokens=1,2" %%G in ('
dir /-c /a:d /t:C "%file.curr%"
') do (
if not defined defined (
echo FLDR^|%%G %%H^|%%~tg^|%file.curr%^|
set "defined=%%~tg %%G %%H"
)
)
)
) else (
rem file
for %%g in ("%file.curr%") do (
set "defined="
for /F "skip=5 tokens=1,2" %%G in ('
dir /-c /a:-d /t:C "%file.curr%"
') do (
if not defined defined (
echo FILE^|%%G %%H^|%%~tg^|%file.curr%^|
set "defined=%%~tg %%G %%H"
)
)
)
)
goto :loop
:loopend
ENDLOCAL
goto :eof
My current output looks as follows (reduced to reasonable size):
==>D:\bat\SO\31824138.bat
FILE|26.07.2015 11:02|26.07.2015 11:02|d:\test\File N.txt|
FLDR|24.07.2015 20:21|05.08.2015 18:44|d:\test\set|
FILE|24.07.2015 20:23|24.07.2015 20:23|d:\test\set\hklm.txt|
FILE|24.07.2015 20:26|24.07.2015 20:30|d:\test\set\regs.txt|
FILE|05.08.2015 18:44|05.08.2015 18:45|d:\test\set\t!exclam!t.txt|
==>

Batch split a text file

I have this batch file to split a txt file:
#echo off
for /f "tokens=1*delims=:" %%a in ('findstr /n "^" "PASSWORD.txt"') do for /f "delims=~" %%c in ("%%~b") do >"text%%a.txt" echo(%%c
pause
It works but it splits it line by line. How do i make it split it every 5000 lines. Thanks in advance.
Edit:
I have just tried this:
#echo off
setlocal ENABLEDELAYEDEXPANSION
REM Edit this value to change the name of the file that needs splitting. Include the extension.
SET BFN=passwordAll.txt
REM Edit this value to change the number of lines per file.
SET LPF=50000
REM Edit this value to change the name of each short file. It will be followed by a number indicating where it is in the list.
SET SFN=SplitFile
REM Do not change beyond this line.
SET SFX=%BFN:~-3%
SET /A LineNum=0
SET /A FileNum=1
For /F "delims==" %%l in (%BFN%) Do (
SET /A LineNum+=1
echo %%l >> %SFN%!FileNum!.%SFX%
if !LineNum! EQU !LPF! (
SET /A LineNum=0
SET /A FileNum+=1
)
)
endlocal
Pause
exit
But i get an error saying: Not enough storage is available to process this command
This will give you the a basic skeleton. Adapt as needed
#echo off
setlocal enableextensions disabledelayedexpansion
set "nLines=5000"
set "line=0"
for /f "usebackq delims=" %%a in ("passwords.txt") do (
set /a "file=line/%nLines%", "line+=1"
setlocal enabledelayedexpansion
for %%b in (!file!) do (
endlocal
>>"passwords_%%b.txt" echo(%%a
)
)
endlocal
EDITED
As the comments indicated, a 4.3GB file is hard to manage. for /f needs to load the full file into memory, and the buffer needed is twice this size as the file is converted to unicode in memory.
This is a fully ad hoc solution. I've not tested it over a file that high, but at least in theory it should work (unless 5000 lines needs a lot of memory, it depends of the line length)
AND, with such a file it will be SLOW
#echo off
setlocal enableextensions disabledelayedexpansion
set "line=0"
set "tempFile=%temp%\passwords.tmp"
findstr /n "^" passwords.txt > "%tempFile%"
for /f %%a in ('type passwords.txt ^| find /c /v "" ') do set /a "nFiles=%%a/5000"
for /l %%a in (0 1 %nFiles%) do (
set /a "e1=%%a*5", "e2=e1+1", "e3=e2+1", "e4=e3+1", "e5=e4+1"
setlocal enabledelayedexpansion
if %%a equ 0 (
set "e=/c:"[1-9]:" /c:"[1-9][0-9]:" /c:"[1-9][0-9][0-9]:" /c:"!e2![0-9][0-9][0-9]:" /c:"!e3![0-9][0-9][0-9]:" /c:"!e4![0-9][0-9][0-9]:" /c:"!e5![0-9][0-9][0-9]:" "
) else (
set "e=/c:"!e1![0-9][0-9][0-9]:" /c:"!e2![0-9][0-9][0-9]:" /c:"!e3![0-9][0-9][0-9]:" /c:"!e4![0-9][0-9][0-9]:" /c:"!e5![0-9][0-9][0-9]:" "
)
for /f "delims=" %%e in ("!e!") do (
endlocal & (for /f "tokens=1,* delims=:" %%b in ('findstr /r /b %%e "%tempFile%"') do #echo(%%c)>passwords_%%a.txt
)
)
del "%tempFile%" >nul 2>nul
endlocal
EDITED, again: Previous code will not correctly work for lines starting with a colon, as it has been used as a delimiter in the for command to separate line numbers from data.
For an alternative, still pure batch but still SLOW
#echo off
setlocal enableextensions disabledelayedexpansion
set "nLines=5000"
set "line=0"
for /f %%a in ('type passwords.txt^|find /c /v ""') do set "fileLines=%%a"
< "passwords.txt" (for /l %%a in (1 1 %fileLines%) do (
set /p "data="
set /a "file=line/%nLines%", "line+=1"
setlocal enabledelayedexpansion
>>"passwords_!file!.txt" echo(!data!
endlocal
))
endlocal
Test this: the input file is "file.txt" and output files are "splitfile-5000.txt" for example.
This uses a helper batch file called findrepl.bat - download from: https://www.dropbox.com/s/rfdldmcb6vwi9xc/findrepl.bat
Place findrepl.bat in the same folder as the batch file or on the path.
#echo off
:: splits file.txt into 5000 line chunks.
set chunks=5000
set /a s=1-chunks
:loop
set /a s=s+chunks
set /a e=s+chunks-1
echo %s% to %e%
call findrepl /o:%s%:%e% <"file.txt" >"splitfile-%e%.txt"
for %%b in ("splitfile-%e%.txt") do (if %%~zb EQU 0 del "splitfile-%e%.txt" & goto :done)
goto :loop
:done
pause
A limitation is the number of lines in the file and the real largest number is 2^31 - 1 where batch math tops out.
#echo off
setlocal EnableDelayedExpansion
findstr /N "^" PASSWORD.txt > temp.txt
set part=0
call :splitFile < temp.txt
del temp.txt
goto :EOF
:splitFile
set /A part+=1
(for /L %%i in (1,1,5000) do (
set "line="
set /P line=
if defined line echo(!line:*:=!
)) > text%part%.txt
if defined line goto splitFile
exit /B
If the input file has not empty lines, previous method may be modified in order to run faster.

How to create a unique output filename for Windows Script?

I am trying to create a windows script that should generate this kind of filename everytime I run it: filename1, filename2, filename3 and so on. Here is what I have so far:
(
#echo off
wmic logicaldisk get size,freespace,caption
) > disk.txt
I hope you can help me. Thanks!!
:: make a tempfile
:maketemp
SET "tempfile=%temp%\%random%"
IF EXIST "%tempfile%*" (GOTO maketemp) ELSE (ECHO.>"%tempfile%a")
You now have any number of filenames available.
%tempfile%a exists and is empty, but %tempfile%anythingelse should be available for use.
#ECHO OFF
SETLOCAL
SET "basename=filename"
SET /a outname=0
:genloop
SET /a outname+=1
IF EXIST "%basename% %outname%.txt" GOTO genloop
SET "outname=%basename% %outname%.txt"
ECHO %outname%
GOTO :EOF
Ah - increment the destination filename on each run. This should do that. It's not actually creating a file - you'd need to create the file %outname% each time to have it increment...
(the space between %basename% and %outname% is optional, of course - omit it if desired.)
edited to include .txt
This will give you up to 1000 filenames but you can go higher, up to 2 Billion, but the higher you go the longer the delay will be before it picks a filename.
#echo off
for /L %%a in (1,1,1000) do if not defined filename if not exist "filename%%a.txt" set "filename=filename%%a.txt"
(
wmic logicaldisk get size,freespace,caption
) > "%filename%"
#echo off
setlocal enableextensions
call :getNextFilename "filename*.txt" nextFilename
echo %nextFilename%
echo test > "%nextFilename%"
call :getNextFilename "%cd%\filename*.txt" nextFilename
echo %nextFilename%
echo test > "%nextFilename%"
endlocal
exit /b
:getNextFilename whatToSearch returnVariable
setlocal enableextensions enabledelayedexpansion
for /f %%a in ("$\%~1"
) do for /f "tokens=1,* delims=*" %%b in ("%%~nxa"
) do ( set "left=%%b" & set "right=%%c" )
set "max=0"
for %%a in ("%~1"
) do for /f "tokens=1 delims=%left%%right% " %%b in ("%%~nxa"
) do for /f "tokens=* delims=0 " %%c in ("0%%~b"
) do if %%~c geq !max! set /a "max=%%c+1"
endlocal & set "%~2=%~dp1%left%%max%%right%" & exit /b
This should find the next file in sequence independently of the existence of holes in the numeration of the files. A path can be included or omitted. The * will be used as the placeholder for the numeration. BUT this will not work if files or included paths have "problematic" characters.
If the date/time of creation of the file can be considered, then this version can be optimized as
:getNextFilename whatToSearch returnVariable
setlocal enableextensions disabledelayedexpansion
for /f %%a in ("$\%~1"
) do for /f "tokens=1,* delims=*?" %%b in ("%%~nxa"
) do ( set "left=%%b" & set "right=%%c" )
set "max=0"
for /f "delims=" %%a in ('dir /tc /o-d /b "%~1" 2^>nul'
) do for /f "tokens=1 delims=%left%%right% " %%b in ("%%~nxa"
) do for /f "tokens=* delims=0 " %%c in ("0%%~b"
) do set /a "max=%%c+1" & goto done
:done
endlocal & set "%~2=%~dp1%left%%max%%right%" & exit /b
that will take the latest created instance of the file set.
I finally figured out where to put the .txt extension. This is from #Magoo's answer but I wanted the file to be a text file so I placed the .txt twice in order for it to work properly.
#ECHO OFF
SETLOCAL
SET "basename=DISK-OUT"
SET /a outname=0
:genloop
SET /a outname+=1
IF EXIST "%basename% %outname%.txt" GOTO genloop
SET "outname=%basename% %outname%.txt"
(
wmic logicaldisk get size,freespace,caption
) > "%outname%"
GOTO :EOF

Resources