How can I restructure file names on bulk via batch script - windows

I have 324 files on a Windows 10 machine which are named in the following pattern:
[7 Numbers] [Space] [Last name] [Space] [First name]
And I need them to be:
[Last name] [Space] [First name] [Space] [7 Numbers]
I have done some quick research and found that I could write a batch script utilizing the 'rename' function:
#echo off
rename “y:\*.txt” “???-Test1.*”
However, I was unable to find out how I can program the script to take the first 7 chracters and put them to the end.
Any help is appreciated.

Given the little detail on the exact formatting of your structures, i.e what happens in the event a surname has a split like van Halen which also now contains a space:
anyway, this will cater for the situation as you've mentioned only and not for names/surnames containing spaces.
#echo off
for /f "tokens=1-3*" %%i in ('dir /b /a-d *.txt ^| findstr /R "^[1-9]"') do echo ren "%%i %%j %%k" "%%~nk %%~nj %%~ni%%~xk"
Note this example will simply echo the command and not perform the actual rename. You need to test it first before you do the renaming. Once you are happy with the result printed to console, then remove echo from the line.
Note. findstr is important here as we need to only perform the action if the file starts with numbers. You can define the findstr filter even more if you want to be more specific. Here I just focused on numbers in the beginning of any .txt file considering no name or surname should start with a number., Unless you're 50cent or some other random rapper.

#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
rem The following setting for the source directoryis a name
rem that I use for testing and deliberately includes spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files\t w o"
FOR %%b IN ("%sourcedir%\??????? * *.txt") DO FOR /f "tokens=1*delims= " %%u IN ("%%~nb") DO SET /a junk=0&SET /a "junk=1%%u" 2>nul&IF !junk! geq 10000000 IF !junk! leq 19999999 ECHO REN "%%b" "%%v %%u.txt"
GOTO :EOF
Please note that this routine uses delayedexpansion
The first for puts the absolute filename of each file matching the mask into %%b
The second partitions the name part of the filename (%%~nb) into that part before the first space (token 1) to %%u and the remainder (token *) to %%v
junk is then set to 0 and then reset to the value of 1%%u. set /a will leave junk unchanged (therefore, 0) if %%u is not a numeric string (the 2>nul suppresses the error message) so if %%u is numeric, junk will be set to 10000000 ... 19999999.
Use !junk! to access the run-time value of junk, check it is within range and if so, echo the ren required.
Remove the echo keyword before the ren after checking the resultant report to actually rename the files.

Related

In Windows cmd, how to replace the " special character with a line break?

Just to be thorough, I'll state here my whole project and what I'm aiming at.
I intend to adapt a shell script to work in Windows cmd, as this is intended for people who are not going to have some sophisticate language available.
for g in $(curl -Ls https://api.chess.com/pub/player/hikaru/games/archives | jq -rc ".archives[]") ; do curl -Ls "$g" | jq -rc ".games[].pgn" ; done >> games.pgn
For some reason, Chess.com's API doesn't have a very important feature that Lichess' does, to export all games of a single player, so what I can do manually is to use https://api.chess.com/pub/player/hikaru/games/archives to export all available monthly archives and then hit the API for each one of them. (hikaru inside this will be a set variable, it's the nickname of the desired player to export).
The result for this command is something like
{"archives":["https://api.chess.com/pub/player/hikaru/games/2015/11","https://api.chess.com/pub/player/hikaru/games/2015/12","https://api.chess.com/pub/player/hikaru/games/2016/02","https://api.chess.com/pub/player/hikaru/games/2016/03","https://api.chess.com/pub/player/hikaru/games/2016/04","https://api.chess.com/pub/player/hikaru/games/2016/05"]}
to which I only have to append /pgn to get the desired result.
Obviously, cmd doesn't have jq available, so this involves "parsing" the string inside a batch file.
I figured if I just could replace every occurrence of " with a linebreak and echo the results, I could then use find (or findstr) to easily get a list of lines that only would need to be prefaced with curl and appended with /pgn to get my final result.
The big question is: how do I replace " with a linebreak in cmd? I found a few answers, but none of them seems to work with a special character, part of the problem is that I also didn't understand these answers enough to try and adapt them.
A second way of perhaps achieving the same result would be replacing [, ] and , with line breaks, but then I would also have to worry with deleting the final " to append /pgn, so if I'm able to do the former, it would be cleaner.
in batch/cmd, a for loop is used to process a list (separated by default delimiters like space, tab, comma). So just replace [ and ] with a space or comma, and you have a nice list to split. Finally, use find to filter the output to the relevant parts and you're done:
#Echo off
setlocal
set "string={"archives":["https://api.chess.com/pub/player/hikaru/games/2015/11","https://api.chess.com/pub/player/hikaru/games/2015/12","https://api.chess.com/pub/player/hikaru/games/2016/02","https://api.chess.com/pub/player/hikaru/games/2016/03","https://api.chess.com/pub/player/hikaru/games/2016/04","https://api.chess.com/pub/player/hikaru/games/2016/05"]}"
set "string=%string:[= %"
set "string=%string:]= %"
for %%a in (%string%) do echo %%~a|find "/"
Output:
https://api.chess.com/pub/player/hikaru/games/2015/11
https://api.chess.com/pub/player/hikaru/games/2015/12
https://api.chess.com/pub/player/hikaru/games/2016/02
https://api.chess.com/pub/player/hikaru/games/2016/03
https://api.chess.com/pub/player/hikaru/games/2016/04
https://api.chess.com/pub/player/hikaru/games/2016/05
(in case you wonder: the tilde in echo %%~a removes surrounding quotes)
Stephan's answer gave me the directions I needed to research more and build my own solution. This is not the final script to my project, but it does solve every problem presented in my original question:
#echo off
setLocal enabledelayedexpansion
for /f "delims=" %%a in (input.txt) do (
for %%b in (%%a) do (
set string=%%b
set "string=!string:[=,!"
set "string=!string:]=,!"
echo !string!>>replaced.txt
)
)
for /f "delims=" %%c in (replaced.txt) do (
for %%d in (%%c) do (
echo %%~d>>echo.txt
)
)
for /f %%e in (echo.txt) do echo curl %%~e/pgn|find ".">>list.txt
I basically run 3 sets of loops, the first one loads my input (this could not be done via set because there's a size limit, using a nested loop works around that) and replaces [ and ] for commas.
The second loop sorts again the output. This is done basically to trim unwanted characters from the first and last line.
The last loop generates a list of curl commands that will later be executed into a PGN file (which is a chess file).
This ends the scope of the question, but since my project wasn't that complex, I'll present it's final version, which improves on Compo's answer, in case someone else stumbles upon this question:
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Chess.com and Lichess API Scraper ::
:: Author: fabiorzfreitas ::
:: Extract all games from a player from Chess.com and Lichess ::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: This tool uses Chess.com and Lichess APIs to extract all games from a given player. ::
#echo off
setLocal enabledelayedexpansion
echo.
echo.
echo.
echo All input must be lowcase!
echo.
echo You can skip the input bellow by pressing Enter
echo.
echo.
echo.
set /p lichess="Input Lichess nickname and press Enter: "
set /p chess="Input Chess.com nickname and press Enter: "
echo.
:Lichess
if not defined lichess goto :Chess
curl https://lichess.org/api/games/user/%lichess% >> Games.pgn
:Chess
if not defined chess goto :End
(for /f "usebackq tokens=2 delims=[]" %%g in (`curl https://api.chess.com/pub/player/%chess%/games/archives`) do (
for %%h In (%%g) do curl "%%~h/pgn" >> Games.pgn
)
)
:End
exit
Based upon your own answer, it seems as if you could remove at least one of those steps by using the brackets [ and ], as delimiters.
You could also nest a for loop within another instead of having individual ones and writing to files.
Here it is as a single line batch-file:
#(For /F "UseBackQ Tokens=2 Delims=[]" %%G In ("input.txt") Do #For %%H In (%%G) Do #Echo curl.exe "%%~H/pgn") 1>"list.txt"
To do it directly in cmd:
(For /F "UseBackQ Tokens=2 Delims=[]" %G In ("input.txt") Do #For %H In (%G) Do #Echo curl.exe "%~H/pgn") 1>"list.txt"

FINDSTR to find text START END of string

I have string photo="999" price="10" category="1" . I want to get only 10. This means I need to the string which start price=" and ends with "
#For /F "Tokens=1*Delims==" %%A In ('FindStr /I "^price=" "C:\price.txt" 2^>NUL')Do #Set "Ver=%%~B"
#Echo(%%Ver%% = %Ver% & Pause
findstr always returns the complete line, if successful. So it's not the right tool for this task (actually, there is no tool in cmd at all that could do that this way).
But with a bit of logic, you can work around it: remove the part from the start until (including) the triggerword price (a task, the set command is happy to do), then process the rest with a for /f loop to get the desired substring:
set "string=photo="999" price="10" category="1""
echo check: %string%
echo debug: %string:*price=%
for /f tokens^=2^ delims^=^" %%a in ("%string:*price=%") do set "ver=%%~a"
echo ver=%ver%
If you are sure of the exact format of your string (in your example the searched substring is the second quoted argument, so the fourth token when splitted by ") it gets as easy as:
for /f tokens^=4^ delims^=^" %%a in ("%string%") do echo ver=%%~a
or
for /f tokens^=4^ delims^=^" %%a in (file.txt) do echo ver=%%~a
#ECHO OFF
SETLOCAL
set "string=photo="999" price="10" category="1""
:: remove quotes
set "string=%string:"=%"
for /f %%a in ("%string:* price=%") do set /a pricefound%%a
set pri
goto :eof
Since we don't have a representative sample of the file in question, we're forced to the conclusion that the requirement is to find the one and only appearance of price="anumber" in the file.
So, since findstr output, properly framed, would select this line, all we need do is process the string.
This is kind of a quick-and-dirty method; it may be adequate for OP's purpose.
First, remove the quotes from the string as they have a habit of interfering.
Next, use for /f in string-processing mode where it does its magic on the quoted string in parentheses. The string is the original string, minus quotes, so replace all characters up to "Spaceprice" with nothing and take the first token of the result, resulting in =10 assigned to %%a in the example case.
Then execute "set /a somevariablename=10" by simply concatenating the two strings.
Note that if the file contains a line like ... pricelastweek="9" ... then other measures may need to be taken.
Here's an example which tries to follow a similar methodology as your example code.
It uses FindStr to isolate any line in C:\price.txt, which includes the word price="<OneOrMoreDigits>". That line is saved as a variable named price, which is split under delayed expansion in a nested For loop, to remove everything up to, and including the first instance of the string price, leaving, in this case, ="10" category="1". The nested loop further splits that, to take the second token, using a doublequote character as the delimiter, (which should be your required value).
#For /F Delims^=^ EOL^= %%G In ('%__AppDir__%findstr.exe /IR "\<price=\"[0123456789]*\"\>" "C:\price.txt"') Do #(Set "price=%%G" & SetLocal EnableDelayedExpansion
For /F Tokens^=2^ Delims^=^" %%H In ("!price:* price=!") Do #EndLocal & Set "price=%%H")
#Echo %%price%% = %price% & Pause
Well clearly you need to match lines that contain price=" as there may be other lines.
What's unclear is if you need match 10 exactly, or just want that to be any number.
It seems likely you just want to match any number and grab it.
This is done easily with:
#For /F "Tokens=4 Delims=^= " %%A In ('
TYPE "C:\price.txt" ^| FIND /I "price="""') Do #(
Set "Ver=%%~A" & CALL SET Ver &Pause )
While is you need to match Price="10", which seems less useful, but at least one person took that meaning and your wording is a little unclear so I will add that was well:
#For /F "Tokens=4 Delims=^= " %%A In ('
TYPE "C:\price.txt" ^| FIND /I "price=""10"""') Do #(
Set "Ver=%%~A" & CALL SET Ver &Pause )
Note in all examples I left in the # symbols since I assume this is you being clever, and leaving ECHO ON and only removing the # symbols when you want to debug some specific thing you are doing.
However, in case not, it's worth pointing out that in a script it's usually easiest to place ECHO OFF at the start of the script instead of putting an # at the beginning of each statement to stop it from echoing.
Cheers! :)

Windows Batch file - strip leading characters

I have a batch file which copies some local files up to a google storage area using the gsutil tool. The gsutil tool produces a nice log file showing the details of the files that were uploaded and if it was OK or not.
Source,Destination,Start,End,Md5,UploadId,Source Size,Bytes Transferred,Result,Description
file://C:\TEMP\file_1.xlsx,gs://app1/backups/file_1.xlsx,2018-12-04T15:25:48.428000Z,2018-12-04T15:25:48.804000Z,CPHHZfdlt6AePAPz6JO2KQ==,,18753,18753,OK,
file://C:\TEMP\file_2.xlsx,gs://app1/backups/file_2.xlsx,2018-12-04T15:25:48.428000Z,2018-12-04T15:25:48.813000Z,aTKCOQSPVwDycM9+NGO28Q==,,18753,18753,OK,
What I would like to do is to
check the status result in column 8 (OK or FAIL)
If the status is OK then move the source file to another folder (so that it is not uploaded again).
The problem is that the source filename is appended with "file://" which I can't seem to remove, example
file://C:\TEMP\file_1.xlsx
needs to be changed into this
C:\TEMP\file_1.xlsx
I am using a for /f loop and I am not sure if the manipulation of the variables %%A is different within a for /f loop.
#echo off
rem copy the gsutil log file into a temp file and remove the header row using the 'more' command.
more +1 raw_results.log > .\upload_results.log
rem get the source file name (column 1) and the upload result (OK) from column 8
for /f "tokens=1,8 delims=," %%A in (.\upload_results.log) do (
echo The source file is %%A , the upload status was %%B
set line=%%A
set line=!line:file://:=! >> output2.txt echo !line!
echo !line!
)
The output is like this.
The source file is file://C:\TEMP\file_1.xlsx , the upload status was OK
The source file is file://C:\TEMP\file_2.xlsx , the upload status was OK
I'm expecting it to dump the altered values out into a new file but it is not producing anything at the moment.
Normally I would extract from a specific character to the end of the string with something like this but it doesn't work with my For/f loop.
%var:~7%
Any pointers or a different way of doing it greatly appreciated.
Since the part to remove seems fixed it is easier to use substrings.
Also using for /f "skip=1" evades he neccessity of the external command more +1 and another intermediate file.
#echo off & setlocal EnableDelayedExpansion
type NUL>output2.txt
for /f "skip=1 eol=| tokens=1,8 delims=," %%A in (.\upload_results.log) do (
echo The source file is %%A , the upload status was %%B
set "line=%%A"
set "line=!line:~7!"
echo(!line!>>output2.txt
echo(!line!
)
File names and paths can contain also one or more exclamation marks. The line set line=%%A is parsed by Windows command processor a second time before execution with enabled delayed expansion. See How does the Windows Command Interpreter (CMD.EXE) parse scripts? Every ! inside the string assigned to loop variable A is on this line interpreted as begin or end of a delayed expanded environment variable reference. So the string of loop variable A is assigned to environment variable line with an unwanted modification if file path/name contains one or more exclamation marks.
For that reason it is best to avoid usage of delayed expansion. The fastest solution is for this task using a second FOR to get file:// removed from string assigned to loop variable A.
#echo off
del output2.txt 2>nul
for /F "skip=1 tokens=1,8 delims=," %%A in (upload_results.log) do (
echo The source file is %%A , the upload status was %%B.
for /F "tokens=1* delims=/" %%C in ("%%~A") do echo %%D>>output2.txt
)
Even faster would be without the first echo command line inside the loop:
#echo off
(for /F "skip=1 delims=," %%A in (upload_results.log) do (
for /F "tokens=1* delims=/" %%B in ("%%~A") do echo %%C
))>output2.txt
The second solution can be written also as single command line:
#(for /F "skip=1 delims=," %%A in (upload_results.log) do #for /F "tokens=1* delims=/" %%B in ("%%~A") do #echo %%C)>output2.txt
All solutions do following:
The outer FOR processes ANSI (fixed one byte per character) or UTF-8 (one to four bytes per character) encoded text file upload_results.log line by line with skipping the first line and ignoring always empty lines and lines starting with a semicolon which do not occur here.
The line is split up on every occurrence of one or more commas into substrings (tokens) with assigning first comma delimited string to specified loop variable A. The first solution additionally assigns eighth comma delimited string to next loop variable B according to ASCII table.
The inner FOR processes the string assigned to loop variable A with using / as string delimiter to get assigned to specified loop variable file: and to next loop variable according to ASCII table the rest of the string after first sequence of forward slashes which is the full qualified file name.
The full qualified file name is output with command echo and appended either directly to file output2.txt (first solution) or first to a memory buffer which is finally at once written into file output2.txt overwriting a perhaps already existing file with that file name in current directory.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
del /?
echo /?
for /?
See also the Microsoft article about Using command redirection operators for an explanation of the redirections >, >> and 2>nul

Extract first character from a string

I've no knowledge regarding Windows batch programming syntax. I have a text file containing user IDs which I need to delete using curl command and for that I need to extract first character of every user ID and then pass to the curl command. I know the curl command which will require two variables:
'UserID' - Read from the text file.
'firstCharacter' - Extracting first character from the User ID.
Below is the code to fetch user IDs from users.txt file:
#echo off
for /f "tokens=*" %%a in (users.txt) do call :processline %%a
pause
goto :eof
:processline
echo %*
goto :eof
:eof
Please help me with extracting the first character from the read User IDs.
Thanks.
The cmd.exe can do a limited amount of string parsing. JosefZ gave you a good place to start.
C:>echo %PROCESSOR_ARCHITECTURE%
AMD64
C:>echo %PROCESSOR_ARCHITECTURE:~0,1%
A
In a batch / command file I was always needing the first x characters or a string and ended up with many functions all doing the same thing but different names, i.e. getFirstChar, getFirstTwoChars, etc.. - so decided to make a generic function where I could pass in the number of characters I needed:
::-- getFirstXChars
:getFirstXChars
set sValIn=%1
set /a iNo=%2
set vWorkVal=%%sValIn:~0,%iNo%%%
call:getFirstX %vWorkVal% vWorkVal2
set %3=%vWorkVal2%
:getFirstX
set %2=%~1
goto:eof
to use the syntax would be
set varToTrim=ABCDEFG
call:getFirstXChars %varToTrim% 2 varToTrimAfter
#echo 1 %varToTrim%
#echo 2 %varToTrimAfter%
pause
result from command file:
1 ABCDEFG
2 AB
I just added to set variable "id" to be %%a, then I used substring notation to get the first character.
Substrings are processed by using :~start,length after the variable name and before the last % in the variable.
#echo off
for /f "tokens=*" %%a in (output.txt) do set id=%%a & call :processline %%a
pause
goto :eof
:processline
echo %id:~0,1%
goto :eof
:eof

Batch: compare files using CRC32

I have given myself a simple task of writing a batch file that will do the following:
Compare CRC32 of each file in "first" folder with each file in "second" folder. Output needs to be the name of every file from the "first" folder, which doesn't have its duplicate in "second".
Note: "CRC32.exe -nf" outputs CRC32 in first line, and file size in second one.
Here is how I tried to do it:
#echo off
for %%i in ("%cd%\first\*.*") do (
set uniq=0
for /f %%x in ('crc32 %%i -nf') do (
set one=%%x
goto find_two
)
:find_two
for %%j in ("%cd%\second\*.*") do (
for /f %%x in ('crc32 %%j -nf') do (
set two=%%x
goto compare
)
:compare
if one==two (
goto next
) else (
set uniq=1
)
)
if uniq==1 (
echo %%i >>result.txt
)
:next
)
I assume there are several errors present in this code, but I had trouble finding them. So if anyone has time, and thinks he can help, I would be grateful.
If you think different approach is required, feel free to show it.
There are two major problems.
Goto's inside brackt doesn't work as expected, they stop the loop immediately, so your code will simply fail there.
The solution is to avoid goto or to call a function and use the goto there.
The second problem are the compares.
if one==two ( will be always false, as it compare the word one against the word two, but not the content of the variables.
Normally you would use if "%one%"=="%two%" echo equal, but as it should work inside of brackets you need delayed expansion, so it looks like.
if !one!==!two! echo equal
Don't forget to enable the delayed expansion before you start the loop with
setlocal EnableDelayedExpansion
Might be easier to capture the output of crc32 using something like this:
for /f %%x in ('crc32 "%filename%" -nf ^| find /v "CRC32"') do stuff with %%x
Can you show me the full output of an example crc32 if this is not helpful?

Resources