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.
Related
I want to make a program that takes the content of the second line of a text file and puts it on the first. (It doesn't matter if the second doesn't get edited)
for /f "tokens=1" %%t in (file.txt) do set string1=%%t
for /f "tokens=2" %%t in (file.txt) do set string2=%%t
echo %string1%%string2%>file.txt
I have two issues hat I can't seem to be able to fix.
One: the loops only include the first word of each line in the variables.
Two: Echo doesn't replace the first line of the file with the variables given and instead writes ECHO command deactivated (I have the French version of Windows 10 and simply translated what got written in the file, the text in English Windows version might be slightly different, but you get the idea)
If you have any suggestions, I would appreciate if you explain what the code you provide does (I always like to learn)
Your question is not clear and can be understood in several different ways. Anyway, this management is simpler with no for command:
#echo off
setlocal EnableDelayedExpansion
< file.txt (
rem Takes the content of the first line
set /P "line1="
rem Takes the content of the second line and puts it on the first
set /P "line2="
echo !line1!!line2!
rem It doesn't matter if the second line doesn't get edited
echo !line2!
rem Copy the rest of lines
findstr "^"
) > output.txt
move /Y output.txt file.txt
The FOR command uses a space as a delimiter by default. So you have to tell it to not use any delimiters with the DELIMS option. Also, you should be able to do this with a single FOR /F command. Just hold the previous line in a variable.
#ECHO OFF
setlocal enabledelayedexpansion
set "line1="
(for /f "delims=" %%G in (file.txt) do (
IF NOT DEFINED line1 (
set "line1=%%G"
) else (
echo !line1!%%G
set "line1="
)
)
REM If there are an odd amount of lines, line1 will still be defined.
IF DEFINED line1 echo !line1!
)>File2.txt
EDIT: I think I completely misunderstood your question. Once you clarify your question I will repost a code solution if needed.
Use skip to omit the first line and write the 2nd line twice. In general an edit of a file implies a rewrite to a new file and possibly a rename to retain the old file name.
:: Q:\Test\2018\07\25\SO_51508268.cmd
#Echo off
Set "flag="
( for /f "usebackq skip=1 delims=" %%A in ("file1.txt") Do (
If not defined flag (
Echo=%%A
Set flag=true
)
Echo=%%A
)
) >file2.txt
Del file1.txt
Ren file2.txt file1.txt
After running the batch a file1.txt with initially numbered lines 1..5 looks like this:
> type file1.txt
2
2
3
4
5
I have a hundred of text files with the below structure:
file1.txt
Class Categeory_1 {
(content 1, may contain many other block ending with }; )
};
file2.txt
Class Categeory_2 {
(content 2, may contain many other block ending with }; )
};
I would like to merge all the files without the first and last line of each file, so the output.txt should be:
(content 1, may contain many other block ending with }; )
(content 2, may contain many other block ending with }; )
...
Files names are random, and class name are also random, but starting with "Category_"
I know how to merge all of the files together:
#echo off
for /r %%i in (*.txt) do (
if not %%~nxi == output.txt (
copy /A output.txt+"%%i" && echo. >> output.txt
)
)
but not sure how to skip the first and last line of each file. Can you please provide some help, thank you.
Here is a working code sample
#echo off
setlocal enabledelayedexpansion
if exist output.txt del output.txt
set "var="
for /r %%i in (*.txt) do (
if "%%~nxi" NEQ "output.txt" (
set "var="
for /f "usebackq skip=1 delims=" %%b in ("%%~i") do (
if "!var!" NEQ "" Echo !var!
set var=%%b
))) >> output.txt
Here's a quick summary of what it does:
Setlocal allows the reference of updated variable values in the for-loop
Delete an existing output and reset var variable
For every text file, that is not output.txt
Reset the value of var to nothing
For every line in this text-file, skipping the first line:
If var is not empty, echo its value
Set the value of var to the current line
>> redirect all echo's into output.txt
Note, the order of the last 2 steps is what allows you to skip the last line since you are always echoing the previous line.
This means if the file only has 2 lines, it will not echo anything,
If it has 3 lines it will only echo the middle one,
And if it has 4 lines it will echo the middle two, etc.
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.
I have a batch script that will combine all .txt files with a few caveats, such as adding a comma in between each file and adding a square bracket at the start and end of the output file.
echo [ >> output.txt
for %f in (*.txt) do type "%f" >> output.txt & echo. >> output.txt & echo , >> output.txt
echo ] >> output.txt
What I would like to do it limit the output.txt to 10,000 txt files, whilst creating a new output.txt for the next 10,000 files. So for 25,000 records i will end up with;
Output1.txt (10,000 txt files)
Output2.txt (10,000 txt files)
Output3.txt (5,000 txt files)
How can I change my script to accommodate this?
Also if possible, I don't really want a comma at the end of the very last record that it combines. Is there a way to achieve this?
There are issues with your existing code:
A simple FOR loop will likely include your Output file(s). Obviously you don't want that. That could be prevented by writing the file list to a temp file, before any output is created. Easily done via DIR /B /A-D *.txt >tempFile
It takes significant time to repeatedly open the same output file thousands of times. Better (faster) to open it once, if possible.
Ideally, the final code should do most of the processing in some kind of FOR loop, with delayed expansion to enable working with changing values within the loop. Reading file names with a FOR loop can cause problems with delayed expansion because it will corrupt any name that happens to contain !. It takes a bit more code, but using SET /P to read the file is significantly faster, and delayed expansion doesn't cause problems.
Typically you must know the total number of lines in a file to detect the end when using SET /P. But in this case, the absence of data in a row indicates the end of file - There can never be any empty lines in DIR /B output.
The following code is untested, but if it does not work, then any fix should be minor.
#echo off
setlocal enableDelayedExpansion
dir /b /a-d *.txt >files.temp
set /a cnt=0
call :read <files.temp
del files.temp
exit /b
:read
set "file="
set /p "file="
if not defined file exit /b
set /a cnt+=1
call :write >output%cnt%.txt
goto :read
:write
echo [
type "!file!"
echo(
for /l %%N in (2 1 10000) do (
set "file="
set /p "file="
if not defined file goto :end
echo ,
type "!file!"
echo(
)
:end
echo ]
exit /b
I am relearning DOS/batch & seems different.
Script should create a text file (it does, verified) then read it line by line looking for a beginning word and discarding lines thru that line as it builds TMP.txt.
It should then create a file of remaining lines up to and including a finishing word and ignore the remainder.
Then it will look for a line containing a specific word.
Problem: I get ("inFile.txt") was unexpected. The reams of text I have pulled down into my reference directory has not been helpful as it appears (does't it always) correct from what I expect.
Here is the script to and a bit after that point:
REM Append a final line to input text
echo IDSS >> inFile.txt
REM Bookend the node's text with BEGwd and FINwd.
REM Read inFile.txt file- Ignore %%A if BEGwd not found,
REM else write remaining lines to TMP.
for /F "tokens=*" %%A ("inFile.txt") DO ( <<---- Problem line
if "*%BEGwd%*" == "*%%A*" echo "%%A" >> TMP.txt
)
REM Read TMP look for FINwd- Write non-%%B lines to TMP2
REM until FINwd
for /F "tokens=*" %%B ("TMP.txt") DO (
if "*%FINwd%*" == "*%%B*" goto SEARCH
echo "%%B" >> TMP2.txt
)
Am also unsure if batch will accept my IF command compare with *'s?
Appreciating any assistance there too.
for /F "tokens=*" %%A ("inFile.txt") DO ( <<---- Problem line
Correct syntax is for /F "USEBACKQtokens=*" %%AIN("inFile.txt") DO (
the usebackq option is required because you have "quoted your filename".