Can't get last 2 characters in token, and for loop returns first token in a vaiable - batch-file

Ok, here's the set up:
I have a ton of text files with the following format:
filename## time Description of file with spaces
I'm trying to rename the actual file on disk with the description of the file, using a for loop:
FOR /F "tokens=1,2,*" %%a IN (%1) DO call :rename %%a %%b "%%c"
goto :eof
:rename
set origfilename=%1
set last2=%1:~-2%
set time=%2
set "multiword=%~3"
ren %origfilename%.ext "%last2%_%time%_%multiword%.ext"
set origfilename=
set last2=
set time=
set multiword=
Here's what's happening:
last2 is never picking up the last 2 characters of the original file name, and if I echo %3 after the DO instead of the call to :rename, it has the full token with spaces. When I try to use %3 in the loop, it only contains the first token of %3.
I tried using setlocal EnableDelayedExpansion before the for, but it didn't work as I expected.
Am I trying to do too many things at once? Thanks for the help!

Try this. I think this will do what you want.
#echo off
for /f "Tokens=1,2,3*" %%a in ('dir /b filename*') do (
call :rename "%%a %%b %%c %%d" %%a %%b "%%c %%d")
goto :eof
:rename full orig time new
echo %2
set last2=%2
set last2=%last2:~-2%
echo ren "%~1.ext" "%last2%_%3_%~4.ext"
Remove the Echo if everything looks good

Related

Batch folder creation based on part of file name, without keeping extension

Trying to create a script that will take the third token of a file name, create a folder based on it and move the associated file to that folder.
Have got this so far:
#ECHO OFF
SETLOCAL
SET "sourcedir=D:\Sourcedir"
PUSHD %sourcedir%
FOR /f "tokens=1,2,3,4 delims=" %%a IN (
'dir /b /a-d "*.pdf"'
) DO (
ECHO MD %%c
ECHO MOVE "%%a %%b %%c %%d" .\%%c\
)
POPD
GOTO :EOF
Only problem is the folder being created is including the file extension where as I just need the folder to be named the third token.
Example file name:
"File Number 10.pdf
Expected folder name:
10
Thanks
Why did you use delims=? This will remove delimiter, and take whole line to %%a.
Try this:
#ECHO OFF
SETLOCAL
SET "sourcedir=D:\Sourcedir"
PUSHD %sourcedir%
FOR /f "tokens=1,2,3" %%a IN (
'dir /b /a-d "*.pdf"'
) DO (
ECHO MD %%~nc
ECHO MOVE "%%a %%b %%c" .\%%~nc\
)
POPD
GOTO :EOF
When no delims= set, it will use space. So %%c will be 10.pdf, ~n is to extract its name part.
This is based on your question, which you can concatenate %%a %%b %%c together with spaces, then it's simple.
If your filenames are more complicated, then an inner for loop is better.
-- Which another question already gave a great solution.
Here's an alternative, which will just use the last space delimited string/number, regardless of how many there are, (if there are none it will use the whole filename)!
#Echo Off
For %%A In ("D:\Sourcedir\*.pdf") Do Call :L "%%A"
Exit /B
:L
Set "F=%~n1"
Set "F=%F: ="&Set "F=%"
If Not Exist "%~dp1%F%\" MD "%~dp1%F%"
Move /Y %1 "%~dp1%F%"
And if you wanted to move only those which have at least one space, you can include that inside the For parentheses.
#Echo Off
For %%A In ("D:\Sourcedir\* *.pdf") Do Call :L "%%A"
Exit /B
:L
Set "F=%~n1"
Set "F=%F: ="&Set "F=%"
If Not Exist "%~dp1%F%\" MD "%~dp1%F%"
Move /Y %1 "%~dp1%F%"
You can run 2 for loops get the full name in the first, then split the name in the second loop, get the 3rd token, create the directory and then copy the actual file name from the first loop.
This way you do not need to try and patch the name together again, I know it works, but it is ugly and not prefered:
#echo off
setlocal enabledelayedexpansion
set "sourcedir=D:\Sourcedir"
pushd %sourcedir%
for %%a in (*.pdf) do (
set "var=%%a"
for /f "tokens=3" %%i in ("!var!") do (
echo md "%%~ni"
echo move "%%~a" "%%~ni"
)
)
popd
goto EOF
For more information on these commands, see help for each from cmd.exe i.e
for /?
set /?
setlocal /?
set and setlocal has very specific information regarding delayed expansion.

Batch File String Management in FOR Loop

Am trying to use substring manipulation within a FOR loop and I can't get it to work for love nor money. I've been told that we can't use substring manipulation on a loop variable (%%f etc), so you have to set another variable to equal the loop (set MyVariable=%%f) and then use a substring option to work on this decendent (set MyOtherVar=%MyVariable:~0,-3%). However when echoing these variables only the loop variable is set/non-null.
Here's the code I'm currently using.
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
SET OutPref="373137#"
SET OutSuf1="#Window Clean POD "
SET OutSuf2="#1520755.pdf"
SET MatchStr=#mydomain.com
cd c:\Oscar\Scripts\FileNamer\Inbound\
for /f "tokens=*" %%f in ('dir /b *.pdf') do (
c:\Oscar\Scripts\FileNamer\pdftotext.exe -q %%f
set InPdfVer=%%f
set InTxtVer=%InPdfVer:~0,-3%txt
echo Loop Val= %%f
echo InPdfVer= %InPdfVer%
echo InTxtVer= %InTxtVer%
pause
set InAddLine=findstr %MatchStr% %InTxtVer%
set stemp=%InAddLine%
set pos=0
:loop
set /a pos+=1
echo %stemp%|findstr /b /c:"%MatchStr%" >NUL
if errorlevel 1 (
set stemp=%stemp:~1%
if defined stemp GOTO loop
set pos=0
)
set /a pos-=3
call set StoreNo=%InAddLine:~%pos%,-25%
call:getvalue C:\Oscar\Scripts\FileNamer\StoreList.inf %StoreNum% StoreName
set OutFile=%OutPerf%%StoreNo%%OutSuf1%%StoreName%%OutSuf2%
move %%f c:\Oscar\Scripts\FileNamer\Outbound\%OutFile%
)
cd c:\Oscar\Scripts\FileNamer\
exit 0
:getvalue
rem This function reads a value from a file and stored it in a variable
rem %1 = name of file to search in
rem %2 = search term to look for
rem %3 = variable to place search result
FOR /F "tokens=1,2* delims==" %%i in ('findstr /b /l /i %~2= %1') DO set %~3=%%~j
goto:eof
Hope this makes sense, can try and explain it. Quite likely the bottom part doesnt work either but didnt get that far!
Thanks for any thoughts either way, as a general overview the script is supposed to take PDF files in the incoming folder, convert them to text, search for an email address in that file, look that email address in an external list and then move the PDF file (renaming the file with an aggreed convention in the process) and then move onto the next file, in a loop, until the end of the matching files.
Kind regards,
Oscar
OK so the rest of it seems to what what it should now but I still can't get this substring to set, I just end up with the whole string in the decendent variable. Here's the new code (please excuse the pauses and echos used for troubleshooting).
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
SET OutPref=373137#
SET OutSuf1=#Window Clean POD
SET OutSuf2=#1520755.pdf
SET MatchStr=#mydomain.com
cd c:\Oscar\Scripts\FileNamer\Inbound\
for /f "tokens=*" %%f in ('dir /b *.pdf') do (
c:\Oscar\Scripts\FileNamer\pdftotext.exe -q %%f
set InPdfVer=%%f
call set InTxtVer=!InPdfVer:~0,-3!txt
for /f "tokens=*" %%x in ('findstr !MatchStr! !InTxtVer!') do set InAddLine=%%x
call:getpos
echo !pos!
pause
call set StoreNo=!InAddLine:~!pos!,-25!
call:getvalue C:\Oscar\Scripts\FileNamer\StoreList.inf !StoreNum! StoreName
echo OutPerf !OutPref!
echo StoreNo !StoreNo!
echo OutSuf1 !OutSuf1!
echo StoreName !StoreName!
echo Outsuf2 !OutSuf2!
set OutFile=!OutPerf!!StoreNo!!OutSuf1!!StoreName!!OutSuf2!
echo %%f !OutFile!
pause
REM move %%f c:\Oscar\Scripts\FileNamer\Outbound\!OutFile!
)
cd c:\Oscar\Scripts\FileNamer\
exit /b
:getpos
set stemp=!InAddLine!
set pos=0
:loop
set /a pos+=1
echo !stemp!|findstr /b /c:"!MatchStr!" >NUL
if errorlevel 1 (
set stemp=!stemp:~1!
if defined stemp GOTO loop
set pos=0
)
set /a pos-=3
goto:eof
:getvalue
rem This function reads a value from a file and stored it in a variable
rem %1 = name of file to search in
rem %2 = search term to look for
rem %3 = variable to place search result
FOR /F "tokens=1,2* delims==" %%i in ('findstr /b /l /i %~2= %1') DO set %~3=%%~j
goto:eof
Thanks all for your inputs, here's the finished script with a few more updates.
Makes use of pdftotext.exe as part of the freeware xpdf suite (please donate as it's a great utility) and in this case some lookup files that help resolve a site number to its description. In the format of
001=My Town
I totally failed to get a workable way to pad 2 digit site codes with a leading 0 to make all sites 3 digits but ended up doing the same thing with another lookup file!
I hope this is of some use to someone else!
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
SET OutPref=373137#
SET OutSuf1=#My Text
SET OutSuf2=#1520755.pdf
SET MatchStr=#mydomain.com
SET BaseDir=C:\myfolder\
SET LogFile=C:\myfolder\FileNamer.log
SET InFolder=C:\myfolder\Inbound\
SET OutFolder=C:\myfolder\Outbound\
SET StoreList=C:\myfolder\StoreList.inf
SET StoreNoConv=C:\myfolder\StoreNoConv.inf
echo Starting Run %TIME% %DATE% >> %LogFile%
echo Starting Run %TIME% %DATE%
cd %InFolder%
for /f "tokens=*" %%f in ('dir /b *.pdf') do (
%BaseDir%pdftotext.exe -q %%f
set "InTxtVer=%%~nf.txt"
for /f "tokens=*" %%x in ('findstr !MatchStr! !InTxtVer!') do set InAddLine=%%x
call:getpos
call set StoreNo=%%InAddLine:~!pos!,-25%%
echo Now Renaming Store No !StoreNo!
call:getvalue %StoreList% !StoreNo! StoreName
call:getvalue %StoreNoConv% !StoreNo! ThreeDigitNo
set OutFile=!OutPref!Store!ThreeDigitNo!!OutSuf1!!StoreName!!OutSuf2!
echo %%f moved to !OutFile! >> %LogFile%
move "%%f" "%OutFolder%!OutFile!" >> %LogFile%
del !InTxtVer!
)
cd %BaseDir%
exit /b
:getpos
set stemp=!InAddLine!
set pos=0
:loop
set /a pos+=1
echo !stemp!|findstr /b /c:"!MatchStr!" >NUL
if errorlevel 1 (
set stemp=!stemp:~1!
if defined stemp GOTO loop
set pos=0
)
set /a pos-=4
goto:eof
:getvalue
rem This function reads a value from a file and stores it in a variable
rem %1 = name of file to search in
rem %2 = search term to look for
rem %3 = variable to set search result under
FOR /F "tokens=1,2* delims==" %%i in ('findstr /b /l /i %~2= %1') DO set %~3=%%~j
goto:eof

Using endlocal set variables in a batch file, but the variables are never available outside of the setlocal/endlocal block

I'm trying to set the value of a variable that is inside a SETLOCAL, inside a FOR loop and inside an IF statement. However, it never seems to work. I tried using a SET statement at the ENDLOCAL statement, but that doesn't seem to actually set anything. Echoing the variable after that only echos the original set value of 0.
#ECHO off
SET pathsource=K:
SET sourcefile=0
SET counter=0
SETLOCAL enableextensions enabledelayedexpansion
REM Get the newest pptx file in the specified directory. Get the filename and last modified timestamp
FOR /f "tokens=1-3,5*" %%a IN ('dir ^"%pathsource%\*.pptx^" /a-d-h-s /o-d /tw ^| find /i ^".pptx^"') DO (
REM echo !counter!
REM only get the first row by using a counter
IF !counter! EQU 0 (
REM variables are: a-date, b-time, c-am/pm, d&e-filename
ECHO %%a %%b %%c %%d %%e
SET sourcefile=%%d %%e
)
SET /A counter+=1
)
ENDLOCAL & (
SET "sourcefile=%sourcefile%"
)
ECHO %sourcefile%
REM do other stuff with the %sourcefile% variable after this
Because you assign the values within the Set/End Local block, any changes made within this are discarded once on the ENDLOCAL command is reached.
Just move your SETLOCAL and ENDLOCAL commands to the very top and bottom of your script respectively. This would make all assignments made within the entirety of script stick throughout.
Additionally, you don't really need the counter variable. You can just jump out of the loop after the first file is processed:
#ECHO off
REM delayed expansion not needed for the portion shown
SETLOCAL enableextensions
SET pathsource=K:
SET sourcefile=0
REM Get the newest pptx file in the specified directory. Get the filename and last modified timestamp
FOR /f "tokens=1-3,5*" %%a IN ('dir ^"%pathsource%\*.pptx^" /a-d-h-s /o-d /tw ^| find /i ^".pptx^"') DO (
REM variables are: a-date, b-time, c-am/pm, d&e-filename
ECHO %%a %%b %%c %%d %%e
SET sourcefile=%%d %%e
REM We only care about the first row
GOTO EndLoop
)
:EndLoop
ECHO %sourcefile%
REM do other stuff with the %sourcefile% variable after this
ENDLOCAL
One last thing is you can simply your FOR syntax a bit by using native DIR and FOR variable commands:
#ECHO off
REM delayed expansion not needed for the portion shown
SETLOCAL enableextensions
SET pathsource=K:
SET sourcefile=0
REM Get the newest pptx file in the specified directory. Get the filename and last modified timestamp
CD "%pathsource%\"
FOR /f "usebackq tokens=* delims=" %%a IN (`dir "%pathsource%\*.pptx" /a-d-h-s /o-d /tw /b`) DO (
REM Use For loop variables.
REM Print the date/time and file name.
ECHO %%~ta %%~a
SET sourcefile=%%~a
REM We only care about the first row
GOTO EndLoop
)
:EndLoop
ECHO %sourcefile%
REM do other stuff with the %sourcefile% variable after this
ENDLOCAL
If all you want is the name and timestamp of the newest PPTX file, your script is a lot more complicated than it needs to be. Simply scrape dir "path\*.pptx" /b /o:-d then break out of the for /f loop after the first line.
#ECHO off
setlocal
SET "pathsource=K:"
REM Get the newest pptx file in the specified directory. Get the filename and last modified timestamp
pushd "%pathsource%"
FOR /f "delims=" %%a IN ('dir "*.pptx" /b /o:-d') DO (
set "sourcefile=%%~nxa"
set "timestamp=%%~ta"
goto break
)
:break
ECHO %sourcefile%
echo %timestamp%
REM do other stuff with the %sourcefile% variable after this

Batchfile: read last lines from logfiles and copy them to a new file

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
)
)

Change letters alphabetically in a for loop to print all tokens

I have a file (MyFile.txt) with a string like this, for example:
var1,var2,asds,123,var6
This file is generated automatically, and I don't know how many words it will have.
I make a for to print all words, one in each line. And process them one by one
for /f "tokens=1-30 delims=," %%a in (MyFile.txt) do call Process.bat %%a
In Process.bat I have ping %1 for example.
This only processes the first token. My problem is, I need to process all tokens, but I need to do it automatically, is there a way to loop over all existing tokens?
In my example: var1,var2,asds,123,var6 it would be: do call Process.bat %%a, then %%b, then %%c, then %%d and %%e.
Thank you.
This will work even if you have various line in Myfile.txt
#echo off&cls
for /f "delims=" %%a in (MyFile.txt) do for %%b in (%%a) do call Process.bat %%b
What about this:
set /p var=<MyFile.txt
set var="%var%"
for %%a in ("%var:,=" "%) do call Process.bat %%a
That Should work.
Mona
you can combine for to read all your lines and call to parse the vars in the line
try this to get you started
#echo off
setlocal enabledelayedexpansion
for /f %%a in (file.txt) do (
set line=%%a
set line=!line:,= !
call :parms !line!
)
goto :eof
:parms
if "%1"=="" goto :eof
echo ping %1
shift
goto :parms
goto :eof

Resources