For Loop High CSRSS CPU - windows

I have a batch file that takes an input file (in this case the file is a flat text file comprised of a file listing from a specified set of Windows XP system directories).
Then through a combination of a for loop to iterate through each file listed in the text file), setting a variable, using several call commands (in this case 9), and the find command to search the text file for files that appear in the output of other flat files.
The problem I am running into is that csrss.exe is using quite a bit of CPU processing. In reading some other articles it seems that csrss has a relationship with command line processing.
What I want to know is if there is a better way to do this that will:
Reduce overall script time.
Reduce CPU load.
Below is a sample of the script:
:: ------------------------------------------------------
:: Running Multiple Commands
:: ------------------------------------------------------
for /f "delims=?" %%A in (dir-selected_tmp.txt) do (
set filepath=%%A
call :filepathparse
call :md5hashchk
call :versionchk
call :densitychk
call :processchk
call :mutexchk
call :networkchk
call :injectedchk
call :persistservicechk
call :servicedllchk
call :persistrunchk
call :persistothchk
call :unsigneddllchk
call :filesysag
)
goto :comparison
:: ####################################################################
:filepathparse
for %%B in ("%filepath%") do (
set filename=%%~nxB
)
goto :eof
:: ####################################################################
:md5hashchk
for /f "delims= " %%b in ('md5deep.exe "%filepath%"') do set hashvalue=%%b
goto :eof
:: ####################################################################
:versionchk
for /f "delims= " %%e in ('sigcheck.exe /accepteula -q -n "%filepath%"') do set versionvalue=%%e
goto :eof
:: ####################################################################
:densitychk
for /f "delims= " %%d in ('densityscout.exe -p 0.1 "%filepath%"') do set densityvalue=%%d
goto :eof
:: ####################################################################
:processchk
find.exe /I "%filepath%" %temp_outpath%\wmic-processes.txt > nul 2>&1 && goto processexist
set isprocess=NoActiveProc
goto :eof
:processexist
set isprocess=ActiveProc
goto :eof
:: ####################################################################
:mutexchk
if "%startup_chk%"=="-b" (
goto :mutexbl
)
find.exe /I "%filepath%" %temp_outpath%\filesystem-handles_compared.txt > nul 2>&1 && goto mutexexist
set ismutex=NoNewActiveMutex
goto :eof
:mutexexist
set ismutex=NewActiveMutex
goto :eof
:mutexbl
find.exe /I "%filepath%" %parsed_outpath%\filesystem-handles.txt > nul 2>&1 && goto mutexexist
set ismutex=NoNewActiveMutex
goto :eof
:mutexexist
set ismutex=NewActiveMutex
goto :eof
:: ####################################################################
:networkchk
find.exe /I "%filename%" %temp_outpath%\tcpvcon_tmp.txt > nul 2>&1 && goto networkexist
set isnetwork=NoActiveNetwork
goto :eof
:networkexist
set isnetwork=ActiveNetwork
goto :eof
:: ####################################################################
:injectedchk
find.exe /I "%filepath%" %temp_outpath%\injecteddll_tmp.txt > nul 2>&1 && goto injectedexist
set isinjected=NoActiveInject
goto :eof
:injectedexist
set isinjected=ActiveInject
goto :eof
:: ####################################################################
:persistservicechk
find.exe /I "%filepath%" %temp_outpath%\autoruns-services_tmp.txt > nul 2>&1 && goto serviceexist
set isservice=NoInstalledService
goto :eof
:serviceexist
set isservice=InstalledService
goto :eof
:: ####################################################################
:servicedllchk
find.exe /I "%filepath%" %temp_outpath%\registry_hklm_installed_service_dlls_final.txt > nul 2>&1 && goto servicedllexist
set isservicedll=NoInstalledServiceDLL
goto :eof
:servicedllexist
set isservicedll=InstalledServiceDLL
goto :eof
:: ####################################################################
:persistrunchk
find.exe /I "%filepath%" %temp_outpath%\autoruns-run_tmp.txt > nul 2>&1 && goto runpersistexist
set isrunpersist=NoRunPersist
goto :eof
:runpersistexist
set isrunpersist=RunPersist
goto :eof
:: ####################################################################
:persistothchk
find.exe /I "%filepath%" %temp_outpath%\autoruns-oth_tmp5.txt > nul 2>&1 && goto persistothexist
set isothpersist=NoOtherPersist
goto :eof
:persistothexist
set ispersist=OtherPersist
goto :eof
:: ####################################################################
:unsigneddllchk
find.exe /I "%filepath%" %temp_outpath%\listdlls_temp2.txt > nul 2>&1 && goto unsigneddllexist
set isunsigneddll=NoUnSignedProcDLL
goto :eof
:unsigneddllexist
set isunsigneddll=UnSignedProcDLL
goto :eof
:: ####################################################################
:filesysag
:: Compiling Into Syslog Format
echo %datestamp%^|%currtime%^|%computername%^|%currip%^|%username%^|%lastlogintime%^|"%filepath%"^|%hashvalue%^|%versionvalue%^|%densityvalue%^|%isprocess%^|%ismutex%^|%isnetwork%^|%isinjected%^|%isservice%^|%isservicedll%^|%isrunpersist%^|%isothpersist%^|%isunsigneddll% >> %syslog_outpath%\%computername%-syslog.txt
:: Compiling Into Parsed Format
echo "%filepath%"^|%hashvalue%^|%versionvalue%^|%densityvalue%^|%isprocess%^|%ismutex%^|%isnetwork%^|%isinjected%^|%isservice%^|%isservicedll%^|%isrunpersist%^|%isothpersist%^|%isunsigneddll% >> %parsed_outpath%\%computername%-filesystem.txt
goto :eof

Sadly, your reluctance to provide further details means we are severely retricted on the amount of help we can offer.
I'd recommend:
First, build a list of the filepaths yu are interested in
(for /f "delims=?" %%A in (dir-selected_tmp.txt) do echo %%A )>paths_of_interest.txt
Then build temporary files containing a subset of the log data
findstr /I /g:paths_of_interest.txt %temp_outpath%\processes.txt >"%temp%\processes.x.txt"
Then re-process according to your original scheme, instead using paths_of_interest.txt and "%temp%\processes.x.txt"
This should reduce the amount of repetition, especially if you have long files.
:: ------------------------------------------------------
:: Running Multiple Commands
:: ------------------------------------------------------
:: Pre-processing
SET /a step=1000
SET /a dirstart=0
SET "dirskip="
:mainloop
SET /a dirsleft=0
SETLOCAL enabledelayedexpansion
(
for /f "%dirskip%delims=?" %%A in (dir-selected_tmp.txt) do (
SET /a dirsleft+=1
IF !dirsleft! leq %step% ECHO %%A
)
)>temp_dirs.txt
endlocal&SET /a dirsleft=%dirsleft%
FINDSTR /i /l /g:temp_dirs.txt "%temp_outpath%\wmic-processes.txt" >temp_filepathparse.txt
if "%startup_chk%"=="-b" (
FINDSTR /i /l /g:temp_dirs.txt "%parsed_outpath%\filesystem-handles.txt" >temp_mutexchk.txt
) ELSE (
FINDSTR /i /l /g:temp_dirs.txt "%temp_outpath%\filesystem-handles_compared.txt" >temp_mutexchk.txt
)
FINDSTR /i /l /g:temp_dirs.txt "%temp_outpath%\tcpvcon_tmp.txt" >temp_networkchk.txt
FINDSTR /i /l /g:temp_dirs.txt "%temp_outpath%\injecteddll_tmp.txt" >temp_injectedchk.txt
FINDSTR /i /l /g:temp_dirs.txt "%temp_outpath%\autoruns-services_tmp.txt" >temp_persistservicechk.txt
FINDSTR /i /l /g:temp_dirs.txt "%temp_outpath%\registry_hklm_installed_service_dlls_final.txt" >temp_servicedllchk.txt
FINDSTR /i /l /g:temp_dirs.txt "%temp_outpath%\autoruns-run_tmp.txt" >temp_persistrunchk.txt
FINDSTR /i /l /g:temp_dirs.txt "%temp_outpath%\autoruns-oth_tmp5.txt" >temp_persistothchk.txt
FINDSTR /i /l /g:temp_dirs.txt "%temp_outpath%\listdlls_temp2.txt" >temp_unsigneddllchk.txt
for /f "delims=" %%A in (temp_dirs.txt) do (
set "filepath=%%A"
call :filepathparse
....
call :filesysag
)
SET /a dirstart=%dirstart%+%step%
SET /a dirsleft=%dirsleft%-%step%
IF dirsleft gtr 0 SET dirskip=SKIP=%dirstart%&GOTO mainloop
goto :comparison
Then simply replace the filename in each of your routines :processchk to :unsigneddllchk with the corresponding temp_whatever.txt file created in the pre-processing. The switch on mutex is already done in the pre-processing.
This should reduce the searching done by the find command and hence the overall run time by selecting only those lines containing the target strings into the temp_* files created.
[Edit in response to further information provided]
Note the preprocessing block before the first findstr has been changed and that there is now a post-processing block between call :filesysag ) and goto :comparison
The object here is to reduce the size of the temp_dirs.txt file. I've arbitrarily chosen 1,000 (the size of step) but have no real idea of how big step should be. The larger it is, the quicker the process will be, but if it's too large, temp_dirs.txt will be too big for findstr to handle.
Note the use of the parsing trick on the line endlocal&SET /a dirsleft=%dirsleft% which uses the parsing system to move the modified value of dirsleft out of the setlocal/endlocal block.

Related

Single line nested loop

I am experimenting with a single line cmd /c to get an inner loop without branching. (Actually i have the showLines routine which performs the loop.) I know its worst for performance but i want to know if its possible to get it run without quotes. Currently it raises "%G was unexpected at this time" error. So, it needs some correct escaping or expansion of variables.
#echo off
setlocal enableDelayedExpansion
set "param=%~1"
netstat -aonb | findstr /n $ > tmpFile_Content
for /F "tokens=*" %%A in ('type tmpFile_Content ^| findstr /r /c:"%param%" /i') do (
SET line=%%A
for /F "tokens=1 delims=:" %%I in ("!line!") DO (
set /a LineNum=%%I
rem set /a NextLineNum=LineNum+1
)
set /a lineNum=!LineNum!-1
if !lineNum!==0 ( set param="tokens=*" ) else ( set param="tokens=* skip=!lineNum!" )
rem FOLLOWING WORKS FINE in quotes
cmd /q /v:on /c "#echo off && setlocal enableDelayedExpansion && set cnt=2 && for /F %%param%% %%B in (tmpFile_Content) do ( echo %%B && set /a cnt-=1 >nul && if ^!cnt^!==0 exit /b )"
rem Following does not work even though cmd should take the rest of arguments after /c
cmd /q /v:on /c setlocal enableDelayedExpansion && FOR /F "tokens=*" %%C IN ('echo !param!') DO ( for /F %%C %%G in (tmpFile_Content) do ( echo %%G && set /a cnt-^=1 >nul && if ^!cnt^!==0 exit /b ))
rem call :showLines !LineNum!
)
del tmpFile_Content
goto :eof
:showLines
set /a lineNum=%1-1
set cnt=2
for /F "tokens=* skip=%lineNum%" %%B in (tmpFile_Content) do (
echo %%B
set /a cnt-=1
if !cnt!==0 goto exitLoop
)
:exitLoop
exit /b
to construct for loops with variable parameters, you essentially need to define and execute them as a macro. Eg:
#Echo Off & Setlocal ENABLEdelayedExpasnion
Set param="tokens=* delims="
Set "test=string line"
Set For=For /F %param% %%G in ("^!test^!") Do echo %%G
%For%
Of course you could go even further, and build the entire for loop with another for loop macro on the fly.
UPDATE:
Method for defining conditional concatenation of commands now exampled
Syntax simplified to allow the same usage form for regular expansion and within codeblocks by having the constructor macro call a subroutine to expand the new for loop once it's constructed.
delayed concatenation variable usage simplified to avoid the escaping requirement
#Echo off
::: { Macro Definition
Setlocal DisabledelayedExpansion
(set \n=^^^
%= This creates an escaped Line Feed - DO NOT ALTER =%
)
::: [ For Loop Constructor macro. ] For advanced programmers who need to use dynamic for loop options during code blocks.
::: - usage: %n.For%{For loop options}{variable set}{For Metavariable}{commands to execute}
::: - use delayed !and! variable to construct concatenated commands in the new for loop.
Set n.For=For %%n in (1 2) Do If %%n==2 (%\n%
Set FOR=%\n%
For /F "tokens=1,2,3,4 Delims={}" %%1 in ("!mac.in!") Do (%\n%
Set "FOR=For /F %%1 %%3 in ("!%%2!") Do (%%~4)"%\n%
)%\n%
Call :Exc.For%\n%
)Else Set mac.in=
Set "and.=&&"
Set "and=!and.!"
::: } End macro definition.
Setlocal EnableDelayedExpansion& rem // required to expand n.For constructor macro
::: - Usage examples:
Set "example=is a string line"
%n.For%{"tokens=* delims="}{example}{%%G}{Echo/%%~G}
%n.For%{"tokens=1,2,3,4 delims= "}{example}{%%G}{"Echo/%%~J %%~G %%~H %%~I !and! Echo/%%~I %%~G %%~H %%~J"}
Set "example2=Code block example"
For %%a in (1 2 3) do (
%n.For%{"Tokens=%%a Delims= "}{example2}{%%I}{"For /L %%# in (1 1 4) Do (Set %%I[%%#]=%%a%%#) !and! Set %%I[%%#]"}
)
Pause > Nul
Goto :EOF
:Exc.For
%FOR%
Exit /B
Example output:
is a string line
line is a string
string is a line
Code[1]=11
Code[2]=12
Code[3]=13
Code[4]=14
block[1]=21
block[2]=22
block[3]=23
block[4]=24
example[1]=31
example[2]=32
example[3]=33
example[4]=34
Finally, i came up with following to execute code given in string for anyone interested experimentally and may be for some insight about escaping and expansion.
I use macro instead of cmd which will be much more faster i think(not sure because its said that "call" also causes launch of cmd).
So, it is a simple one-liner without a lot of extra code. But things easily become complicated and when extra escaping and special characters used then #T3RR0R's macro routine would be a necessity.
#echo off
setlocal EnableDelayedExpansion
set "param=%~1"
netstat -aonb | findstr /n $ > tmpFile_Content
for /F "tokens=*" %%A in ('type tmpFile_Content ^| findstr /r /c:"%param%" /i') do (
SET line=%%A
for /F "tokens=1 delims=:" %%I in ("!line!") DO (
set /a LineNum=%%I
rem set /a NextLineNum=LineNum+1
)
set /a lineNum=!LineNum!-1
rem CORRECT QUOTING
if !lineNum!==0 ( set "param="tokens=*"" ) else ( set "param="tokens=* skip=!lineNum!"" )
rem FOLLOWING WORKS FINE in quotes
rem cmd /q /v:on /c "set cnt=2 && for /F ^!param^! %%B in (tmpFile_Content) do ( echo %%B && set /a cnt-=1 >nul && if ^!cnt^!==0 exit /b )"
rem For reading !cnt! use !x!cnt!x!.
rem Only one extra variable used, and in routine its replaced with !(exclamation) for our "cnt" variable.
set "x=^!x^!"
call :ExecCode "set cnt=2 && for /F ^!param^! %%%%B in (tmpFile_Content) do (echo %%%%B && set /a cnt=!x!cnt!x!-1 >nul && if !x!cnt!x!==0 (exit /b) )"
rem call :showLines !LineNum!
)
del tmpFile_Content
goto :eof
:ExecCode
setlocal
rem Replace with exclamation for variable
set "x=^!"
set "s=%~1"
%s%
endlocal
exit /b
:showLines
set /a lineNum=%1-1
set cnt=2
for /F "tokens=* skip=%lineNum%" %%B in (tmpFile_Content) do (
echo %%B
set /a cnt-=1
if !cnt!==0 goto exitLoop
)
:exitLoop
exit /b

find multiple files paths with single string

I tried to write a batch script that find all the paths of files that have the same name as the input string. right now it can find only the first file found, and i cant think of a way to make it list multiple files locations. I am not very experienced and I need some help.
this is part of the script code:
:start
cls
echo Enter file name with extension:
set /p filename=
echo Searching...
for %%a in (C D E F G H U W) do (
for /f "tokens=*" %%b in ('dir /s /b "%%a:\%filename%"') do (
set file=%%~nxb
set datapath=%%~dpb\
::the path of the file without the filename included "C:\folder\folder\"
set fullpath=%%b
::the path of the file with the filename included "C:\folder\folder\file"
goto break
)
)
:notfound
cls
echo Enter file name with extension:
echo %filename%
echo File Not Found!
ping localhost -n 4 >nul
goto start
:break
if "%datapath:~-1%"=="\" set datapath=%datapath:~,-1%
cls
echo 3 %filename% found
echo %fullpath1%
echo %fullpath2%
echo %fullpath3%
--- || ---
I want the script to search the computer and list every encountered files with the same name and I want to be able to put those files' paths into different variables.
For example, if readme.txt is the input, then I want the list of all the paths of all the files with that specific name (readme.txt) and I want to set variable for each path so I can use it after that.
input:
readme.txt
output:
3 files found
C:\folder\folder\readme.txt
C:\folder\folder\folder\readme.txt
D:\folder\readme.txt
#echo off
set filename=readme.txt
for %%a in (C D E F G H U W) do (
for /f "tokens=*" %%b in ('dir /s /b "%%a:\%filename%"') do (
echo you can do something here with %%~nxb in %%~dpb
echo full name: %%b
)
)
I see no need to set the filenames to variables, as you can process them inside your loop. But if you really need them (for some reason) in variables:
#echo off
setlocal enabledelayedexpansion
set filename=readme.txt
set count=0
for %%a in (C D E F G H U W) do (
for /f "tokens=*" %%b in ('dir /s /b "%%a:\%filename%" 2^>nul') do (
set /a count+=1
set _file[!count!]=%%b
)
)
set _file
You can try with this code :
#echo off
Title Searching for the path with the same file name
Mode con cols=80 lines=3 & Color 9E
SET /a Count=0
set /a cnt=1
set "FileName=Readme.txt"
set "Report=%~dp0Report.txt"
set "Folder2Copy=%~dp0Readme_Folder"
set "Result2Copy=%~dp0Result2Copy.txt
If exist %Folder2Copy% RD /S /Q %Folder2Copy%
If Exist %Report% Del %Report%
If Exist %Result2Copy% Del %Result2Copy%
echo(
Echo Searching for the path with the same file name
Rem Looking for fixed drives and store them into variables
SETLOCAL enabledelayedexpansion
For /f "skip=1" %%a IN ('wmic LOGICALDISK where driveType^=3 get deviceID') DO (
for /f "delims=" %%b in ("%%a") do (
SET /a "Count+=1"
set "Drive[!Count!]=%%b"
)
)
:Display
for /L %%i in (1,1,%Count%) do (
cls
Title Please wait a while ... Searching for "%FileName%" on "!Drive[%%i]!\"
echo(
echo Please wait a while ... Searching for "%FileName%" on "!Drive[%%i]!\"
Call :FindPathFile !Drive[%%i]!\ %FileName% >> %Report%
)
Start "" %Report%
Goto :AskQuestion
::***************************************************************************************
:FindPathFile <Location> <FileName>
Where.exe /r %1 %2
Goto :eof
::***************************************************************************************
:AskQuestion
cls & Mode con cols=100 lines=5
echo(
echo Did you want to make copy of all files found as name "%FileName%"
echo saved on "%Report%" ? (Y/N) ?
set /p "Input="
If /I "%INPUT%"=="Y" (
for /f "delims=" %%i in ('Type "%Report%"') do (
Call :MakeCopy "%%~i" "%Folder2Copy%\"
)
)
Call :Explorer "%Folder2Copy%\" & exit
If /I "%INPUT%"=="N" (
Exit
)
Goto :eof
::***************************************************************************************
:MakeCopy <Source> <Target>
If Not Exist "%~2\" MD "%~2\" (
if not exist "%2\%~n1" (
echo copying "%~1" to "%~2"
copy /N /B "%~1" "%~2" >>%Result2Copy% 2>&1
) else (
call :loop "%~1" "%~2"
)
)
::***************************************************************************************
:loop
set "fname=%2\%~n1(%cnt%)%~x1"
if exist "%fname%" set /a cnt+=1 && goto :loop
copy "%~1" "%fname%"
exit /b
::***************************************************************************************
:Explorer <file>
explorer.exe /e,/select,"%~1"
Goto :EOF
::***************************************************************************************

Find string in multiple .txt files

I have a folder with many .txt files. I would like to find string "X" in all of these files then I would like to copy the found strings into .txt files into a different folder.
So far I have tried :
#echo on
findstr /m "X" "%userprofile%\Desktop\New_Folder\New_Folder\*.txt"
if %errorlevel%==0 do (
for %%c in (*.txt) do (
type %%c >> "%UserProfile%\Desktop\New_Folder\%%~nc.txt"
pause
I do not understand the output %%~nc.txt part it's suppost to copy the changed .txt files to a new folder with the same name.
I would like to point out that string "X" is found in different places in the .txt file.
This batch file can did the trick (-_°)
So, just give a try : ScanfilesWordSearch_X.bat
#ECHO OFF
::******************************************************************************************
Title Scan a folder and store all files names in an array variables
SET "ROOT=%userprofile%\Desktop"
Set "NewFolder2Copy=%userprofile%\Desktop\NewCopyTxtFiles"
SET "EXT=txt"
SET "Count=0"
Set "LogFile=%~dp0%~n0.txt"
set "Word2Search=X"
SETLOCAL enabledelayedexpansion
REM Iterates throw the files on this current folder and its subfolders.
REM And Populate the array with existent files in this folder and its subfolders
For %%a in (%EXT%) Do (
Call :Scanning "%Word2Search%" "*.%%a"
FOR /f "delims=" %%f IN ('dir /b /s "%ROOT%\*.%%a"') DO (
( find /I "%Word2Search%" "%%f" >nul 2>&1 ) && (
SET /a "Count+=1"
set "list[!Count!]=%%~nxf"
set "listpath[!Count!]=%%~dpFf"
)
) || (
( Call :Scanning "%Word2Search%" "%%~nxf")
)
)
::***************************************************************
:Display_Results
cls & color 0B
echo wscript.echo Len("%ROOT%"^) + 20 >"%tmp%\length.vbs"
for /f %%a in ('Cscript /nologo "%tmp%\length.vbs"') do ( set "cols=%%a")
If %cols% LSS 50 set /a cols=%cols% + 20
set /a lines=%Count% + 10
Mode con cols=%cols% lines=%lines%
ECHO **********************************************************
ECHO Folder:"%ROOT%"
ECHO **********************************************************
If Exist "%LogFile%" Del "%LogFile%"
rem Display array elements and save results into the LogFile
for /L %%i in (1,1,%Count%) do (
echo [%%i] : !list[%%i]!
echo [%%i] : !list[%%i]! -- "!listpath[%%i]!" >> "%LogFile%"
)
(
ECHO.
ECHO Total of [%EXT%] files(s^) : %Count% file(s^) that contains the string "%Word2Search%"
)>> "%LogFile%"
ECHO(
ECHO Total of [%EXT%] files(s) : %Count% file(s)
echo(
echo Type the number of file that you want to explore
echo(
echo To save those files just hit 'S'
set /p "Input="
For /L %%i in (1,1,%Count%) Do (
If "%INPUT%" EQU "%%i" (
Call :Explorer "!listpath[%%i]!"
)
IF /I "%INPUT%"=="S" (
Call :CopyFiles
)
)
Goto:Display_Results
::**************************************************************
:Scanning <Word> <file>
mode con cols=75 lines=3
Cls & Color 0E
echo(
echo Scanning for the string "%~1" on "%~2" ...
goto :eof
::*************************************************************
:Explorer <file>
explorer.exe /e,/select,"%~1"
Goto :EOF
::*************************************************************
:MakeCopy <Source> <Target>
If Not Exist "%~2\" MD "%~2\"
Copy /Y "%~1" "%~2\"
goto :eof
::*************************************************************
:CopyFiles
cls
mode con cols=80 lines=20
for /L %%i in (1,1,%Count%) do (
echo Copying "!list[%%i]!" "%NewFolder2Copy%\"
Call :MakeCopy "!listpath[%%i]!" "%NewFolder2Copy%">nul 2>&1
)
Call :Explorer "%NewFolder2Copy%\"
Goto:Display_Results
::*************************************************************
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "destdir=U:\destdir"
SET "mystring=x"
FOR %%a IN ("%sourcedir%\*.txt") DO FINDSTR "%mystring%" "%%a">nul&IF NOT ERRORLEVEL 1 FINDSTR "%mystring%" "%%a">"%destdir%\%%~nxa"
GOTO :EOF
You would need to change the settings of sourcedir and destdir to suit your circumstances and set mystring appropriately, noting that you may have to adjust the findstr switches to accomodate case, literal and space-in-target-string.
Naturally, you could code sourcedir etc. directly as literals, but doing it this way means that the relevant strings need only be changed in one place.
You are close, but checking the ErrorLevel of findstr does not make sense here as this reflects the overall result, that is, ErrorLevel is set to 0 in case any of the files contain the search string.
I would parse the output of findstr /M using a for /F loop and copy the returned files in the body:
for /F "eol=| delims=" %%F in ('
findstr /M /I /C:"X" "%USERPROFILE%\Desktop\New_Folder\New_Folder\*.txt"
') do (
copy "%%F" "%USERPROFILE%\Desktop\New_Folder\"
)
This copies all those files which contain the literal search string (in a case-insensitive manner).

Windows batch script issue

I am having an issue with the following Windows batch script. I have multiple XML files in the %indir% that I want to import one at a time and email the report. Basically the section starting with "if /I %v_continue% == y" is not executing as expected. I did some debugging step by step with echo on, and it goes through the script till the %BLAT% command without executing any of the steps, and then it starts execution with the first "copy %script_path%.....". It executes the import.exe correctly, but then nothing gets assigned to %subj% and subsequently the %BLAT% command fails. Any advice?
Thanks!
#echo off
set environment=%1
set domain=%2
if [%environment%] == [] goto :endofscript
rem - Get the script path
set script_path=%~dp0
rem - Get the script name without the extension
set script_name=%~n0
rem - Get the script name with the extension
rem set script_name=%~nx0
rem - get the script extension
set script_ext=%~x0
rem - Set environment variables
call %script_path%\setenv.cmd
set cnt=0
set filemask=*.xml
for /f %%a in ('dir /b /a-d %indir%\%filemask%') do call :procfile %%a
goto :EOF
:procfile
set impfile=%1
set v_continue=n
set emailyn=y
set trset=%impfile:~0,3%
set /A cnt + = 1
if 1%cnt% lss 100 set cnt=0%cnt%
if %trset% == RCT (
set "subtxt=Receipt Confirmation"
set v_continue=y
)
if %trset% == SHP (
set "subtxt=Shipment Confirmation"
set v_continue=y
setlocal EnableDelayedExpansion
set MOFound=
for /f "tokens=3 delims= " %%f in ('find /i /c "<RefID>MO-ORD</RefID>" %indir%\%impfile%') do (set MOFound=%%f)
if !MOFound! GTR 0 (
copy %indir%\%impfile% %inarchdir%
move %indir%\%impfile% %S_INDIR%\FX%dttmstamp%%cnt%.xml 2>NUL
goto :EOF
)
endlocal
)
if /I %v_continue% == y (
copy %script_path%\%script_name%.dat %infile%
cscript %REPLACEVBS% %infile% "DOMAIN" "%domain%" 1>NUL 2>&1
cd /d %rptdir%
%DLC%\bin\import.exe -b -T d:\tmp -p %pfile%
rem - Check for errors
find /i /c "ERROR:" %rptfile% > NUL
if %ERRORLEVEL% NEQ 0 (
set "subj=SUCCESS: %subtxt% Import Report (%environment%/%domain%)"
set emailyn=y
) else (
set "subj=ERROR: %subtxt% Import Report (%environment%/%domain%)"
set emailyn=y
)
move %rptfile% %logdir%\%script_name%_%datestamp%_%cnt%.prn 2>NUL
move %outfile% %logdir%\%script_name%_%datestamp%_%cnt%.out 2>NUL
if /I %emailyn% == y (
echo Report location: %logdir%\%script_name%_%datestamp%_%cnt%.prn > %msgfile%
%BLAT% %msgfile% -server abc-com.mail.protection.outlook.com -f donotreply#abc.com -s "%subj%" -t %INBEMAIL% -attachi %logdir%\%script_name%_%datestamp%_%cnt%.prn
)
)
del /f /q %infile%
del /f /q %pfile%
del /f /q %msgfile%
:delfiles
rem - Delete log files that are older than 10 days.
PushD "%logdir%" && (
forfiles /M %script_name%_*.prn /D -10 /C "CMD /C del /f /q #PATH" 2>NUL
) & PopD
:endofscript
exit /B

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.

Resources