I have a batch script which :
check the files in a directory and check if it exists in another
directory and it should not exists there
count each file with a specific format, there should be just one of each
if both of the above statement is true then generate a success file.
Below is my code which is working fine:
SET /A file1=file2=Val=0
SET /A FileE=1
set /a flagname=1
for %%A in (*ABC*.txt) do set /a file1+=1
for %%A in (*XYZ*.txt) do set /a file2+=1
for %%i in ("*") do if exist "Processed\%%~nxi" SET /A FileE=0
SET /A Val=%file1%*%file2%*%FileE%
if %Val% EQU 1 (
echo SUCESS>Sucess.txt
SET Flag=Sucess
echo %Flag%) else (
if %file1% EQU 0 ( echo Missing ABC.txt files >> Error.txt)
if %file1% GTR 1 ( echo More than 1 ABC.txt files >> Error.txt)
if %file2% EQU 0 ( echo Missing XYZ.txt files >> Error.txt)
if %file2% GTR 1 ( echo More than 1 XYZ.txt files >> Error.txt)
(for %%i in ("*") do if exist "Processed\%%~nxi" echo(File Exists in
Processed
Folder %%~i)>>Error.txt
SET Flag=FAILURE
echo %Flag%)
My problem is how to transform above code to iterate over a list of number of files like 100 ? Below is the code which I tried :
#echo off
setlocal enable delayed expansion
Set Filen[0]=ABC*.txt
Set Filen[1]=XYZ*.txt
SET /A Val=1
SET /A File1=0
FOR /l %%G in (0,1,1) Do (
echo !Filen[%%G]! hi
set File1=0
echo %file1% Count
for %%A in (!Filen[%%G]! ) do (
set File1=!File1!+1
echo %%A %file1%)
)
Put your search words in a string and iterate over it:
Set "Search=ABC DEF XYZ"
For %%A in (%Search%) do (
Or in a file and read one by one
For /f "usebackq" %%A in ("Search.txt") Do (
With this file Search.txt
ABC
DEF
XYZ
In the environment
> tree a:\ /F
Auflistung der Ordnerpfade für Volume RamDisk
A:\
└───Test
│ ABC_123.txt
│ DEF_456.txt
│ Search.txt
│
└───Processed
The following batch
:: Q:\Test\2018\07\01\SO_51120147.cmd
#Echo off & Setlocal EnableDelayedExpansion
Set "BaseDir=A:\Test"
Set Err=^>^> "Error.txt" Call Echo=[%%date%% %%time%%]
PushD "%BaseDir%" || (%Err% can't locate %BaseDir% & Pause &Goto :Eof)
%Err% Job %~f0 started by %USERNAME%
Set " Flag=Sucess"
:: Uncomment for string variant
:: Set "Search=ABC DEF XYZ"
:: For %%A in (%Search%) do (
:: use the file variant
For /f "usebackq" %%A in ("Search.txt") Do (
Set Cnt=0
For %%B in (%%A*.txt) Do Set /A Cnt=1
if !Cnt! NEQ 1 (
%Err% Count of %%A*.txt file=[!Cnt!]
SET Flag=FAILURE
)
)
For %%A in (*) do if exist "Processed\%%~nxA" (
%Err% File %%A does exist in Processed Folder
Set Flag=FAILURE
)
%Err% Job %~f0 terminated with %Flag%
Yields this output:
> type A:\Test\Error.txt
[2018-07-01 13:10:34,43] Job Q:\Test\2018\07\01\SO_51120147.cmd started by LotPings
[2018-07-01 13:10:34,45] Count of XYZ*.txt file=[0]
[2018-07-01 13:10:34,47] Job Q:\Test\2018\07\01\SO_51120147.cmd terminated with FAILURE
EDIT: Explanation of the somehow tricky line:
Set Err=^>^> "Error.txt" Call Echo=[%%date%% %%time%%]
To not have to pre/append every line which should go to the error log with
the redirection and a (fixed) file name I put these together with the
echo command and a [date time] stamp into a variable.
To avoid immediate interpretation when setting the variable, the '>' have to
be esaped with a caret, and to delay the expansion of the %-signs these have
to be doubled. (otherwise each log entry had the same date time)
To force expansion of date time when echoing the (pseudo) call is neccessary.
Related
#echo off
call :checkFTP1 %* > all_log_all_log_%date:~10,4%%date:~4,2%%date:~7,2%.log 2>&1
call :checkFTP2 %* >> all_log_all_log_%date:~10,4%%date:~4,2%%date:~7,2%.log 2>&1
call :checkFTP3 %* >> all_log_all_log_%date:~10,4%%date:~4,2%%date:~7,2%.log 2>&1
call :doCommands1 %* >> all_log_all_log_%date:~10,4%%date:~4,2%%date:~7,2%.log 2>&1
call :doCommands2 %* >> all_log_all_log_%date:~10,4%%date:~4,2%%date:~7,2%.log 2>&1
call :doCommands3 %* >> all_log_all_log_%date:~10,4%%date:~4,2%%date:~7,2%.log 2>&1
call :doCommands4 %* >> all_log_all_log_%date:~10,4%%date:~4,2%%date:~7,2%.log 2>&1
exit /b
:checkFTP1
#echo off
Setlocal
:: Is folder empty
set _TMP=
for /f "delims=" %%a in ('dir /b "C:\test\folder1"') do set _TMP="%%a"
IF {%_TMP%}=={} (
goto :Exit1
) ELSE (
goto :checkFTP2
)
Endlocal
:checkFTP2
#echo off
Setlocal
:: Is folder empty
set _TMP=
for /f "delims=" %%a in ('dir /b "C:\test\folder2"') do set _TMP="%%a"
IF {%_TMP%}=={} (
goto :Exit2
) ELSE (
goto :checkFTP3
)
Endlocal
:checkFTP3
#echo off
Setlocal
:: Is folder empty
set _TMP=
for /f "delims=" %%a in ('dir /b "C:\test\folder3"') do set _TMP="%%a"
IF {%_TMP%}=={} (
goto :Exit3
) ELSE (
goto :doCommands1
)
Endlocal
:doCommands1
call script1.bat
if %errorlevel% EQU 0 (goto :doCommands2 ) Else ( ECHO error on script 1 ,2,3,4)
exit
:doCommands2
call script2.bat
if %errorlevel% EQU 0 (goto :doCommands3 ) Else ( ECHO Script 1 Completed Successfully , ERRORS on 2,3,4)
exit
:doCommands3
call script3.bat
if %errorlevel% EQU 0 (goto :doCommands4) Else ( ECHO Script 2 Completed Successfully , ERRORS on 3,4)
exit
:doCommands4
call script4.bat
if %errorlevel% EQU 0 (goto :completed1) Else ( ECHO Script 3 Completed Successfully , ERRORS on 4)
exit
:Exit1
Echo Today Date %DATE% at %Time%
Echo ###################FTP-1 FILES MISSING #########################
Exit
:Exit2
Echo Today Date %DATE% at %Time%
Echo ###################FTP-2 FILES MISSING (#########################
Exit
:Exit3
Echo Today Date %DATE% at %Time%
Echo ###################FTP-3 FILES MISSING #########################
Exit
:completed1
Echo Today Date %DATE% at %Time%
Echo ###################all scripts Completed Successfully#########################
Exit
I have above batch file which calls multiple bat files. I have tested the script and it worked fine.
My only issue is that the log file generated contains all information, and it's a large file.
Is it possible to just log comments and echo, and exclude what executed in screen?
For example I don't want 1 file moved to be showing in log file.
I've taken your code and re-written it to use generic functions which deduplicates the code into an a more easily managed form and allows you to add or remove any steps to FTP and Script sections as needed by editing the list of variables.
I also only return output that does not include the "file(s) moved".
Alternatively if you really only want the status lines to print you could change this to just have those print to the log and not have all info print to the log (this is being done because you are putting the redirection to the log on the calls to other steps.
Also the way this was written before it was actually going through all the goto commands and not needing the calls to each at the top.
Here is the refactored code, I haven't tested it but it should be fine I have to step out for a few hours and you can ask any questions and I'll be happy to answer when I have time.
#( Setlocal EnableDelayedExpansion
echo off
SET "_FolderList="C:\test\folder1" "C:\test\folder1" "C:\test\folder1" "
SET "_ScriptList="c:\test\script1.bat" "E:\script2.bat" "C:\Path\To\script3.bat" "C:\Path\To\script4.bat" "
SET "_Scripts_Failed=1,2,3,4"
SET "_Scripts_Completed="
SET "_Continue=0"
CALL :GetDateTime
SET "MasterLog=all_log_all_log_!IsoDate!_!IsoTime!.log"
)
CALL :Main
( Endlocal
EXIT /B
)
:Main
SET /A "_Counter=0"
FOR %%_ IN (%_FolderList%) DO (
IF DEFINED _Continue (
SET /A "_Counter+=1"
CALL :CheckFTP_List %%~_
)
)>> "%MasterLog%"
SET /A "_Counter=0"
FOR %%_ IN (%_FolderList%) DO (
IF DEFINED _Continue (
SET /A "_Counter+=1"
CALL :DoCommands_List %%~_
)
)>> "%MasterLog%"
IF DEFINED _Continue (
Echo Today Date %DATE% at %Time%
Echo ###################all scripts Completed Successfully#########################
)
GOTO :EOF
:CheckFTP_List
REM Is folder empty
for /f "delims=" %%a in ('dir /b /A-D "%*') do (
set "_Continue=%%a" )
IF NOT Defined _Continue (
Echo.Today Date on %DATE% at %Time%
Echo.###################FTP-%_Counter% FILES MISSING #########################
)
GOTO :EOF
:DoCommands_List
call "*%" | FIND /I /V "file(s) moved" &REM only outputs lines that don't contain files moved.
if %errorlevel% NEQ 0 (
SET "_Continue="
SET "_Scripts_Completed=%_Scripts_Completed:,1=1%"
SET "_Scripts_Failed=!_Scripts_Failed:%_Scripts_Completed%=!"
Echo Today Date %DATE% at %Time%
ECHO. Error encountered on Script %Counter%! -- Completed Scripts: !_Scripts_Completed! -- Failed Scripts: !_Scripts_Failed!
) ELSE (
SET "_Scripts_Completed=%_Scripts_Completed:,1=1%,%Counter%"
)
GOTO :EOF
:GetDateTime
FOR /F "Tokens=1-7 delims=MTWFSmtwfsouehrandit:-\/. " %%A IN ("%DATE% %TIME: =0%") DO (
FOR /F "Tokens=2-4 Delims=(-)" %%a IN ('ECHO.^| DATE') DO (
SET "%%~a=%%~A"
SET "%%~b=%%~B"
SET "%%~c=%%~C"
SET "HH=%%~D"
SET "Mn=%%~E"
SET "SS=%%~F"
SET "Ms=%%~G"
)
)
SET "IsoTime=%HH%.%Mn%.%SS%.%Ms%"
SET "IsoDate=%yy%-%mm%-%dd%"
GOTO :EOF
So I need a code that will take a text file (we'll cal it list.txt) and turn every line into a variable.
To give some context, I have some file names listed in list.txt, which adds and deletes file names occasionally by user request. I want the user of this code to be able to select which document they'd like to open using variables.
For example, if list.txt looks like this
list.txt
loading.txt
test1.txt
test2.txt
test3.txt
test4.txt
Then I'd like an automatic variable for every .txt listed. I then would add a simple if statement to open the file matched with the variable.
Am I making this too complicated for myself or is this the only way to do this?
EDIT:
I am not attempting something like this:
type list.txt
echo.
echo.
set /p GROUPSELECT= Please type out the FULL name of the group listed:
CD %grouplist%
type %GROUPSELECT%
It will display the file contents, and then display the specific file chosen by the input. I'd think that the variable might be easier to do more with later though, just a thought.
Edit2
I tried this:
#Echo OFF
FOR /F "Usebackq Delims=" %%a IN (
"list.txt"
) DO (
set jim=%%a
)
echo %jim%
PAUSE
%jim% will only be the last line in the text file, so how do I make the next step into making them all different?
Give this a try. I have commented each line of code that should explain what it is doing. Let me know if you need any further explanation.
#echo off
REM FOR commnd is parsing the output of the FINDSTR commands.
REM First FINDSTR command is finding non empty lines in the file
REM Second Findstr command is assigning a number to each line of the file
REM The output is split into two tokens and the number is assigned to %%G and the line of the file to %%H
for /F "tokens=1* delims=:" %%G IN ('findstr /V "^$" list.txt ^|findstr /N ".*"') DO (
REM create a variable of the lines in the file
set "file%%G=%%H"
REM Create a menu of the lines in the file
echo %%G %%H
REM Get the number of lines in the output
set "num=%%G"
)
REM Ask user to chose a number
set /P "filenum=Chose a file number 1 to %num%:"
REM Need to enable delayed expansion to use array variables
setlocal enabledelayedexpansion
REM Check if they type in a correct number and display the file
IF DEFINED file%filenum% type !file%filenum%!
endlocal
pause
Is this the sort of thing you're looking for?
#For /F "Delims==" %%# In ('Set # 2^>Nul')Do #Set "%%#="
#For /F Tokens^=1*Delims^=[]^ EOL^= %%# In ('Type "file.txt"^|Find /V /N ""'
)Do #Set "#%%#=%%$"&Echo( %%#. %%$
:Opt
#Echo(
#Set /P "Opt=Choose a document to open>"
#Set #|Findstr /B "#%Opt%=">Nul||GoTo :Opt
#Call Start "" "%%#%Opt%%%"
Just change the name of the text file containing your list on line 2 as needed.
#TheBoy your question is confusingly framed. If you DO NOT want the example you put in the edit, then can you better explain what you DO want??
Do you want to say iterate over the list file, and create a choice screen?
#(SETLOCAL EnableDelayedExpansion
ECHO OFF
SET "_ChoiceList_File=C:\Admin\ChoiceFile.txt"
REM Full Character List to populate Choices
SET "_CharList=0 1 2 3 4 5 6 7 8 9 A B C D E F N X"
)
CALL :Main
( ENDLOCAL
EXIT /B 0
)
:Main
REM Now we can Do Our Choices btu lets do it in a Sub Function.
CALL :MakeChoice _ChoiceResult
ECHO.
ECHO The Index Chosen Was: %_Chosen%
ECHO The Result Matched is: "%_ChoiceResult%"
REM ECHO Here you output "%_ChoiceResult%"
TYPE "%_ChoiceResult%"
GOTO :EOF
:MakeChoice
cls
color 1A
SET %~1="
SET "_Choices="
SET "_Chosen="
SET "_Amount="
SET "_Choice.17.Value=Next Set!"
SET "_Choice.18.Value=EXIT!"
SET "_Choice_Show_Next="
echo. Pick a File:
echo.========================
REM Create Numbered Array of Choices and output Choices to the Screen
FOR /F "tokens=* usebackq" %%A IN ("%_ChoiceList_File%") DO (
SET /A "_Amount+=1"
SET "_Choice.!_Amount!.Value=%%A"
IF !_Amount! EQU 16 (
SET /A "_Amount+=2"
CALL :MakeChoice "%~1"
IF DEFINED _Chosen (
IF !_Chosen! NEQ 17 (
REM IF !_Chosen! NEQ 18 (
GOTO :EOF
REM )
)
SET "_Amount="
SET "_Chosen="
)
)
)
IF NOT DEFINED _Chosen (
SET /A "_Amount+=1"
SET "_Choice.!_Amount!.Value=!"
SET /A "_Amount+=1"
SET "_Choice.!_Amount!.Value=EXIT!"
CALL :MakeChoice "%~1"
)
GOTO :EOF
:MakeChoice
CLS
ECHO.
SET "_Temp="
SET "_Choices="
SET /A "_Skipline= !_Amount! - 1"
REM Create Choice List to Display only the choices needed.
FOR %%A IN (%_CharList%) DO (
SET /A "_Temp+=1"
IF !_Temp! LEQ !_Amount! (
IF !_Temp! EQU !_Skipline! (
ECHO.
ECHO.=============================
)
IF DEFINED _Choice.!_Temp!.Value (
SET "_Choices=!_Choices!%%A"
CALL ECHO. %%A : %%_Choice.!_Temp!.Value%%
)
)
)
ECHO.
CHOICE /C !_Choices! /N /M "What File do you want to choose? "
SET "_Chosen=%ERRORLEVEL%"
SET "%~1=!_Choice.%_Chosen%.Value!"
GOTO :EOF
)
You can try this:
#echo off
setlocal enabledelayedexpansion
set jim=
for /f "delims=] tokens=1*" %%a in ('find /v /n "" ^<list.txt') do (
set jim=!jim! %%b
)
echo Select a file from !jim!
set /p file=
type !file!
pause
This will read all lines from the list.txt and return them within a variable !jim!.
I have about 12TB (and growing) library, distributed over 3 HDDs, of video files and I would like to back them up to an external harddrive. I am using Windows 10 Pro.
My backup drive has only 8TB and I would like to always backup the newest 8TB of video files. So far I have tried about 10 sync tools but none of them allowed me to copy files according to creation date.
Also with robocopy I haven't found a way to copy only the latest 8TB of files.
Any suggestions?
I have 2 batch scripts for you that do what you have asked for. The main difference between them is, that the 1st script is using where to locate all the files which will use the timestamp of the last change. To use another timestamp e.g. last access or time of creation you have to use the 2nd script I attached which uses dir.
1.
Batch script using where to locate files:
It takes minimum 2 arguments:
Usage: batch.bat <dst> <src_1> [<src_...> <src_n>]
It uses the where /r <src_n> * /t command which will build a list of all files in all subdirectories with a timestamp of the last change in the following format:
<size> <date> <time> <file name>
5397 11.07.2017 14:32:09 C:\Users\...\foo.txt
10860 12.07.2017 11:25:15 C:\Users\...\bar.log
The timestamp is of last change if you need the time of the creation or last access take the 2nd batch script below which is using dir as there is the possibility to choose between different timestamps.
This output will be written (without the column size) into a temporary file under the temp dir %TEMP% (will be deleted automatically after script) for every source that is passed via the argument list. The complete temporary file is sorted by the date and time, newest first.
This sorted output will be used to copy it into the destination folder.
Example:
Current directory in cmd is C:\...\Desktop):
batch.bat "backup_folder" "F:\folder" "F:\folder with space"
Current directory somewhere:
batch.bat "C:\...\Desktop\backup_folder" "F:\folder" "F:\folder with space"
The C:\...\Desktop\backup_folder will contain 2 folders folder and folder with space which contain all files of these source folders after the scripts operation.
In your case the batch script would copy only 8 TB of the newest files because then the drive will be full and the batch script will exit because it recognizes copy errors.
The batch script:
#echo off
setlocal enabledelayedexpansion
rem Check if at least 2 arguments are passed
set INVARGS=0
if [%1] == [] set INVARGS=1
if [%2] == [] set INVARGS=1
if %INVARGS% == 1 (
echo Usage: %0 ^<dst^> ^<src_1^> ^<src_...^> ^<src_n^>
goto :EOF
)
rem Store Carriage return character in CR (used for spinner function, see below)
for /f %%a in ('copy /Z "%~dpf0" nul') do set "CR=%%a"
rem Set temp file name
set "uniqueFile=%TEMP%\%0_%RANDOM%.tmp"
rem Set destination path and shift to next argument
set "dstpath=%~1"
shift
rem Store src_1 to src_n arguments into src array
set idx=0
:again
rem If %1 is blank, we are finished
if not [%1] == [] (
if not exist "%~1" (
echo The following source folder doesn't exist:
echo "%~1"!
echo The program will terminate!
goto :end
)
pushd "%~1"
set "src[%idx%]=!cd!"
if [!src[%idx%]:~-1!] == [\] (
set src[%idx%]=!src[%idx%]:~0,-1!
)
set /a idx=%idx%+1
popd
rem Shift the arguments and examine %1 again
shift
goto :again
)
rem Build command string:
rem where /r "src_1" * /t ^& where /r "src_..." * /t ^& where /r "src_n" * /t ^& break"
set "command="
for /F "tokens=2 delims==" %%s in ('set src[') do (
set "command=!command!where /r "%%s" * /t ^& "
)
set "command=!command!break"
echo "Command: ^<!command!^>"
rem Clear temp file and write files to copy in it
break>"%uniqueFile%"
for /f "tokens=2,3,4,* delims= " %%F in ('!command!') do (
call :spinner
echo %%F %%G %%H %%I>>"%uniqueFile%"
)
rem Open the built file list
echo File list will be opened...
%uniqueFile%
rem Ask user if copying should start
echo Should this files copied into %dstpath%? (Y/N)
set INPUT=
set /P INPUT=Answer?:%=%
if not [%INPUT%] == [y] (
if not [%INPUT%] == [Y] (
goto :end
)
)
set "dstpathPart="
rem Sort files newest first (last changed timestamp)
for /f "tokens=3,* delims= " %%F in ('type "%uniqueFile%" ^| sort /r') do (
rem Build destination part from source folder names
call :buildDestinationPath dstpathPart "%%F %%G"
rem Prepend the destination folder
set "dstFile=!dstpath!\!dstpathPart!"
rem Make directories that doesn't exists
for %%F in ("!dstFile!") do set "directory=%%~dpF"
if not exist "!directory!" ( mkdir "!directory!" )
rem Copy files and echo it
echo copy /y "!file!" "!dstFile!"
copy /y "!file!" "!dstFile!"
echo.
rem If copying failed exit program
if errorlevel 1 (
echo Copying failed... Maybe there is no more space on the disk!
echo The program will terminate!
goto :end
)
)
goto :end
rem Function definitions
:buildDestinationPath
for /F "tokens=2 delims==" %%s in ('set src[') do (
rem Go into folder e.g.: C:\src_1\ --> C:\
pushd "%%s"
cd ..
rem file contains full path of the where command
set "file=%~2"
rem Remove trailing space
if "!file:~-1!" == " " (
call set "file=%%file:~0,-1%%"
)
rem remove src folder from file to make it relative
rem e.g. C:\src_1\foo\bar\file1.txt --> src_1\foo\bar\file1.txt
call set "dstpathPart_temp=%%file:!cd!=%%"
rem Switch back to origin folder
popd
rem If the folder name changed the substring taken was right so take next
if not [!dstpathPart_temp!] == [!file!] (
set "%~1=!dstpathPart_temp!"
goto :next
)
)
:next
goto :EOF
:spinner
set /a "spinner=(spinner + 1) %% 4"
set "spinChars=\|/-"
<nul set /p ".=Building file list... !spinChars:~%spinner%,1!!CR!"
goto :EOF
:end
del "%uniqueFile%"
goto :EOF
2.
Batch script using dir to locate files:
It takes one more argument so that you need minimum 3 arguments:
Usage: batch.bat <timeordering> <dst> <src_1> [<src_...> <src_n>]
It will generate the same list as explained above but with the help of the dir command. The dir command takes the following parameter which is the <timeordering> parameter of the script:
/tc Creation
/ta Last access
/tw Last written
The batch script:
#echo off
setlocal enabledelayedexpansion
rem Check if at least 2 arguments are passed
set INVARGS=0
if [%1] == [] set INVARGS=1
if [%2] == [] set INVARGS=1
if [%3] == [] set INVARGS=1
if %INVARGS% == 1 (
echo Usage: %0 ^<timeordering^> ^<dst^> ^<src_1^> ^<src_...^> ^<src_n^>
goto :EOF
)
rem Store Carriage return character in CR (used for spinner function, see below)
for /f %%a in ('copy /Z "%~dpf0" nul') do set "CR=%%a"
rem Set temp file name
set "uniqueFile=%TEMP%\%0_%RANDOM%.tmp"
rem Set timeordering and destination path and shift to next argument
set "timeordering=%~1"
shift
set "dstpath=%~1"
shift
rem Store src_1 to src_n arguments into src array
set idx=0
:again
rem If %1 is blank, we are finished
if not [%1] == [] (
if not exist "%~1" (
echo The following source folder doesn't exist:
echo "%~1"!
echo The program will terminate!
goto :end
)
pushd "%~1"
set "src[%idx%]=!cd!"
if [!src[%idx%]:~-1!] == [\] (
set src[%idx%]=!src[%idx%]:~0,-1!
)
set /a idx=%idx%+1
popd
rem Shift the arguments and examine %1 again
shift
goto :again
)
rem Clear temp file and write files to copy in it
break>"%uniqueFile%"
rem call commands with all sources:
rem call :getFileInformation /TC "src_1\*"
rem /TW
rem /TA
for /F "tokens=2 delims==" %%s in ('set src[') do (
call :getFileInformation %timeordering% "%%s\*" "%uniqueFile%""
)
rem Open the built file list
echo Unsorted file list will be opened...
%uniqueFile%
rem Ask user if copying should start
echo Should this files copied into %dstpath%? (Y/N)
set INPUT=
set /P INPUT=Answer?:%=%
if not [%INPUT%] == [y] (
if not [%INPUT%] == [Y] (
goto :end
)
)
set "dstpathPart="
rem Sort files newest first (last changed timestamp)
for /f "tokens=3,* delims= " %%F in ('type "%uniqueFile%" ^| sort /r') do (
rem Build destination part from source folder names
call :buildDestinationPath dstpathPart "%%F %%G"
rem Prepend the destination folder
set "dstFile=!dstpath!\!dstpathPart!"
rem Make directories that doesn't exists
for %%F in ("!dstFile!") do set "directory=%%~dpF"
if not exist "!directory!" ( mkdir "!directory!" )
rem Copy files and echo it
echo copy /y "!file!" "!dstFile!"
copy /y "!file!" "!dstFile!"
echo.
rem If copying failed exit program
if errorlevel 1 (
echo Copying failed... Maybe there is no more space on the disk!
echo The program will terminate!
goto :end
)
)
goto :end
rem Function definitions
:buildDestinationPath
for /F "tokens=2 delims==" %%s in ('set src[') do (
rem Go into folder e.g.: C:\src_1\ --> C:\
pushd "%%s"
cd ..
rem file contains full path of the where command
set "file=%~2"
rem Remove trailing space
if "!file:~-1!" == " " (
call set "file=%%file:~0,-1%%"
)
rem remove src folder from file to make it relative
rem e.g. C:\src_1\foo\bar\file1.txt --> src_1\foo\bar\file1.txt
call set "dstpathPart_temp=%%file:!cd!=%%"
rem Switch back to origin folder
popd
rem If the folder name changed the substring taken was right so take next
if not [!dstpathPart_temp!] == [!file!] (
set "%~1=!dstpathPart_temp!"
goto :next
)
)
:next
goto :EOF
:getFileInformation
for /f "delims=" %%a in ('dir /s /b /a-d %~1 "%~2"') do (
for /f "tokens=1,2 delims= " %%b in ('dir %~1 "%%a" ^| findstr /l /c:"%%~nxa"') do (
echo %%b %%c %%a>>"%~3"
call :spinner
)
)
goto :EOF
:spinner
set /a "spinner=(spinner + 1) %% 4"
set "spinChars=\|/-"
<nul set /p ".=Building file list... !spinChars:~%spinner%,1!!CR!"
goto :EOF
:end
del "%uniqueFile%"
goto :EOF
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).
I have to check a tree for duplicating files and write all of them to List.txt file.
But my script seems to skip one of the file locations in each group. (For example, if there are 4 duplicating files, only 3 of them appear in the list.)
If I'm not mistaken, it's the location of the "previousFile" of the last comparison that is missing. How do I write it to the list, too?
Also, how can I group paths in the List.txt by the filename so that it looks something like this:
File fileNameA.txt :
C:\path1\fileNameA.txt
C:\path2\fileNameA.txt
C:\path3\fileNameA.txt
File fileNameB.txt :
C:\path1\fileNameB.txt
C:\path2\fileNameB.txt
C:\path3\fileNameB.txt
C:\path4\fileNameB.txt
File fileNameC.txt :
C:\path1\fileNameC.txt
C:\path2\fileNameC.txt
...
?
That's my script so far:
#echo off
setlocal disableDelayedExpansion
set root=%1
IF EXIST List.txt del /F List.txt
set "prevTest=none"
set "prevFile=none"
for /f "tokens=1-3 delims=:" %%A in (
'"(for /r "%root%" %%F in (*) do #echo %%~zF:%%~fF:)|sort"'
) do (
set "currentTest=%%A"
set "currentFile=%%B:%%C"
setlocal enableDelayedExpansion
set "match="
if !currentTest! equ !previousTest! fc /b "!previousFile!" "!currentFile!" >nul && set match=1
if defined match (
echo File "!currentFile!" >> List.txt
endlocal
) else (
endlocal
set "previousTest=%%A"
set "previousFile=%%B:%%C"
)
)
You need to count matches and add echo previous filename to echo current one in case of the first match.
Note '"(for /r "%root%" %%F in (*) do #echo(%%~nxF?%%~zF?%%~fF?)|sort"' changes:
used ? (question mark) as a delimiter: reserved character by Naming Files, Paths, and Namespaces
added %%~nxF? prefix to sort output properly by file names even in my sloppy test folder structure, see sample output below.
This output shows than even cmd poisonous characters (like &, %, ! etc.) in file names are handled properly with DisableDelayedExpansion kept.
#ECHO OFF
SETLOCAL EnableExtensions DisableDelayedExpansion
set "root=%~1"
if not defined root set "root=%CD%"
set "previousTest="
set "previousFile="
set "previousName="
set "match=0"
for /f "tokens=1-3 delims=?" %%A in (
'"(for /r "%root%" %%F in (*) do #echo(%%~nxF?%%~zF?%%~fF?x)|sort"'
) do (
set "currentName=%%A"
set "currentTest=%%B"
set "currentFile=%%C"
Call :CompareFiles
)
ENDLOCAL
goto :eof
:CompareFiles
if /I "%currentName%" equ "%previousName%" ( set /A "match+=1" ) else ( set "match=0" )
if %match% GEQ 1 (
if %match% EQU 1 echo FILE "%previousFile%" %previousTest%
echo "%currentFile%" %currentTest%
) else (
set "previousName=%currentName%"
set "previousTest=%currentTest%"
set "previousFile=%currentFile%"
)
goto :eof
Above script lists all files of duplicated names regardless of their size and content. Sample output:
FILE "d:\bat\cliPars\cliParser.bat" 1078
"d:\bat\files\cliparser.bat" 12303
"d:\bat\Unusual Names\cliparser.bat" 12405
"d:\bat\cliparser.bat" 335
FILE "d:\bat\Stack33721424\BÄaá^ cčD%OS%Ď%%OS%%(%1!)&°~%%G!^%~2.foo~bar.txt" 120
"d:\bat\Unusual Names\BÄaá^ cčD%OS%Ď%%OS%%(%1!)&°~%%G!^%~2.foo~bar.txt" 120
To list all files of duplicated names with the same size but regardless of their content:
:CompareFiles
REM if /I "%currentName%" equ "%previousName%" (
if /I "%currentTest%%currentName%" equ "%previousTest%%previousName%" (
set /A "match+=1"
REM fc /b "%previousFile%" "%currentFile%" >nul && set /A "match+=1"
) else ( set "match=0" )
To list all files of duplicated names with the same size and binary content:
:CompareFiles
REM if /I "%currentName%" equ "%previousName%" (
if /I "%currentTest%%currentName%" equ "%previousTest%%previousName%" (
REM set /A "match+=1"
fc /b "%previousFile%" "%currentFile%" >nul && set /A "match+=1"
) else ( set "match=0" )
Edit If the name of the file doesn't matter (only its contents), you could apply next changes in FOR loop and in :CompareFiles subroutine:
#ECHO OFF
SETLOCAL EnableExtensions DisableDelayedExpansion
set "root=%~1"
if not defined root set "root=%CD%"
set "previousTest="
set "previousFile="
set "match=0"
for /f "tokens=1-2 delims=?" %%A in (
'"(for /r "%root%" %%F in (*) do #echo(%%~zF?%%~fF?)|sort"'
) do (
set "currentTest=%%A"
set "currentFile=%%B"
rem optional: skip all files of zero length
if %%A GTR 0 Call :CompareFiles
)
ENDLOCAL
goto :eof
:CompareFiles
if /I "%currentTest%" equ "%previousTest%" (
fc /b "%previousFile%" "%currentFile%" >nul && set /A "match+=1"
) else ( set "match=0" )
if %match% GEQ 1 (
if %match% EQU 1 echo FILE "%previousFile%" %previousTest%
echo "%currentFile%" %currentTest%
) else (
set "previousTest=%currentTest%"
set "previousFile=%currentFile%"
)
goto :eof