I have this script within a .bat file and would like to only keep the most recent 100 lines, at the end of the file.
How do I delete all other rows?
#echo off
REM *DATETIME2.BAT
REM *Copy date and time to create a new log file
echo.|date>>"\\spserver\myfolder\dt.tmp"
echo.|time>>"\\spserver\myfolder\dt.tmp"
IF NOT EXIST "\\spserver\myfolder\dt.tmp" GOTO Error1
type "\\spserver\myfolder\dt.tmp"|FIND "current">"\\spserver\myfolder\dt.log"
rem del "\\spserver\myfolder\dt.tmp"
GOTO End
:Error1
Echo.
Echo There was an error processing the command.
Echo Unable to find temporary sort file DATETIME2.TMP.
Echo.
GOTO End
:End
Here you go:
Call :Tail 100 dt.log tempout.txt
move /y tempout.txt dt.log>nul
exit /b
:Tail <numlines> <FileIn> <FileOut>
FOR /F "usebackq tokens=3,3 delims= " %%l IN (
`find /c /v "" %2`) DO (call SET find_lc=%%l)
SET /a linenumber=%find_lc%-%1
IF %linenumber% LSS 1 (
more +0 %2 >> %3
) ELSE (
more +%linenumber% %2 >> %3
)
Related
I am able to get list of all the file names, path size etc using the below code but I have not been able to get rid of double quotes in the file name and file path. Also i have spaces in the file name that breaks when the file name is listed.
#echo off
setlocal enabledelayedexpansion
if exist Z:\ (net use Z: /delete)
net use Z: "\\my drive path"
SET Path1=Z:\
pushd
SET "Path2=Z:\folder1"
for /f "skip=1 tokens=1,2,3,4,5* delims=,] " %%i in ('forfiles /p "%Path2%" /s /m ""*.mmp"" /c "cmd /c echo #fsize 0x22#file0x22 #path #fdate #ftime"') do (
set FILE_SIZE_WINDOWS=%%i
set FILE_NAME=%%~j
set FILE_PATH=%%~k
set FILE_DATE=%%l
set FILE_TIME=%%m
set "unit=B"
for %%b in (KB MB GB TB PB EB ZB YB) do if 1024 lss !FILE_SIZE_WINDOWS! (
set "unit=%%b"
if !FILE_SIZE_WINDOWS! lss 2147483647 (set /a "FILE_SIZE_WINDOWS=FILE_SIZE_WINDOWS/1024") else (set "FILE_SIZE_WINDOWS=!FILE_SIZE_WINDOWS:~0,-3!"))
if "!FILE_NAME!" neq "!FILE_NAME:Admin=!" (set Developer=Admin
echo !Developer!)
echo !FILE_NAME! !FILE_SIZE_WINDOWS!!unit! !FILE_PATH! !FILE_DATE! !FILE_TIME! !Developer!
if "!FILE_NAME!" == "!FILE_NAME:Undo=!" (echo !FILE_NAME! does not contain Undo
echo !FILE_NAME! !FILE_SIZE_WINDOWS!!unit! !FILE_PATH! !FILE_DATE! !FILE_TIME! !Developer! >> List.txt
) else (
echo !FILE_NAME! !FILE_SIZE_WINDOWS!!unit! !FILE_PATH! !FILE_DATE! !FILE_TIME! !Developer! >> UndoList2.txt
)
)
echo Done
popd
endlocal
Expected output:
Filename.mmp C:\Filepath
I am new to using batch files so could someone please help me split a string i am getting from a file.
I am using %USERPROFILE% to get my string.
The string is: "C:\Users\nicholas"
I would like to keep the C:\ part, but get rid of the \Users\nicholas part.
for /f "tokens=2 delims=\" %A in ('set userprofile') do echo %A\
See for /?
Also
echo %userprofile:~0,3%
If you carefully read the output of set /?, you'll find the answer:
May also specify substrings for an expansion.
%PATH:~10,5%
would expand the PATH environment variable, and then use only the 5
characters that begin at the 11th (offset 10) character of the expanded
result.
So, you can use something like this to get the first 3 characters of your string:
> echo %userprofile:~0,3%
C:\
I As you need the drive where where the users are located you can use directly
%systemdrive% variable - this is the drive where the windows is installed
II the easiest way to get a drive from path:
for %%a in ("%userprofile%") do echo %%~da\
%~da - expands a path to its drive only
III over-complicated but powerful way (split function that can be used for a different things):
#echo off
call :split "%userprofile%" "\" 1 drive
echo %drive%\
goto :eof
:split [%1 - string to be splitted;%2 - split by;%3 - possition to get; %4 - if defined will store the result in variable with same name]
::http://ss64.org/viewtopic.php?id=1687
setlocal EnableDelayedExpansion
set "string=%~2%~1"
set "splitter=%~2"
set /a position=%~3
set LF=^
rem ** Two empty lines are required
echo off
for %%L in ("!LF!") DO (
for /f "delims=" %%R in ("!splitter!") do (
set "var=!string:%%~R%%~R=%%~L!"
set "var=!var:%%~R=%%~L!"
if "!var!" EQU "!string!" (
echo "%~1" does not contain "!splitter!" >&2
exit /B 1
)
)
)
if "!var!" equ "" (
endlocal & if "%~4" NEQ "" ( set "%~4=")
)
if !position! LEQ 0 ( set "_skip=" ) else (set "_skip=skip=%position%")
for /f "eol= %_skip% delims=" %%P in (""!var!"") DO (
if "%%~P" neq "" (
set "part=%%~P"
goto :end_for
)
)
set "part="
:end_for
if not defined part (
endlocal
echo Index Out Of Bound >&2
exit /B 2
)
endlocal & if "%~4" NEQ "" (set %~4=%part%) else echo %part%
exit /b 0
set File1 = C:\filepath
set File2 =C:\filepath
FOR %i IN (%FILE1%) DO SET DATE1=%%~ti\
FOR %i IN (%FILE2%) DO SET DATE2=%%~ti
IF "%DATE1%" GTR "%DATE2%" ECHO Files have same age && GOTO END
FOR %i IN ('DIR /B /O:D "%FILE1%" "%FILE2%"') DO SET NEWEST=%%i
ECHO Newer file is "%NEWEST%"
try this:
#echo off
rem --compares the age of two files
rem --by Vasil "npocmaka" Arnaudov
call :isOlder "C:\test.file1" "C:\test.file2"
goto :eof
:isOlder [%1 path to first file ; %2 path to second file]
setlocal
call :get_file_c_time "%~1" time1
call :get_file_c_time "%~2" time2
if "%time1%" LEQ "%time2%" (
echo YES
) else (
echo NO
)
goto :eof
:get_file_c_time [ %1 path to file; %2 variable to assign value ]
set file_path=%~1
if not exist "%file_path%" echo file %1 does not exist&& exit /b 1
if "%~2" equ "" echo need a secont parameter && exit /b 2
setlocal enableDelayedExpansion
for /f "skip=5 tokens=1,2,3,4,5,6 delims=/:.гчЈз " %%T in ('dir /tc "%file_path%"') do (
if "%%Y" EQU "PM" (
set /a "hour=%%W+12"
) else (
set hour=%%W
)
set ftime=%%V%%U%%T!hour!%%Y
goto :endfor
)
:endfor
endlocal & set "%~2=%ftime%"
goto :eof
It depends on TIME settings but in the most of the cases should work..
Another approach is with WMIC/WMI but I will need some time to create script
In a batch file I get a folder with a list of subfolders with an unknown number of underscores e.g.:
a_b_10
c_d_e_2
f_17
I need to remove the last token of the names i.e.
a_b
c_d_e
f
Thanks
You could try to get the last part with an underscore and then remove this from your string, but this only works when the last part is unique.
In your sample the last part seems to be always a number in spite of the other parts.
This uses the trick to parse parts of a string by replace the delimiter by a linefeed character.
#echo off
setlocal EnableDelayedExpansion
set LF=^
for /F "delims=" %%X in ('dir /b /AD') do (
call :removeLastPart "%%~X"
)
exit /b
:removeLastPart
set "str=%~1"
for %%L in ("!LF!") DO (
for /F "delims=" %%P in ("!str:_=%%~L!") do set "last=%%P"
)
echo The last part is '!last!'
REM ** now remove the last part by replacing with nothing **
set "str=!str:_%last%=!"
echo !str!
exit /b
#echo off
set "root_dir=C:\scriptests"
setlocal enableDelayedExpansion
for /f %%d in ('dir /b /a:d *_*') do (
call :lastindexof "%%d" _ lio
set "f_name=%%~d"
echo renaming !f_name!
for %%S in (!lio!) do ren !f_name! !f_name:~0,%%S!
)
endlocal
exit /b 0
:lastindexof [%1 - string ; %2 - find last index of ; %3 - if defined will store the result in variable with same name]
#echo off
setlocal disableDelayedExpansion
set "str=%~1"
set "splitter=%~2"
set LF=^
rem ** Two empty lines are required
setlocal enableDelayedExpansion
for %%L in ("!LF!") DO (
for /f "delims=" %%R in ("!splitter!") do (
set "var=!str:%%R=%%L!"
)
)
for /f delims^=^" %%P in ("!var!") DO (
set "last_part=%%~P"
)
if "!last_part!" equ "" if "%~3" NEQ "" (
echo "not contained" >2
endlocal
set %~3=-1
exit
) else (
echo "not contained" >2
endlocal
echo -1
)
call :strlen0.3 str strlen
call :strlen0.3 last_part plen
call :strlen0.3 splitter slen
set /a lio=strlen-plen-slen
endlocal & set lio=%lio%
endlocal & if "%~3" NEQ "" (set %~3=%lio%) else echo %lio%
exit /b 0
:strlen0.3 StrVar [RtnVar]
setlocal EnableDelayedExpansion
set "s=#!%~1!"
set "len=0"
for %%A in (2187 729 243 81 27 9 3 1) do (
set /A mod=2*%%A
for %%Z in (!mod!) do (
if "!s:~%%Z,1!" neq "" (
set /a "len+=%%Z"
set "s=!s:~%%Z!"
) else (
if "!s:~%%A,1!" neq "" (
set /a "len+=%%A"
set "s=!s:~%%A!"
)
)
)
)
endlocal & if "%~2" neq "" (set %~2=%len%) else echo **%len%**
exit /b
This solution will create the "renfolders.bat.txt" file for you to check in notepad, and run it as a batch file if you are happy with it.
This uses a helper batch file called repl.bat - download from: https://www.dropbox.com/s/qidqwztmetbvklt/repl.bat
Place repl.bat in the same folder as the batch file or in a folder that is on the path.
dir *_* /b /s /ad |repl ".*\\(.*)_.*" "ren \q$&\q \q$1\q" xa >"renfolders.bat.txt"
I just figured out this solution:
for /f %%f in ('dir /b /AD c:\MainFolder\') do (
set var=%%f
set var=!var:_= !
set /a count=0
for %%i in (!var!) do (set /a count+=1)
set /a count2=0
for %%i in (!var!) do (
set /a count2+=1
if /I !count2! equ 1 (set var2=%%i) else if not !count2! equ !count! (set var2=!var2!_%%i)
)
echo !var2!
)
Here is a novel approach
For each folder containing at least one _ in name:
create a temporary empty file with the same name
rename the temporary file, stripping off everything after the final _
read the new name into a variable
strip off the final _ from the name
For an explanation of how the rename works, see How does the Windows RENAME command interpret wildcards?
#echo off
setlocal enableDelayedExpansion
set "loc=%temp%\removeToken"
md "%loc%"
for /d %%F in (*_*) do (
copy nul "%loc%\%%F" >nul
ren "%loc%\*" "*_"
for %%A in ("%loc%\*") do set "new=%%~nxA"
del /q "%loc%\*"
echo old=%%F
echo new=!new:~0,-1!
echo(
)
rd "%loc%"
EDITED - Wrong copy of the code posted
It separates the elements of the directory name and iterates over them discarting the last element
#echo off
setlocal enabledelayedexpansion
for /d %%a in (*_*) do (
set "old=%%~na" & set "new=" & set "previous="
for %%b in ("!old:_=" "!") do (
if not defined previous (
set "previous=%%~b"
) else if not defined new (
set "new=!previous!"
) else set "new=!new!_!previous!"
set "previous=%%~b"
)
echo ren "%%~fa" "!new!"
)
endlocal
In Linux (Bash) there is a very useful functionality for dumping literal text out to another file like this:
cat > see.txt << EOF
contents going into
my file
EOF
What I need is the equivalent for a Windows batch script. I haven't found this kind of functionality built-in, but I was thinking I could write a subroutine to do it (I don't want to rely on anything that isn't natively in Windows since XP), but I'm having trouble. Here's what I have so far with help from various sources:
call:catMyChunk myCustomText c:\see.txt
exit /b
goto:myCustomText
This is my test file
Hope you like it.
<got these>
% and these %
! these too
yeah
:myCustomText
:catMyChunk
::Should call this function with 2 args, MYDELIM and outFile.txt
::where is to be catted to outFile.txt
::and text starts with <beginning of line>goto:MYDELIM
::and ends with <beginning of line>:MYDELIM
set searchStart=goto:%~1
set searchStop=:%~1
set outFile=%~2
set startLine=0
set endLine=0
for /f "delims=:" %%a in ('findstr -b -n !searchStart! %~dpnx0') do set "startLine=%%a"
for /f "delims=:" %%a in ('findstr -b -n !searchStop! %~dpnx0') do set "endLine=%%a"
set /a linesLeftToRead=%endLine% - %startLine%
del %outFile%
if "%linesLeftToRead%" LEQ "0" (
echo Error finding start and end delmieters for %searchStop% in catMyChunk routine
exit /B 1
)
setlocal DisableDelayedExpansion
for /f "usebackq skip=%startLine% delims=" %%a in (`"findstr /n ^^ %~dpnx0"`) do (
set "oneLine=%%a"
setlocal EnableDelayedExpansion
set "oneLine=!oneLine:*:=!"
set /a linesLeftToRead-=1
if !linesLeftToRead! LEQ 0 exit /B
echo(!oneLine!>>%outFile%
)
goto: EOF
So my requirement is that the chunk of text is output literally without any changes whatsoever (i.e. I don't want to have to escape blank lines, %, !, <, etc.). This code is doing almost everything I need, except I haven't found a way to get the exclamation points output properly. Here is the output I get, which isn't quite right:
This is my test file
Hope you like it.
<got these>
% and these %
these too
yeah
Edit:
For anyone wanting the modified version of the subroutine that now works, here it is:
:catMyChunk
::Should call this function with 2 args, MYDELIM and outFile.txt
::where is to be catted to outFile.txt
::and text starts with <beginning of line>goto:MYDELIM
::and ends with <beginning of line>:MYDELIM
set searchStart=goto:%~1
set searchStop=:%~1
set outFile=%~2
set startLine=0
set endLine=0
for /f "delims=:" %%a in ('findstr -b -n !searchStart! %~dpnx0') do set "startLine=%%a"
for /f "delims=:" %%a in ('findstr -b -n !searchStop! %~dpnx0') do set "endLine=%%a"
set /a linesLeftToRead=%endLine% - %startLine%
del %outFile%
if "%linesLeftToRead%" LEQ "0" (
echo Error finding start and end delmieters for %searchStop% in catMyChunk routine
exit /B 1
)
setlocal DisableDelayedExpansion
for /f "usebackq skip=%startLine% delims=" %%a in (`"findstr /n ^^ %~dpnx0"`) do (
set "oneLine=%%a"
set /a linesLeftToRead-=1
setlocal EnableDelayedExpansion
set "oneLine=!oneLine:*:=!"
if !linesLeftToRead! LEQ 0 exit /B
echo(!oneLine!>>%outFile%
endlocal
)
endlocal
goto: EOF
Your code is missing a single endlocal in your FOR-loop.
You will then create for each loop a new local-context through the setlocal EnableDelayedExpansion, this will explode with some more lines in your text file.
Also, to preserve the exclamation marks (and also the carets) you need the toggling technique:
DOS batch files: How to read a file?
setlocal DisableDelayedExpansion
for /f "usebackq skip=%startLine% delims=" %%a in (`"findstr /n ^^ %~dpnx0"`) do (
set "oneLine=%%a"
setlocal EnableDelayedExpansion
set "oneLine=!oneLine:*:=!"
set /a linesLeftToRead-=1
if !linesLeftToRead! LEQ 0 exit /B
echo(!oneLine!>>%outFile%
endlocal
)
+1, Interesting application! I modified your code for a simpler and faster version:
#echo off
call :catMyChunk myCustomText see.txt
exit /b
goto:myCustomText
This is my test file
Hope you like it.
<got these>
% and these %
! these too
yeah
:myCustomText
:catMyChunk
::Should call this function with 2 args, MYDELIM and outFile.txt
::where is to be catted to outFile.txt
::and text starts with <beginning of line>goto:MYDELIM
::and ends with <beginning of line>:MYDELIM
setlocal DisableDelayedExpansion
set searchStart=goto:%~1
set searchStop=:%~1
set outFile=%~2
if exist %outFile% del %outFile%
set copyFlag=No
echo No> copyFlag
for /f "delims=" %%a in (%~f0) do (
set "oneLine=%%a"
setlocal EnableDelayedExpansion
if !copyFlag! == No (
if !oneLine! == %searchStart% echo Yes> copyFlag
) else (
if !oneLine! == %searchStop% (
echo No> copyFlag
) else (
echo/!oneLine!>> %outFile%
)
)
endlocal
set /p copyFlag=< copyFlag
)
endlocal
goto :eof
I also created another version that looks more like the Linux version, that is, the lines to copy are placed directly after invoking the routine, and the execution continue after the last copied line. Of course, to make this possible the routine is not invoked via call, but entered with a goto, and when the routine ends it execute a goto %MYDELIM% instead of a "return" (exit /b or goto :eof). Also, because a goto can not have parameters, the "parameters" are defined in variables before the invocation.
#echo off
set searchStop=EndOfMyText
set outFile=see.txt
goto :catMyChunk EndOfMyText
This is my test file
Hope you like it.
<got these>
% and these %
! these too
yeah
:EndOfMyText
exit /b
:catMyChunk
::Before JUMP to this "function" define 2 vars: searchStop and outFile
::where is to be catted to %outFile%
::and text starts with goto :catMyChunk %searchStop%
::and ends with :%searchStop%
setlocal DisableDelayedExpansion
set searchStart=goto :catMyChunk %searchStop%
if exist %outFile% del %outFile%
set copyFlag=No
echo No> copyFlag
for /f "delims=" %%a in (%~f0) do (
set "oneLine=%%a"
setlocal EnableDelayedExpansion
if !copyFlag! == No (
if /I !oneLine! == !searchStart! echo Yes> copyFlag
) else (
if !oneLine! == :%searchStop% (
echo No> copyFlag
) else (
echo/!oneLine!>> %outFile%
)
)
endlocal
set /p copyFlag=< copyFlag
)
endlocal
goto %searchStop%
EDIT
This new version is even faster and now works with all special cases, including empty lines:
:catMyChunk
::Should call this function with 2 args, MYDELIM and outFile.txt
::where is to be catted to outFile.txt
::and text starts with <beginning of line>goto:MYDELIM
::and ends with <beginning of line>:MYDELIM
setlocal EnableDelayedExpansion
set searchStart=goto:%~1
set searchStop=:%~1
set outFile=%~2
if exist %outFile% del %outFile%
findstr /n ^^ "%~f0" > pipeline.txt
call :seekMyChunk < pipeline.txt
del pipeline.txt
exit /B
:seekMyChunk
set oneLine=:EOF
set /P oneLine=
if !oneLine! == :EOF goto startNotFound
set oneLine=!oneLine:*:=!
if not !oneLine! == %searchStart% goto seekMyChunk
:catNextLine
set oneLine=:EOF
set /P oneLine=
if !oneLine! == :EOF goto stopNotFound
set oneLine=!oneLine:*:=!
if !oneLine! == %searchStop% goto :eof
echo/!oneLine!>> %outFile%
goto catNextLine
:startNotFound
echo Error finding start delimiter for %searchStart% in catMyChunk
goto :eof
:stopNotFound
echo Error finding stop delimiter for %searchStop% in catMyChunk
goto :eof