Firstly, there are a couple of similar questions on here to this (Rename file based on file Content batch file being the one I have tried to work an answer from - but I have no real clue what I'm doing), however I cannot find anything that meets my exact needs, and this is my first real foray into batch programming so the syntax is fairly new to me.
The question:
I have several hundred text files, with different names, where the header is formatted like so:
"Event Type : Full Histogram"
"Serial Number : xxxxxx"
"Version : V 10.60-8.17 "
"File Name : W133FA0Z.580H"
"Histogram Start Time : 12:39:08"
"Histogram Start Date : 2014-04-11"
I would like if possible to create a batch file to rename all the files in the folder to the format of:
StartDate StartTime
so for this example:
2014-04-11 12:39:08
My problems lie in the fact I'm not sure how to actually point it to where to find the string if it was for just one line (I've tried editing the answers in the question I posted above). And, futhermore, I have no idea how to add a second bit of code to find the StartTime string and then append that to the StartDate.
Thanks in advance,
Chris
Here is a very efficient method.
#echo off
pushd "pathToYourFolderContainingFilesToRename"
for /f "tokens=1,3 delims=:" %%A in (
'findstr /bc:^"\"Histogram Start Date :" *.txt'
) do for /f delims^=^"^ %%C in (
"%%B"
) do for /f tokens^=4-6^ delims^=^":^ %%D in (
'findstr /bc:^"\"Histogram Start Time :" "%%A"'
) do ren "%%A" "%%C %%D.%%E.%%F.txt"
popd
The 1st loop serves two purposes. It establishes file names that contain the start date string, as well as also returning the date string for each file.
The 2nd loop strips out spaces and quotes from the date string.
The 3rd loop parses out the start time from the file.
The 2nd and 3rd loops have very awkward syntax to enable including a quote in the list of delimiters. The 2nd loop sets DELIMS to a quote and a space. The 3rd set DELIMS to quote, colon, and a space.
Assuming you JUST have file formatted like in your description in the working directory :
#echo off&cls
setlocal EnableDelayedExpansion
for %%x in (*.txt) do (
set /a $sw=1
for /f "skip=4 tokens=2-4 delims=:" %%a in ('type "%%x"') do (
if !$sw! equ 1 set $Time=%%a-%%b-%%c
if !$sw! equ 2 (
set $Date=%%a
call:DoRen !$Time:~1,-1! !$Date:~1,-1! %%~nx%%~xx)
set /a $sw+=1
)
)
exit/b
:DoRen
echo ren "%3" "%2 %1"
If the output is OK you can remove the echo
The following will get the output you want, where the output will look like 2014-04-11 123908.
#echo off
set file=test.txt
for /f "delims=: tokens=2-4" %%a in ('find "Time" %file%') do set ftime=%%a%%b%%c
for /f "delims=: tokens=2" %%a in ('find "Date" %file%') do set fdate=%%a
echo %fdate:~1,-1% %ftime:~1,-1%
If all the files are in the same directory, then you can simply do this in a another for loop.
#echo off
setLocal enableDelayedExpansion
for /f %%f in ('dir C:\whatever\path\*.txt /B') do (
for /f "delims=: tokens=2-4" %%a in ('find "Time" %%a') do set ftime=%%a%%b%%c
for /f "delims=: tokens=2" %%a in ('find "Date" %%a') do set fdate=%%a
ren "%%~a" "!fdate:~1,-1! !ftime:~1,-1!.txt"
)
This will rename all text files in a specified directory the date and time in their contents. Note that this does not account for text files that do not have the date and time in their contents. You can (and probably should) add that as you see fit.
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
DEL incorrectformat.log 2>nul
DEL alreadyprocessed.log 2>nul
SET "sourcedir=U:\sourcedir"
FOR /f "delims=" %%a IN ('dir /b /a-d "%sourcedir%\*.txt" ') DO (
SET keepprocessing=Y
SET "newname="
FOR /f "tokens=1-5delims=:" %%b IN (
'TYPE "%sourcedir%\%%a"^|findstr /n /r "$" ') DO IF DEFINED keepprocessing (
IF %%b==1 IF NOT "%%~c"=="Event Type " SET "keepprocessing="&>>incorrectformat.log ECHO %%a
IF %%b==5 SET newname=%%d%%e%%f
IF %%b==6 (
SET "keepprocessing="
SET "newname=%%d!newname!.txt"
SET "newname=!newname:"=!"
SET "newname=!newname:~1!"
IF "!newname!"=="%%a" (>>alreadyprocessed.log ECHO(%%a) ELSE (ECHO REN "%sourcedir%\%%a" "!newname!")
)
)
)
GOTO :EOF
Here's my version.
You'd need to set the value of sourcedir to your target directory.
A list of files not matching the specified format is produced as incorrectformat.log
A list of already-processed files is produced as alreadyprocessed.log
The required REN commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO REN to REN to actually rename the files.
Related
I’m using the following script to add a revision number to a .stp file. For example: ABS0012033.stp to ABS0012033_rev001.stp. This is working well.
Now I have some files that end with _asm.stp, for example, ABS0012033_asm.stp. How do I need to modify the script that it works for both file types?
Some additional information: There is only one .stp file to be renamed per time and then the .stp file will be moved to another folder. The text file which contains the revision number is temporary and will be deleted after renaming the .stp file.
The current script does remove the last 4 characters from the filename and stores the filename in variable str1. Then it renames the filename with str1 + revision number.
for /F "usebackq tokens=2" %%a IN (`findstr REVISION C:\PUBLISH_WORKSPACE\*.txt`) do (
SET Rev=%%a)
FOR %%S IN ( c:\publish_workspace\*.STP) DO (SET FILE=%%S)
set str1=%file:~21,-4%
ren %file% %str1%_rev%REV%.stp
EXIT
I have tried to implement an IF function in the following script, but it doesn’t work. It doesn’t write str1 when renaming the filename. Any ideas what the problem could be?
for /F "usebackq tokens=2" %%a IN (`findstr REVISION C:\PUBLISH_WORKSPACE\*.txt`) do (
SET Rev=%%a)
FOR %%S IN ( c:\publish_workspace\*.STP) DO (SET FILE=%%S)
echo.%FILE%|findstr /C:"_asm" >nul 2>&1
if not errorlevel 1 (
set str1=%file:~21,-8%
ren %file% %str1%_rev%REV%.stp
) else (
set str1=%file:~21,-4%
ren %file% %str1%_rex%REV%.stp
)
pause
move c:\publish_workspace\*.stp c:\publish_workspace\stp
del c:\publish_workspace\*.txt
EXIT
Sorry, I'm not sure to understand the problem. What about this?
set Rev=ren001
ren ABS???????_asm.stp ABS???????_%Rev%.stp
This answer makes assumptions, those assumptions could have been resolved had you responded to my comment one day ago.
#Echo Off & SetLocal EnableExtensions
PushD "C:\Publish_Workspace" 2>NUL && (Set "Rev=") || GoTo :EOF
For /F "Tokens=1,* Delims=:" %%G In ('%SystemRoot%\System32\findstr.exe "REVISION" "*.txt"') Do Set "Rev=%%H"
If Not Defined Rev GoTo :EOF
For /F Delims^=^ EOL^= %%G In ('Set "PathExt=" ^& %SystemRoot%\System32\where.exe ".":"*.stp" 2^>NUL') Do For /F Delims^=_^ EOL^= %%H In ("%%~nG") Do Ren "%%G" "%%H_rev%Rev:* =%%%~xG"
I am not a tutor, and will not be providing additional explanation, other than to refer you to my comments, and the built-in help information for each command, (commandName /?).
I am looking for one solution, which will help me to move files to folders with similar name.
I have filenames like TEST1_2018P2.xlsx, TEST2_2018P2.xslx, etc.
And I have folders with names TEST1_City1, TEST2 City2...
What I need is to move file TEST1_2018P2.xlsx to folder TEST1_City1, TEST2_2018P2.xslx to TEST2 City2 and so on.
How can I do that?
Here's my latest code, which is also not working.
#ECHO OFF
SETLOCAL
SET "sourcedir=my_folder"
SET "destdir=my_folder"
FOR /f "delims=" %%a IN ( 'dir /b /a-d "%sourcedir%\*.xlsx" ' ) DO (
FOR /f "tokens=1delims=" %%b IN ("%%a") DO (
FOR /f "delims=" %%d IN ( 'dir /b /ad "%destdir%\*%%b*" ' ) DO (
ECHO(MOVE "%%a" "%destdir%\%%d\"
)
)
)
GOTO :EOF
I'm not sure of your exact task, so this relatively basic example should move any .xlsx file to the first existing directory whose name matches the portion of the filename up to the underscore, plus a space.
Adjust the values on lines 2 and 3 to match your actual directory specs, (without trailing backslashes).
#Echo Off
Set "SourceDir=my_folder"
Set "DestDir=my_folder"
For /F Delims^=^ EOL^= %%A In ('Dir /B/A-D-L "%SourceDir%\*_*.xlsx" 2^>Nul'
) Do Call :Sub "%%A"
GoTo :EOF
:Sub
Set "DirName=%~1"
Set "DirName=%DirName:_="&:"%"
For /F Delims^=^ EOL^= %%A In ('Dir /B/AD-L "%DestDir%\%DirName% *" 2^>Nul'
) Do If Exist "%SourceDir%%~1" Move /Y "%SourceDir%\%~1" "%DestDir%\%%A" 2>Nul
Exit /B
It was not designed to be the most efficient method of performing the task!
Please also note that your existing directory names did not have a clear pattern so this was written for TEST1 City1 TEST2 City2 etc.
If they are all underscores, e.g. TEST1_City1 TEST2_City2 etc. then change "%DestDir%\%DirName% *" on line 11 to "%DestDir%\%DirName%_*".
If the directories can be either of them, and you are sure that no two directories will begin with the string TEST1, TEST2 etc., (which would limit you to only numbers 0..9 in this case), you could probably use "%DestDir%\%DirName%?*" on line 11 as an alternative.
Assuming that actual text of TEST1 doesn't contain any _ characters, you can use:
#echo off
setlocal EnableDelayedExpansion
cd /d "your_folder"
for /F "delims= eol=" %%A IN ('dir /B /A-D "TEST*_2018P2.xlsx"') do (
for /F "tokens=1 delims=_" %%B IN ("%%A") do (
rem Define some important variables:
set "token_1=%%B"
set "num_test=!token_1:~-1!"
set "foldername=!token_1!_City!num_test!"
md "!foldername!\" >nul 2>&1
move "%%~fA" "!foldername!\"
)
)
Let me explain my code:
The first for /F loop is used to find all the files you want (TEST*_2018P2.xlsx) excluding all directories (/A-D) and headers. delims= and eol= options are used: loop through the whole line without skipping lines starting with ;.
The second for /F loop is used to get the first token of the output of the first loop (IN ("%%A")).
The first token is set to token_1 variable and then substract the last number/letter from it setting it to the num_test variable.
A foldername is set because it is used two time, it is really hard to understand this code without setting it in a variable. It is actually set by token_1 variable (TESTn), _City and n (number).
A folder is created with that name. Both STDIN and STDERR are redirected to nul. This happens not to have many processed if exist statement. The current file (%%~fA; full path) is moved to this folder.
Remember to replace "your_folder" with your actual folder!
I have a file named C:\Submitlogs\SubmitLog.txt
Log file content:
--------------
User01,15/05/2018,10:26,Submit,CATERGORY1,WEEK46
User01,17/05/2018,10:29,Submit,CATERGORY1,WEEK46
User04,17/05/2018,11:04,Submit,CATERGORY1,WEEK46
User11,17/05/2018,11:09,Submit,CATERGORY1,WEEK46
So, If the system date is 17/05/2018 then the batch script should copy only the content (mentioned below) from the above which is of 17/05/2018 and paste it in C:\Submitlogs\Copy.txt
User01,17/05/2018,10:29,Submit,CATERGORY1,WEEK46
User04,17/05/2018,11:04,Submit,CATERGORY1,WEEK46
User11,17/05/2018,11:09,Submit,CATERGORY1,WEEK46
I have managed to write the below script but I am getting the output as below.
#echo off
setlocal enableextensions
set sysdt=%date:~-7,2%/%date:~-10,2%/%date:~-4,4%
echo %sysdt%
set "source=C:\Submitlogs"
set "target=C:\Submitlogs\Copy.txt"
pushd "%source%"
(for /f "tokens=1,* delims=," %%a in ('findstr /i /C:%sysdt% "SubmitLog.txt"') do (
echo(%%b )) > "%target%"
popd
It is taking the values after the searched string. Kindly let me know how to get the full line as the output.
Current output:
17/05/2018,10:29,Submit,CATERGORY1,WEEK46
17/05/2018,11:04,Submit,CATERGORY1,WEEK46
17/05/2018,11:09,Submit,CATERGORY1,WEEK46
Expected output:
User01,17/05/2018,10:29,Submit,CATERGORY1,WEEK46
User04,17/05/2018,11:04,Submit,CATERGORY1,WEEK46
User11,17/05/2018,11:09,Submit,CATERGORY1,WEEK46
I would suggest you use a method of determining the date which will give consistent results regardless of user settings or locales.
This method uses the RoboCopy command:
#Echo Off
Set "parent=C:\Submitlogs"
Set "source=SubmitLog.txt"
Set "target=Copy.txt"
If Not Exist "%parent%\%source%" Exit /B
For /F "Tokens=1-3 Delims=/ " %%A In ('RoboCopy/NJH /L "\|" Null'
) Do Set "moment=%%C/%%B/%%A" & GoTo :Break
:Break
Find "%moment%"<"%parent%\%source%">"%parent%\%target%"
This is my first posting so if the format is not as it supposed to be please excuse me for this. (Suggestions for
improvement are welcome.)
I am trying to create a batchfile that will read last lines from logfiles and copy them to a new file.
Until now I have found here a way to read the last line.
Code would be something like:
for /f %%i in ('find /v /c "" ^< someFile.txt') do set /a lines=%%i
set /a startLine=%lines% - 1
more /e +%startLine% someFile.txt > lastLines.txt
The above code works for one file at a time. What I need is to read the last line from all files in a known list and add this line to a new .csv file.
I have been using the following code for getting the 4th entry in the logfiles but it returns every line of every logfile:
for /f %%x in (%list%) do for /f "delims=.txt, tokens=4" %%i in (%%x.txt) do echo %%x, %%i >> output.csv
What I would need is a sort of combination of both but I don't know how to combine them and make the complete last line be copied to the .csv file.
===
#Magoo:
Thanx for your reaction.
In every logfile can be 1 to >100 lines with comma separated information. Something like:
"LOGON,6-1-2015,12:43:39,USERNAME,HOSTNAME,,,,192.168.209.242,00:21:5A:2E:64:5E"
The last code with the 4th entry was used to get a list of all accounts that had logged in to the computers. This code gave me a very large list of all logon/logoff events on all computerlogs I checked in %list%.
In %list$ I had all the names of logfiles I wanted to be checked. This returned all lines.
For a new batchfile I need only the last logon/logoff entry and I want the whole last line.
So I have a .txt file with the hostnames of all computers I need to examine.
This .txt file will be read line by line via the variable %list%.
From every logfile I need only the last line copied to an output file.
===
I just tried the solution offered by JosefZ. Unfortunately this does not work for me yet. No lastlines are copied to the resultfile. In the code I removed the extra entry for possible lastlines for there are no empty lines in the logs, I also added an entry for the hostname I want to be available in the result. JosefZ had the filename there:
#ECHO OFF >NUL
#SETLOCAL enableextensions disabledelayedexpansion
type nul>output.csv
set "list=_listing.txt"
for /F "tokens=*" %%x in ('type "%list%"') do (
set "host=%%~x"
for /F "tokens=*" %%G in ('type "%%~x"') do set "lastline=%%G"
call :lline
)
:endlocal
#ENDLOCAL
goto :eof
:lline
set "filename=.\logs\%filename:&=^&%.txt"
echo %host%,%lastline%>>output.csv
goto :eof
The resultfile shows only the hostnames. I'll puzzle some more with this but all tips are welcome!
===
Got it!!!
#ECHO OFF >NUL
#SETLOCAL enableextensions disabledelayedexpansion
type nul>output.csv
set "list=_listing.txt"
for /F "tokens=*" %%x in ('type "%list%"') do (
set filename= :: *empty previous filename*
set lastline= :: *empty previous lastline*
set "host=%%~x"
set "filename=.\logs\%host%.txt" :: *creating the filename from path+hostname+extention*
for /F "tokens=*" %%G in ('type "%filename%"') do set "lastline=%%G"
call :lline
)
:endlocal
#ENDLOCAL
goto :eof
:lline
echo %host%,%lastline%>>output.csv
goto :eof
Your approach with line numbering could fail if a file has more trailing empty lines. Fortunately for /F loop ignores (does not iterate) empty lines; let's put to use this feature: in the script used next practices:
disabledelayedexpansion to allow ! in file names
set "list=_listing.txt" where the _listing.txt contains list of file names (full path and extension .txt including), one file name on one line: got by dir /b /s *.txt>_listing.txt
type nul>files\output.csv to empty the output file (optional)
set "lastline=!!!file empty!!!" to initialize variable %lastline%; could be set "lastline=" as well
call :lline to process variables %filename% and %lastline%
set "filename=%filename:&=^&%" to allow & in file names
The script is as follows:
#ECHO OFF >NUL
#SETLOCAL enableextensions disabledelayedexpansion
type nul>files\output.csv
set "list=_listing.txt"
for /F "tokens=*" %%x in ('type "%list%"') do (
set "filename=%%~x"
set "lastline=!!!file empty!!!"
rem the whole line
for /F "tokens=*" %%G in ('type "%%~x"') do set "lastline=%%G"
rem the fourth token only
rem for /F "tokens=4" %%G in ('type "%%~x"') do set "lastline=%%G"
call :lline
)
:endlocal
#ENDLOCAL
goto :eof
:lline
set "filename=%filename:&=^&%"
echo %filename% %lastline%
rem >>files\output.csv
goto :eof
Sample _listing.txt file:
d:\bat\files\1exclam!ation.txt
d:\bat\files\2exc!lam!ation.txt
d:\bat\files\11per%cent.txt
d:\bat\files\12per%cent%.txt
d:\bat\files\17per%Gcent.txt
d:\bat\files\18per%%Gcent.txt
d:\bat\files\21ampers&nd.txt
d:\bat\files\22ampers&&nd.txt
Output:
d:\bat>lastlines
d:\bat\files\1exclam!ation.txt 0 15.01.2015 1:52:28.48 -15072 20465
d:\bat\files\2exc!lam!ation.txt 6 15.01.2015 1:52:28.50 3250 16741
d:\bat\files\11per%cent.txt -8 15.01.2015 1:52:28.50 -3692 27910
d:\bat\files\12per%cent%.txt !!!file empty!!!
d:\bat\files\17per%Gcent.txt 0 15.01.2015 1:52:28.56 14508 12374
d:\bat\files\18per%%Gcent.txt 1 15.01.2015 1:52:28.56 30540 26959
d:\bat\files\21ampers&nd.txt 15.01.2015 1:22:50.18
d:\bat\files\22ampers&&nd.txt 15.01.2015 1:22:50.18
Honestly, all that ballast is for (possibly) trailing empty lines in files and for (possibly) ! and & in file names only; all could be done with
for /f %%x in (%list%) do for /f "skip=%startLine% tokens=4" %%i in (%%x) do echo %%x, %%i >> output.csv
You should use a simple FOR to iterate a list of values, not FOR /F.
Something like the following should work:
#echo off
setlocal enableDelayedExpansion
>>output.csv (
for %%F in (
"file1.log"
"file2.log"
"file3.log"
etc.
) do (
for /f %%A in ('find /v /c "" <%%F') do set /a skip=%%A-1
more +!skip! %%F
)
)
The quotes around the file names are there in case you get a name with spaces.
You could use your LIST variable if it looks something like
set LIST="file1.log" "file2.log" "file3.log" etc.
#echo off
setlocal enableDelayedExpansion
set LIST="file1.log" "file2.log" "file3.log" etc.
>>output.csv (
for %%F in (%LIST%) do (
for /f %%A in ('find /v /c "" <%%F') do set /a skip=%%A-1
more +!skip! %%F
)
)
If any of your file names contain the ! character, then you must toggle delayed expansion ON and OFF within your loop. Otherwise the delayed expansion will corrupt the names when %%F is expanded.
#echo off
setlocal disableDelayedExpansion
set LIST="file1.log" "file2.log" "file3.log" etc.
>>output.csv (
for %%F in (%LIST%) do (
for /f %%A in ('find /v /c "" <%%F') do set /a skip=%%A-1
setlocal enableDelayedExpansion
more +!skip! %%F
endlocal
)
)
I have a simply FOR /F loop which strips out all but one line of a text file:
for /f "skip=12 tokens=* delims= " %%f in (.\NonProcessed\*.txt) do (
> newfile.txt echo.%%f
goto :eof
)
But when I run, I get the result:
The system cannot find the file .\NonProcessed\*.txt
The for loop works fine if I enter a fully qualified path to the text file within the brackets, but it can't handle the relative link I have in there. I've been able to use the exact same relative link in another standard for loop in a different batch file running in the same directory without any issues. I can't understand why it won't work! Please help.
EDIT: For comments, code I'm using now is
for %%f in (.\NonProcessed\*.txt) do (
echo f is %%f
for /f "usebackq skip=12 tokens=* delims= " %%a in (%%f) do (
echo a is %%a
> %%f echo.%%a
goto :continue
)
:continue
sqlcmd stuff here
)
Sorry but for /f does not allow you to do that. And no, the problem is not the relative path to files but the wildcard.
According to documentation, you have the syntax case
for /F ["ParsingKeywords"] {%% | %}variable in (filenameset) do command [CommandLineOptions]
For this case, documentation states The Set argument specifies one or more file names. You can do
for /f %%a in (file1.txt file2.txt file3.txt) do ...
but wildcards are not allowed.
If you don't know the name of the file you want to process, your best option is to add an additional for command to first select the file
for %%a in (".\NonProcessed\*.txt"
) do for /f "usebackq skip=12 tokens=* delims= " %%f in ("%%~fa"
) do (
> newfile.txt echo(%%f
goto :eof
)
When executed, the goto command will cancel both for loops so you end with the same behaviour you expected from your original code.
edited to adapt code to comments
#echo off
set "folder=.\NonProcessed"
pushd "%folder%"
for /f "tokens=1,2,* delims=:" %%a in (
' findstr /n "^" *.txt ^| findstr /r /b /c:"[^:]*:13:" '
) do (
echo Overwrite file "%%a" with content "%%c"
>"%%a" echo(%%c
)
popd
Read all the files in the folder, numbering the lines. The output for the first findstr command will be
filename.txt:99:lineContents
This output is parsed to find the line 13, the resulting data is splitted using the colon as a separator, so we will end with the file name in %%a, the line number in %%b and the line content in %%c.
SET FILES_LIST=files_list.config
DIR /b .\NonProcessed\*.txt>!FILES_LIST!
for /f "skip=12 tokens=* delims= " %%f in (!FILES_LIST!) do (
> newfile.txt echo.%%f
goto :eof
)
IF EXIST "!FILES_LIST!" DEL "!FILES_LIST!"
I did not check how your's FOR works, just added my additions/corrections to it.... Hope it will work for you.
Best regards!