Choose Highest Numbered File - Batch File - windows

I have a directory full of .jar files, named progressively like so:
version-1.jar
version-2.jar
version-3.jar
I am trying to select the highest numbered file. Is there any really simple way to do it?
Because doing .\version*.jar causes an error, presumably due to the multiple files?

We need delayed expansion
setlocal enabledelayedexpansion
Just a variable for the maximum:
set max=0
Then iterate over the files:
for %%x in (version-*.jar) do (
We need the file name without extension
set "FN=%%~nx"
And remove the version- from the start:
set "FN=!FN:version-=!"
Now FN should contain just the number and we can compare:
if !FN! GTR !max! set max=!FN!
)
And we're done:
echo highest version: version-%max%.jar
The complete batch file:
#echo off
setlocal enabledelayedexpansion
set max=0
for %%x in (version-*.jar) do (
set "FN=%%~nx"
set "FN=!FN:version-=!"
if !FN! GTR !max! set max=!FN!
)
echo highest version: version-%max%.jar

Here is a slightly simpler version than Joey's code.
#echo off
setlocal enableDelayedExpansion
set max=0
for /f "tokens=1* delims=-.0" %%A in ('dir /b /a-d version-*.jar') do if %%B gtr !max! set max=%%B
echo higest version: version-%max%.jar
This code will work even if the version numbers are zero prefixed as long as the version number is never 0 (zero). Specifying tokens=1* with 0 included as a delimiter causes leading zeros to be stripped from the version number while preserving all zeros after the first non-zero digit.
There is a simpler solution if all versions are zero prefixed to a constant width. But this solution works both with and without zero prefixing.
Joey's code will fail if leading zeros are present because that indicates octal notation. Invalid octal digits with leading zeros will be treated as strings causing the comparison to give the wrong result. This is probably not an issue since the original question implies leading zeros are not present. But better to be safe than sorry.

Related

Batch: Return filename with highest integer

If I have a directory full of text files such as
01.text.sql
02text.sql
3text.sql
how would I return the file name with the highest integer e.g. 3.text.sql? As you can see the numbers might not be prefixed with 0 and might be missing a . after the integer. I know I have to loop through the directory with something like this
Choose Highest Numbered File - Batch File
however, this does not take into account different file name formats. Is there any way a batch script can loop through a directory and automatically pull the file with the highest integer or do I have to store the file names and compare them with each other in a separate loop?
Currently I have something like this but it returns 3text instead of just 3
SETLOCAL enabledelayedexpansion
SET max=0
FOR %%x in (*.sql) DO (
SET "FN=%%~nx"
SET "FN=!FN:*-=!"
IF !FN! GTR !max! SET max=!FN!
)
ECHO Highest script number is %max%
Updated loop:
SETLOCAL enabledelayedexpansion
SET scriptmax=0
FOR %%x in (*.sql) DO (
SET "FN=%%~nx"
SET a=1000!FN!
SET /A FN=a %% 1000
IF !FN! GTR !max! SET max=!FN!
)
set /A a=b will interpret everything in b up to the first non numeric character as a number, so
set FN=03text.txt
set /A a=FN
will set %a% nicely to 3. But we have an additional problem here: Numbers prefixed with a 0 will be interpreted as octal, so 08 and 09 are illegal. To fix this:
set FN=09text.txt
set t=1!FN!
set /A a=t-100
will yield 9.
However, this will only work if you know how many digits to expect, maybe you have to check for a leading zero first (if "%FN:~0,1%" == "0" ...), and/or even chop off leading zeroes (set FN=%FN:~1%)
Edit: a better way:
set FN=09text.txt
set a=1000!FN!
set /A FN=a %% 1000
will work for anything up to 999.

Get a substring from a Windows batch variable

In a batch file I have a variable containing an IP. Just for example:
SET ip = 170.150.120.10
I would like to define another variable, replacing the last octet with 1, which in the example would be 170.150.120.1.
The IP may change (it is defined dynamically), varying in its length, so capturing a fixed length substring and concatenating it would not work anytime (in the example it would be SET baseIP = %ip:~0,9%.1).
How can this task be solved?
Is there some RegEx support in Windows command line?
maytham-ɯɐɥıλɐɯ has the key component of a simple solution - FOR /F. But that solution has a lot of complication that seems unrelated to the question.
The answer can be as simple as:
#echo off
set "ip=170.150.120.10"
for /f "tokens=1-3 delims=." %%A in ("%ip%") do set "new_ip=%%A.%%B.%%C.1"
echo new ip=%new_ip%
Note - You included spaces before and after the = in the SET statement in your question. That is a bad idea, as all of the spaces are significant. You have a variable name that ends with a space, and a value that begins with a space. I removed the unwanted spaces from the answer
Also, I enclosed the assignment within quotes. All characters after the last quote are ignored as long as the first quote is before the variable name. This protects against inadvertent trailing spaces in your value.
EDIT 2017-09-04
Even simpler method - treat the address as a filename, so the last node becomes the extension. Use a simple FOR and the ~n modifier to get the base name (1st 3 nodes), and then add your own extension (last node).
for %%A in (%ip%) do set "new_ip=%%~nA.1"
(Edit: added missing jump)
Here's my take. Iterates over the last four characters, looks if it is a dot, and appends the desired octet to the corresponding prefix part of the given IP. This works with any size (length) of last octet, e.g. 1.1.1.5 and 10.0.0.155
#ECHO OFF
SETLOCAL EnableDelayedExpansion
SET ip=170.150.120.10
SET new_ip_last_octet=1
ECHO Input was %ip%
FOR /L %%G IN (0,-1,-4) DO (
SET tmp=!ip:~%%G!
IF "!tmp:~0,1!" == "." (
SET new_ip=!ip:~0,%%G!.!new_ip_last_octet!
GOTO done
)
)
:done
ECHO New IP is %new_ip%
Output:
Input was 170.150.120.10
New IP is 170.150.120.1
Try this
#echo off
set ipCurrent=170.150.120.100
set ipOffsets=0.100.0.-24
#echo off
for /f "tokens=1-3 delims=. " %%a in ("%ipCurrent%") do (
set part1=%%a
set part2=%%b
set part3=%%c
)
for /f "tokens=1-3 delims=." %%a in ("%ipOffsets%") do (
set /a part1+=%%a
set /a part2+=%%b
set /a part3+=%%c
)
set ipBase= %part1%.%part2%.%part3%.1
#echo %ipCurrent% is changed to%ipBase%
EDIT
Thanks to #dbenham for input, the code above can be reduced to:
#echo off
set "ipCurrent=170.150.120.100"
#echo off
for /f "tokens=1-3 delims=. " %%a in ("%ipCurrent%") do set "ipBase=%%a.%%b.%%c.1"
#echo %ipCurrent% is changed to %ipBase%
Input any ip address range
input 170.150.120.10
or 170.150.120.110
Output 170.150.120.1
Batch resources: Link

Batch adding a character every x characters

If I get my parameter with %1 and it is "Server" how can I add a + sign after every letter?
So my result would be "S+e+r+v+e+r"?
I think Batch file to add characters to beginning and end of each line in txt file this is a similar question but I don't know how to change the code for this purpose.
Any help would be great!
I'm pretty sure this has been asked and answered before, but I couldn't find it.
There is a really cool (and fast) solution that I saw posted somewhere. It uses a new cmd.exe process with the /U option so output is in unicode. The interesting thing about the unicode is that each ASCII character is represented as itself followed by a nul byte (0x00). When this is piped to MORE, it converts the nul bytes into newlines!. Then a FOR /F is used to iterate each of the characters and build the desired string. A final substring operation is used to remove the extra + from the front.
I tweaked my memory of the code a bit, playing games with escape sequences in order to get the delayed expansion to occur at the correct time, and to protect the character when it is appended - all to get the technique to preserve ^ and ! characters. This may be a new twist to existing posted codes using this general technique.
#echo off
setlocal enableDelayedExpansion
set "str=Server bang^! caret^^"
set "out="
for /f delims^=^ eol^= %%A in ('cmd /u /v:on /c echo(^^!str^^!^|more') do set "out=!out!+^%%A"
set "out=!out:~1!"
echo Before: !str!
echo After: !out!
--OUTPUT---
Before: Server bang! caret^
After: S+e+r+v+e+r+ +b+a+n+g+!+ +c+a+r+e+t+^
This batch file should do it:
#ECHO OFF
SETLOCAL EnableDelayedExpansion
SET Text=%~1
SET Return=
REM Batch files don't have a LEN function.
REM So this loop will process up to 100 chars by doing a substring on each.
FOR /L %%I IN (0,1,100) DO (
CALL SET Letter=!Text:~%%I,1!
REM Only process when a letter is returned.
IF NOT "!Letter!" == "" (
SET Return=!Return!+!Letter!
) ELSE (
REM Otherwise, we have reached the end.
GOTO DoneProcessing
)
)
:DoneProcessing
REM Remove leading char.
SET Return=%Return:~1,999%
ECHO %Return%
ENDLOCAL
Calling with Test.bat Server prints S+e+r+v+e+r to the console.

Batch ECHO %varname% just saying "Echo is on."

So I was tasked with making a batch file that does a few specific things. I've never worked with batch before, and I'm finding it hard to find tutorials on what exactly I need. (I've done basic tutorials)
I'm trying to get the most currently edited file from a directory. The only thing I've came up with (and I've noticed other people said to do) is a for loop of files in the directory sorted by date and then just get the first file and break the loop.
Some problems:
1) My loop never breaks
2) My ECHO %variable% doesn't work at the end.
#echo off
SET count=0
FOR /f %%i in ('DIR Y:\ /B /O:-D') DO (
IF count==0 (
SET NewestFile=%%i
SET count=1
)
)
#echo on
ECHO %NewestFile%
When I run this, I get:
C:\>testing.bat
C:\>ECHO
ECHO is on.
I am 100% new to Batch. Maybe I'm doing something that this is really picky about? (Other StackOverflow questions have been solved by people just adding aa space or stuff like that)
Your condition is never met because the string count is never equal to the string 0. You need
if !count!==0 (
set NewestFile=%%i
set count=1
)
But then you also need delayed expansion (at the beginning of your batch file):
setlocal enabledelayedexpansion
The problem here is that you need to tell the batch file that there is a variable. Like foo in Perl won't magically resolve to the contents of the $foo variable count in your batch file isn't equivalent to %count% (the variable contents) or !count! (the same, but with delayed expansion).
Delayed expansion is necessary because the whole for loop is parsed at once. And cmd replaces normal (%foo%) variables with their contents during parsing so that during execution only the values remain. So once execution reaches the if there would be the condition 0==0 because that's what count's value was before the loop. Delayed expansion (using the !foo! syntax) expands the variables immediately prior to execution, so this does not happen.
For more help on delayed expansion you can read help set.
Another way would be to just use absence or presence of the count variable:
SET count=
FOR /f %%i in ('DIR Y:\ /B /O:-D') DO (
IF not defined count (
SET NewestFile=%%i
SET count=1
)
)
This works around the problem above because there is no variable to replace during parsing. All we're doing is a run-time check whether the variable count exists.
If you supplied accurate code then you want to get the first line - and this is one way to do that.
#echo off
FOR /f %%i in ('DIR Y:\ /B /O:-D') DO SET "NewestFile=%%i" & goto :done
:done
ECHO %NewestFile%
If you change the dir command to list the files in ascending order instead of descending order, you can use this one-liner which doesn't need any of the common bizarre cmd.exe scripting hacks. It just keeps the last line of output in the NewestFile variable (I guess it might qualify as a cmd.exe scripting hack, but I don't think it qualifies as bizarre):
for /f %%i in ('DIR Y:\ /B /O:D') do set NewestFile=%%i

Windows bat file equivelent of bash string manipulation

How would I achieve this:
for i in *.e; do mv $i ${i%-b*.e}.e; done
in a Windows batch file? (It renames files containing "-b" to the part before "-b". Note that this is not necessarily the end of the string! e.g. "file-b-4.e" will become "file.e")
If you really want to do this in batch, this should work
#echo off
setlocal disableDelayedExpansion
for %%F in (*.e) do (
set "var=%%~F"
setlocal enableDelayedExpansion
set "var=!var:-b=.e:!"
for /f "eol=: delims=:" %%A in ("!var!") do (
endlocal
echo ren "%%F" "%%A"
)
)
Edit
The comment by panda-34 alluded to the fact that the original posted code failed if the file name begins with -b. The code above was fixed by incorporating the extension into the replacement string. (thanks panda-34 for alerting me to the problem)
panda-34 also provided an alternate solution that uses command injection with search and replace. The injected command is the REM statement.
The panda-34 solution works as long as the file name does not contain & or ^ characters, but fails if it does.
Below is a modified version of the command injection technique that should work with all valid Windows file names. There are 2 critical mods, 1) make sure the special chars in the file name are always quoted, and 2) do not pass the value as a CALL argument, otherwise ^ will be doubled to ^^.
#echo off
setlocal disableDelayedExpansion
for %%i in (*-b*.e) do (
set old="%%~ni"
call :ren_b
)
exit /b
:ren_b
set v=%old:-b=.e"&rem "%
echo ren "%old:~1,-1%.e" %v%
exit /b
Final Edit (I hope):
As baruch indicates in his comment, the solutions above remove starting with the 1st occurance, whereas the original bash command removes starting with the last occurance.
Below is a version that should be an exact equivalent of the original bash command.
#echo off
setlocal disableDelayedExpansion
set "search=-b"
for %%A in (*%search%*.e) do (
set "old=%%A"
setlocal enableDelayedExpansion
set "new=\_!old:%search%=\_!"
for %%B in ("!new!") do (
endlocal
set "new=%%~pB"
setlocal enableDelayedExpansion
set "new=!new:~2,-1!.e"
echo ren "!old!" "!new:\_=%search%!"
endlocal
)
)
Simple, really
for %%i in (*-b*.e) do call :ren_b %%~ni
goto :eof
:ren_b
set v=%*
set v="%v:-b=.e" ^& rem %
ren "%*.e" %v%
Here's a variant to keep the name till the last -b occurence
setlocal enabledelayedexpansion
for %%i in (*-b*.e) do (
set v=%%~ni
set v=!v:-b=\!
for %%j in ("\!v!") do (
set v=%%~pj
set v=!v:~1,-1!
set v=!v:\=-b!
ren "%%i" "!v!.e"
)
)
It will fail for names containing ! and starting with -b.
P.S, Didn't see, dbenham already provided the equivalent solution, probably with more provisions for terminal cases of file names.
Forget it, some convenient things cannot be done in NT scripting. What you are asking here is not possible to my knowledge. And I've written and maintained complex NT scripts bigger than 50 KiB, using all kinds of tricks. The book "Windows NT Shell Scripting" points out many of these, for the same and more see Rob van der Woude's scripting pages.
I reckon you could do part of this, but certainly not in a one-liner due to how variable expansion works in NT scripting. For example you could extract the part of the string that you expect to be -b and check whether it is -b, then extract the other parts and rename from the original name to the one that is comprised of only the extracted parts.
But you'll likely need ten to fifteen lines to achieve that. In that light, consider using a different scripting language for the purpose. Especially if this is a modern Windows version.
I realize this is not the desired answer (i.e. that this is possible and a sample), but cmd.exe is very limited compared to Bash, albeit by far not as limited as some opponents of traditional batch scripting are pointing out.

Resources