I've been working on a BAT file which will delete old files based on creation date. To do this I've generated a list of all files and paths, then a list of files names to be protected. FINDSTR is then used to remove these files from the list of files and paths.
This system works fine until I encounter a file with a dash (or so it seems!)
Here's an example:
cleaner_protect.txt contains:
New File.txt
New File - Copy.txt
cleaner_fullpath.txt contains:
P:\New File.txt
P:\New File - Copy.txt
P:\Old File.txt
I want to remove the New Files stored in cleaner_protect.txt from the cleaner_fullpath.txt, leaving the Old Files behind which I will later delete (not up to that bit yet lol). Here is my code so far:
:: Remove protected files from list to be deleted (fullpath)
:RemoveFile
:: load string into variable
set /p target= <cleaner_protect.txt
:: remove protected file from full path list
echo -----------------------------
echo Searching for: "%target%"
echo -----------------------------
pause
findstr /v ".\<%target%\>." cleaner_fullpath.txt > cleaner_temp.txt
echo -----------------------------
type cleaner_temp.txt
echo -----------------------------
pause
del cleaner_fullpath.txt
ren cleaner_temp.txt cleaner_fullpath.txt
:: Count remaining lines in list
Set target=cleaner_protect.txt
Set /a lines=0
For /f %%j in ('Find "" /v /c ^< %target%') Do Set /a lines=%%j
Echo %target% has %lines% lines.
pause
:: Loop until completed
IF %lines% GTR 0 (
:: Remove line from protected list
more +1 cleaner_protect.txt > cleaner_temp.txt
del cleaner_protect.txt
ren cleaner_temp.txt cleaner_protect.txt
set /a lines-=1
GOTO :RemoveFile
)
Pauses and echos are for debugging purposes... I want this to run almost invisibly.
Can anyone shed some light on this? I need this code to repeatedly go through a dropbox and delete old files which may be in various levels of structure.
Maybe this simple line does all you want:
findstr /E /V /G:cleaner_protect.txt cleaner_fullpath.txt > cleaner_temp.txt
Sample output:
P:\Old File.txt
I would do it as follows:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem /* Prepend each line of `cleaner_protect.txt` with `\\`, remove a trailing space,
rem if applicable, and write result to `cleaner_temp.txt`: */
> "cleaner_temp.txt" (
for /F "usebackq delims= eol=|" %%E in ("cleaner_protect.txt") do (
set "ITEM=\\%%E|"
setlocal EnableDelayedExpansion
echo !ITEM: ^|=!
endlocal
)
)
rem /* Search `cleaner_fullpath.txt` for lines that do not end in any of the lines of
rem `cleaner_temp.txt` and process the returned items only: */
for /F "delims= eol=|" %%F in ('
findstr /V /I /E /L /G:"cleaner_temp.txt" "cleaner_fullpath.txt"
') do (
ECHO del "%%F"
)
rem // Clean up temporary file `cleaner_temp.txt`:
del "cleaner_temp.txt"
endlocal
exit /B
After having tested the script, remove the upper-case ECHO command.
Supposing cleaner_protect.txt contains this:
New File.txt
New File - Copy.txt
The temporary file cleaner_temp.txt is going to contain this:
\\New File.txt
\\New File - Copy.txt
So having the text file cleaner_fullpath.txt:
P:\New File.txt
P:\New File - Copy.txt
P:\Old File.txt
Only the following items are processed:
P:\Old File.txt
The leading \\ is taken as one literal \ by findstr (as it uses the \ as an escape character).
The prefix \\ is implemented in order to cover also the following situations:
Let us assume cleaner_protect.txt holds this:
New File.txt
And cleaner_fullpath.txt holds this:
P:\New File.txt
P:\Very New File.txt
Without the prefix, P:\Very New File.txt would also match New File.txt at the end, hence it would not become deleted erroneously.
Then let us assume cleaner_protect.txt holds this:
.File.txt
And cleaner_fullpath.txt holds this:
P:\.File.txt
P:\Other.File.txt
With a \ prefix, P:\Other.File.txt would also match \.File.txt at the end, because . is a meta character to findstr (even though literal search strings are defined by /L, but escaping like \. still applies and results in a literal .), hence it would also not become deleted erroneously. However, with a \\ prefix, escaping applies to the second \, so a literal \ is the result; the . does not need to be escaped with the /L option.
Related
I get a Text-file from an system - in this file there are filenames (per line one filename) - now with a batch-file every line (filename) from this list should be searched in a directory and copied in an other direcory.
I have this found and it almost works:
#echo off &setlocal
set "sourceRoot=C:\Users\test\Desktop\COPY_TEST\originale"
set "sourceList=C:\Users\test\Desktop\COPY_TEST\DBLIST.TXT"
set "destFolder=C:\Users\test\Desktop\COPY_TEST\kopiert"
for /f "delim=" %%i in ('dir /a-d /b /s "%sourceRoot%\*.pdf"^|findstr /ig:"%sourceList%"') do (
copy "%%~i" "%destFolder%\"
)
ECHO "Ausgabe abgeschlossen"
PAUSE
This works if the list looks like this:
filename1
filename2
....
BUT from the other system i get the list like this:
filename1;
filename2;
....
ANd now i have to edit the search term and add a specific term to them - original:
filename1;
But it should be searched for:
filename1_DE
So from the searchterm in the list the last character ";" should be cut and the term "_DE" should be added.
Not the list should be changed only the search term.
Is that possible?
Many thanks!
for /? explains expansion on variables, but in short, a file can be broken up into parts expanding on the metavariables. So as example if we have a string of a file:
D:\work\file1.txt
we can run a for loop to get the above string or parts of the string:
for %%i in (c:\work\file1.txt) do echo %%i
would echo D:\work\file1.txt
but we can now expand on %%i using things like drive, path, name, extension etc. So a straight forward example would be the below, you can copy it as is and save as a batch file and test it for yourself:
#echo off
for %%i in ("c:\work\file1.txt") do (
echo Full string and retain double quotes: %%i
echo Full string but remove double quotes: %%~i
echo Drive only: %%~di
echo Path only: %%~pi
echo Filename only: %%~ni
echo Extension only: %%~xi
echo Combination of drive and path: %%~dpi
echo Combination name and extension: %%~nxi
echo adding text between name %%~ni and extension %%~xi as: %%~niTEXT%%~xi
)
So given above explanation, you simply require using name only, add text and join with extension.
copy "%%~i" "%destFolder%\%%~ni_DE%%~xi
Thank you for your answer - i changed my version to following one and it works for me :)
#echo off &setlocal
set "sourceRoot=C:\Users\offic\Desktop\COPY_TEST\originale"
set "sourceList=C:\Users\offic\Desktop\COPY_TEST\DBLIST.TXT"
set "destFolder=C:\Users\offic\Desktop\COPY_TEST\kopiert"
set "sprache=_EN"
FOR /f "tokens=1 delims=;" %%i IN ('FINDSTR . "%sourceList%"') DO (
copy "%sourceRoot%\%%~i%sprache%.pdf" "%destFolder%\"
)
ECHO "Ausgabe abgeschlossen"
PAUSE
On Windows 10 I have this .bat:
#echo off
for /f "delims=" %%i in (filelist.txt) do (
echo %%~nxi >> output.txt
type "%%~ni*" >> output.txt
echo. >> output.txt
echo. >> output.txt
)
Exit
Now what this does is:
reads filelist.txt, which contains names of .txt files like:
20180808173105 (without ".txt"
searches for those files: 20180808173105.txt
copies name of files (without ".txt") into output.txt
inserts content of files
inserts two blank lines
repeats whole process for all files named in filelist.txt
--> It works fine! (or do you see any exception where this might malfunction?)
This inserts the full contents of a text file according to a list.
Can I modify it, so
not the whole content of a .txt, but only a part of it is inserted?
For example, everything from just after "title:" to just before "<!--"
if the filelist had a hierarchical structure (outline), it could
be preserved, like so:
#201508081213
###201609101219
to
#201508081213
TEXT
###201609101219
TEXT
I am using this to convert Outlines (using only the file names) to a rough first draft of text for writing articles and blogs
#echo off
2> output.txt echo.
#>&3 (
echo Debug Information
echo -----------------
)
for /f "delims=" %%A in (filelist.txt) do (
for %%B in ("%%~nA*") do call :read "%%~B"
) >> output.txt
exit /b
:read
setlocal
set "line="
echo(%~nx1
for /f "usebackq delims=" %%C in ("%~1") do (
set "line=%%C"
for /f "tokens=*" %%D in ('call echo "%%line:~0,4%%"') do (
#>&3 echo File: "%~1" Test: %%D == "<!--"
if %%D == "<!--" (
#>&3 echo Found: "<!--"
echo.
echo.
exit /b 0
)
)
call echo(%%line%%
)
echo.
echo.
exit /b 0
Note
Not the best language for this task. Had to avoid
enabledelayedexpansion as to known use of ! in <!--.
Used call even though < and some other characters
could cause issue.
for /f loops do not normally process empty lines so
hope that is not a problem.
Hierarchical structure depends on the order in filelist.txt.
The structure of a document can vary and I cannot consider
what the correct order might be. The use of a wildcard gives
some doubt. A filename #a will find #a1 and #a2 so
placement of 3 headings is unknown.
I have left in the std stream 3 messages for your
understanding of operation with the <!-- test.
Std stream 1 is the output that goes to file.
Operation
The file output.txt is erased before echoing text to the file.
The 1st for loop reads each line of the filelist.txt file.
The nested for loop gets the filenames with a wildcard.
Each call to label :read passes the argument of a filename.
In the label of :read, the filename is echoed.
A for loop reads lines from the filename.
Each line is stored in variable named line.
The nested for loop will use call echo to expand
the variable line and echoes the 1st 4 characters.
A comparison is done to test if it is <!-- and if so,
echo 2 newlines and then the label is exited.
If not, echo each line. echo 2 newlines is done at
the end of the label before exiting.
I have file C:/test.txt which is having content as below.
05/13/2017 07:29:34 Value= \\america.com\efpf_share\efpf\ipm_files
05/13/2017 07:29:41 Value= \\america.com\efpf_share\efpf\ipm_files
05/17/2017 08:31:54 Value= \\america.com\efpf_share\efpf\ipm_files
05/17/2017 08:32:03 Value= \\america.com\efpf_share\efpf\ipm_files
I want to extract 'epfp' or any string comes at this place and convert this into upaercase also if its have test attached (as epfptest) then it it should split EPFP-TEST. For extracting I am running the below code and redirecting the output in temp1.txt file
findstr "Value=" C:\test.txt| findstr america > "C:\temp.txt" && for /l %l in (1,1,1) do #for /f "tokens=3* delims=." %a in ('findstr /n /r "^" "C:\temp.txt" ^| findstr /r "^%l:"') do #echo %b > c:\temp1.txt
now temp1.txt file having the content as below :
com\efpf_share\efpf\ipm_files
Now finally I am exracting efpf from below code it gives me the output as below :
for /f "tokens=3 delims=\" %a in (c:\temp1.txt) do #echo %a
epfp
I want this output or to be converted as EPFP (in uppercare) and if this output does not having test string attached then it should only split as EPFP-TEST
Note: Final output can be anything (in this case epfp) and I want this convert in uppercase also if this output containing 'test' string attached then that should be split in "STRING-TEST"
This test file modification task should be definitely not done using a batch file and pure Windows command processor commands. There are much better scripting languages for this task.
It would be also much more useful to do this file content modification with a powerful text editor like UltraEdit or any other text editor with Perl regular expression support. Searching for (\\[^\\]+\\)(?=ipm_files) and using as replace string \U$1\E changes the directory name left to ipm_files to upper case and searching for (?<!\\|-)TEST(?=\\ipm_files) and using as replace string -TEST inserts the hyphen character left to TEST if there is not already a hyphen and the entire folder name is not TEST.
However, here is a commented batch file solution for this task:
#echo off
if not exist "%~dp0Test.txt" goto :EOF
setlocal EnableExtensions DisableDelayedExpansion
set "Modified=0"
set "DataFile=%~dp0Test.txt"
set "TempFile=%TEMP%\%~n0.tmp"
del "%TempFile%" 2>nul
for /F delims^=^ eol^= %%I in ('%SystemRoot%\System32\findstr.exe /N "^" "%DataFile%" 2^>nul') do (
set "Line=%%I"
call :ProcessLine
)
if %Modified% == 1 move /Y "%TempFile%" "%DataFile%"
del "%TempFile%" 2>nul
endlocal
goto :EOF
rem The subroutine ProcessLine removes first line number and colon inserted
rem by FINDSTR at beginning of each line to process correct also empty lines
rem in data file. The subroutine jumps to output of line in case of current
rem line is an empty line.
rem Next the line is split up into substrings using backslash as delimiter.
rem Of interest are only the fourth and fifth substrings. The fifth substring
rem should be ipm_files to identify the current line as a line to process.
rem A jump to writing the line into temporary file is done if this condition
rem is not true. Otherwise the fourth substring is assigned to a variable
rem because that string is the folder name to modify by this batch file.
rem Each ASCII character in the folder name is replaced by its upper case character.
rem If the entire new folder name is TEST, just do the replace and don't change
rem the folder name to -TEST. If the new folder name ends already with -TEST,
rem just do the replace. But if new folder name ends with only TEST, replace
rem just TEST by -TEST with hyphen.
rem A case-sensitive comparison of current and new folder name is done before
rem running the folder replace on line to determine if the replace is really
rem necessary at all. The modification information is saved in an environment
rem variable which is passed over local environment of subroutine to main code
rem above. This information is used finally to determine if the data file must
rem be replaced at all by the temporary file because of a modification is made
rem or the temporary file can be simply deleted as being equal with data file.
:ProcessLine
setlocal EnableDelayedExpansion
set "Line=!Line:*:=!"
if not defined Line goto WriteLine
for /F "tokens=4,5 delims=\" %%A in ("!Line!") do (
if /I not "%%B" == "ipm_files" goto WriteLine
set "CurFolderName=%%A"
)
set "NewFolderName=%CurFolderName%"
for %%C in (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) do set "NewFolderName=!NewFolderName:%%C=%%C!"
if "%NewFolderName%" == "TEST" goto DoReplace
if "%NewFolderName:~-5%" == "-TEST" goto DoReplace
if "%NewFolderName:~-4%" == "TEST" set "NewFolderName=%NewFolderName:~0,-4%-TEST"
:DoReplace
if "%CurFolderName%" == "%NewFolderName%" goto WriteLine
set "Modified=1"
set "Line=!Line:%CurFolderName%\ipm_files=%NewFolderName%\ipm_files!"
:WriteLine
echo(!Line!>>"%TempFile%"
endlocal & set "Modified=%Modified%"
goto :EOF
%~dp0Test.txt must be two times replaced by real file name of data file with relative or absolute path.
The purpose of first FOR loop in main code at top is described in my answer on:
How to read and print contents of text file line by line?
The other command lines are explained by the remarks between main code and subroutine.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
del /?
echo /?
endlocal /?
findstr /?
goto /?
if /?
move /?
rem /?
setlocal /?
I would like to create a batch file that will search in a dir for all .pdf files that have a name of 10 characters as we have many .pdf's with different characters in name so I need to sort them out and move (cut and paste) them to a 2nd directory that is prepared. Can you please help me with this batch file?
example
setdir test contain .pdfs
--+6570296402-1-982464371-120.pdf
+6581239585-1-982470028-120.pdf
5710101306.pdf
0-PZ-6562825.pdf
0-PZ-545515247-1-982466351-120.pdf
5455152471.pdf
result:
target dir - test2 - where need to be moved .pdf with 10 characters
5710101306.pdf
5455152471.pdf
etc
Thank you so much
Running from the current directory you could probably do this using Where and Move:
#Echo Off
For /F "Delims=" %%A In ('Where/F .:??????????.pdf'
) Do Move /Y %%A "Test2">Nul
(for /f "delims=" %%a in ('dir /b /a-d *.pdf') do call :select10 "%%a") >filename.txt
... more processing if required
goto :eof
:select10
set "name=%~n1"
set "name=%name:~9%"
if not defined name goto :eof
set "name=%name:~1%"
if not defined name echo %~1
goto :eof
This should solve the problem.
perform a dir list of *.pdf, selecting filenames only. Pass the filename found to subroutine :select10, in quotes in case of spaces in filename.
The subroutine set name first to the name part of the filename received, then removes the first 9 characters. If the result is an empty variable, skip to end-of-file. If not, select all but the first character. If the result is not an empty string, the name must be 11 or more characters - if it's empty, then echo the name passed in the first instance.
The parentheses around the for command will cause the echoed data to be accumulated into the file nominated.
If you want to move the file to the destination, not simply list the selections, remove the ( before the for, and the ) >filename.txt after and replace the echo with move "%1" destination\
You could also do the same without using a subroutine as:
for /f %%a in ('dir /b /a-d *.pdf') do (
set "name=%%~na"
setlocal enabledelayedexpansion
set "name=!name:~9!"
if defined name (
set "name=!name:~1!"
if not defined name move "%%a" destination\
)
endlocal
)
Using delayed expansion to process the substringing operations.
I a new to windows command line scripts.
I have a batch file which i use to merge multiple text files into one. However i want to be able to also add the name of the text file the row comes from to the end of each row in the merged file.
This is the script i am currently working with:
#ECHO OFF
ECHO Creating %1...
FOR /F "usebackq tokens=1" %%G IN (`DIR /B "C:\My Documents\Data\*.txt"`) DO
(
ECHO Adding %%G
ECHO. >> Output.txt
TYPE %%G >> Output.txt
)
Now i know that to get the filename into the output file i need to use:
ECHO %%G >> Output.txt
However i'm not sure how i would add this to the current script so it adds the filename to each row and I have had no luck with finding any examples.
There is a simple two liner that works from the command line if you are willing to prefix each line with the file name instead of putting the file name at the end. This solution is extremely fast.
cd "C:\My Documents\Data"
findstr "^" *.txt >output.log
Each line will have the format of filename:line content
It is important that your output file use a different extension than the files you are merging. Otherwise you run the risk of the command processing its own output!
The other option is to make sure the output goes to a different folder, perhaps the parent folder:
cd "C:\My Documents\Data"
findstr "^" *.txt >..\output.txt
Or, if you are willing to include the full path to each file in your output, then make sure current directory is not the same as your source, and use
findstr "^" "C:\My Documents\Data\*.txt" >output.txt
The only drawback to the above solution is that problems can arise if the last line of a text file does not end with a newline character. This will cause the first line of the next file to be appended to the last line of the prior file. Something like: FILENAME1:last line without newlineFILENAME2:first line of next file
A simple script can append a newline to files that are missing the newline on the last line. Then the files can be safely merged with the filename prefix on each line:
#echo off
if "%~1" equ ":FindFiles" goto :FindFiles
cd "C:\My Documents\Data"
:: Append newline to text files that are missing newline on last line
for /f "eol=: delims=" %%F in ('"%~f0" :FindFiles') do echo(>>"%%F"
:: Merge the text files and prefix each line with file name
findstr "^" *.txt >output.log
exit /b
:FindFiles
setlocal enableDelayedExpansion
:: Define LF to contain a newline character
set lf=^
:: The above 2 blank lines are critical - do not remove
:: List files that are missing newline on last line
findstr /vm "!lf!" *.txt
You'll need to add each line in the file individually:
#ECHO OFF
ECHO Creating %1...
SET "sourcedir=c:\sourcedir"
FOR /F "delims=" %%G IN ('DIR /B /a-d "%sourcedir%\*.txt"') DO (
ECHO Adding %%G
ECHO. >> Output.txt
for /f "usebackq tokens=*" %%a in ("%sourcedir%\%%~G") do (
Echo %%a - %%G >> Output.txt
)
)
Note in the second last line, the file name and the line is seperated by a -, you can replace this with whatever (don't forget to check for escaping characters) or can get rid of this if you want.
I'm sure that will work, but if it doesn't, tell me the Error message and I can fix it for you.
Mona
---- [edit:pw]
Close - major problem was the ( on the FOR ... %%G line was on the line following the DO - must be on the same line as the DO.
Added /a-d to the DIR to prevent subdirectory names matching
changed "usebackq tokens=1" to use conventional quotes and allow spaces in filenames
assigned target directory name to sourcedir variable and included %sourcedir% in both FOR statements to allow execution from anywhere, otherwise the filenames found in C:\My Doc.... would be searched-for in the current directory for replication into the output.
OP needs to change value assigned to sourcedir to C:\My
Documents\Data