Copy Multiple Files with Same Characters to a Single File - batch-processing

I have a lot of files that need to be joined. Existing file naming structure is 20130514ABCD.txt where the file naming convention is year, month, date, city. I would like to merge files with same last four characters (i.e same city) into one.
I am only able to move the city to a folder created for each city. This is not I want..
#echo off
pushd pathname
for /f %%F in ('dir/b/a-d *.txt') do call :sub1 %%F
goto :eof
:sub1
set name=%1
md %name:~9,12%
move %* %name:~9,12%
What I need is script equivalent to C:\>copy *CITY.TXT to CITY.txt, where CITY name is a variable.

EDITED: This works here with files as you've stated.
#echo off
pushd "pathname"
for /f "delims=" %%F in ('dir /b /a-d *.txt') do call :sub1 "%%F"
popd
pause
goto :eof
:sub1
set "name=%~1"
set "cityname=%name:~8,-4%"
if exist "%cityname%\" goto :EOF
echo processing "%cityname%"
md "%cityname%" 2>nul
copy /b "????????%cityname%.txt" "%cityname%\%cityname%.txt" >nul

Related

CMD: extract part of a filename and use it as variable

I ripped my CDs with EAC to CUE+WAV files, added cover art and all my files are in the same folder with filenames pattern "Album artist - Album title", for ex.:
Clannad - Legend.wav/cue/jpg
David Bowie - Best Of Bowie [Disc 1].wav/cue/jpg
David Bowie - Best Of Bowie [Disc 2].wav/cue/jpg
I'm new to this so I wrote a simple CMD batch to convert my music to FLAC format, but it requires manual copying and pasting of the actual wav/cue/jpg filenames and input of album artist, disc no. and total disc no. for their corresponding tags. It cannot be stored in cuesheet file for some reason, but in my case I have them in filenames as you can see above).
ECHO WAV/CUE/JPG FILENAME
SET /P "input="
ECHO ALBUMARTIST
SET /P "albumartist="
ECHO DISCNUMBER
SET /P "discnumber="
ECHO TOTALDISCS
SET /P "totaldiscs="
flac.exe -0 --picture="D:\Music\%input%.jpg" --tag-from-file="CUESHEET=D:\Music\%input%.cue" -T "ALBUMARTIST=%albumartist%" -T "DISCNUMBER=%discnumber%" -T "TOTALDISCS=%totaldiscs%" "D:\Music\%input%.wav"
My question is about automation of converting all my ripped albums. How can I extract album artist/disc no./total disc no. info from filenames and loop that for every .wav file?
#ECHO OFF
SETLOCAL
SET "destdir=U:\destdir"
PUSHD "%destdir%"
:: Find all .jpgs where there is a .wav and .cue with the same name
FOR /f "delims=" %%a IN ('dir /b /a-d *.jpg') DO IF EXIST "%%~na.wav" IF EXIST "%%~na.cue" (
FOR %%b IN (input albumartist discnumber totaldiscs) DO SET "%%b="
SET "input=%%~na"
FOR /f "delims=-" %%b IN ("%%a") DO SET "albumartist=%%b"
FOR /f "tokens=2delims=[]" %%b IN ("%%a") DO SET "disc=%%b"
IF DEFINED disc (
FOR /f "tokens=1delims=[]" %%d IN ("%%a") DO FOR /f %%c IN ('dir /b "%%d[*.wav"') DO SET /a totaldiscs+=1
)
CALL :gflac
)
POPD
GOTO :EOF
:gflac
:: remove trailing spaces from INPUT
IF "%albumartist:~-1%"==" " SET "albumartist=%albumartist:~0,-1%"&GOTO gflac
:: presume default for disc and totaldiscs
IF DEFINED disc (FOR /f "tokens=2" %%d IN ("%disc%") DO SET /a disc=%%d) ELSE (SET /a disc=1)
IF NOT DEFINED totaldiscs SET /a totaldiscs=1
ECHO( flac.exe -0 --picture="D:\Music\%input%.jpg" --tag-from-file="CUESHEET=D:\Music\%input%.cue" -T "ALBUMARTIST=%albumartist%" -T "DISCNUMBER=%discnumber%" -T "TOTALDISCS=%totaldiscs%" "D:\Music\%input%.wav"
GOTO :eof
You would need to change the setting of destdir to suit your circumstances.
The above will merely echo the required flac line. I left it as you posted but set the scant test data you posted up as I interpret it (ie. there is a set of 3 files) on my U: drive.
Sadly, you've given us insufficient information. I've assumed that you need all three files to be present, and the default for totaldiscs is 1.
First, look for all .jpgs, and if there is a corresponding .wav and .cue then process for flac generation as follows:
Set input to the name part of the .jpg found
set albumartist to the first part of the filename, up to the -
get the [disc n] string if it's present
count the number of .wavs that start with the filename up to the [
generate the flac line.
Within the generation of the flac line, we strip off the trailing spaces from input, convert disc n to n or set disc to 1 (although this information may not be needed), and set the totaldiscs to 1 if it's not been calculated.
You don't say what flac produces as output, but I'd suggest that you further gate that filetype so that the procedure doesn't run if the %input%.finalproductwhateverthatis file is present.
[edited per dbenham's comments]
This is very similar to Magoo's answer, with some bug fixes, and everything is done in one master loop without a CALL. As with Magoo's answer, modify your destination folder at the beginning to suit your needs.
#echo off
:: Delayed expansion must be disabled to protect ! when expanding FOR variables.
:: It is normally disabled by default, but I'm making it explicit, just to be sure.
setlocal disableDelayedExpansion
:: Define where source files are coming from
set "source=D:\Music"
:: Define where output should be stored
set "destination=D:\Music"
pushd "%destination%"
:: Iterate each .wav file (A) and only proceed if .jpg and .cue also exists
for /f "delims=" %%A in ('dir /b /a-d "%source%\*.wav"') do if exist "%source%\%%~nA.jpg" if exist "%source%\%%~nA.cue" (
%= Get base name with path, but without extension =%
set "file=%source%\%%~nA"
%= Extract "artist - album " (B) and "Disc #" (C) from base name (~nA) =%
for /f "delims=[] tokens=1,2" %%B in ("%%~nA") do (
%= Extract "artst " (D) from "artist - abum ". (~nxD) trims trailing space =%
for /f "delims=-" %%D in ("%%B") do set "artist=%%~nxD"
%= Extract the number (E) from "Disc #", use 1 as default if not there =%
set "disc=1"
for /f "tokens=2" %%E in ("%%C") do set "disc=%%E"
%= Count the number of discs (F), will be 0 if no [Disc #] =%
%= The [ is appended to name to prevent something like "Greatest Hits 2" from matching "Greatist Hits" =%
for /f %%F in ('dir /b /a-d "%source%\%%B[*.wav" 2^>nul ^|find /c /v ""') do set "count=%%F"
%= temporarily enable delayed expansion to access variables set within loop =%
setlocal enableDelayedExpansion
%= Set count to 1 if no [Disc #] =%
if !count! equ 0 set /a count=1
flac.exe -0 --picture="!file!.jpg" --tag-from-file="CUESHEET=!file!.cue" -T "ALBUMARTIST=!artist!" -T "DISCNUMBER=!disc!" -T "TOTALDISCS=!count!" "!file!.wav"
%= pop the setlocal stack to get back to state at beginning of loop =%
endlocal
)
)
popd
You may want to add a check to only proceed if the FLAC file does not already exist so you can run the script multiple times without reprocessing files. The outer loop would look something like this, but I can't be sure since I don't know the format of the output file name:
for /f "delims=" %%A in ('dir /b /a-d "%source%\*.wav"') do if exist "%source%\%%~nA.jpg" if exist "%source%\%%~nA.cue" if not exist "%destination%\%%~nA.flac" (
The logic is much simpler using my JREPL.BAT utility if you understand regular expressions:
#echo off
:: Delayed expansion must be disabled to protect ! when expanding FOR variables.
:: It is normally disabled by default, but I'm making it explicit, just to be sure.
setlocal disableDelayedExpansion
:: Define where source files are coming from
set "source=D:\music"
:: Define where output should be stored
set "destination=D:\music"
pushd "%destination%"
:: Iterate all *.wav and use JREPL to format result as "fullFileName|artist - album|artist|Disc#"
:: %%A %%B %%C %%D
:: Only proceed if .jpg and .cue also exist
for /f "tokens=1-4 delims=|" %%A in (
'dir /b /a-d "%source%\*.wav"^|jrepl "^((.+?) - .+?)(?:\[Disc (\d+)])?\.wav$" "$&|$1|$2|$3" /i'
) do if exist "%%~nA.jpg" if exist "%%~nA.cue" (
%= disc and count are both 1 if %%D is empty =%
if "%%D" equ "" (
flac.exe -0 --picture="%source%\%%~nA.jpg" --tag-from-file="CUESHEET=%source%\%%~nA.cue" -T "ALBUMARTIST=%%C" -T "DISCNUMBER=1" -T "TOTALDISCS=1" "%source%\%%A"
%= else count the number of .wav files =%
) else for /f %%E in ('dir /b /a-d "%%B[*.wav"^|find /c /v ""') do (
flac.exe -0 --picture="%source%\%%~nA.jpg" --tag-from-file="CUESHEET=%source%\%%~nA.cue" -T "ALBUMARTIST=%%C" -T "DISCNUMBER=%%D" -T "TOTALDISCS=%%E" "%source%\%%A"
)
)
popd
Again, you can add an IF to the outer loop to proceed only if the .flac file does not already exist in the destination.

FINDSTR in FOR loop with variables as strings (Batch File)

It's my first post here, unfotrtunately question to you instead of help.
I'm writing small program which moves text and pdf files when string is found in text file.(1.txt, 1.txt.pdf ...) It was working very good when i had variables manually set in batch like this :
set c1=xxx
set p1=c:\test\xxx\
set c2=yyy
set p2=c:\test\yyy\
FOR /f "tokens=*" %%A IN ('FINDSTR /i /m "%c1%" "c:\test\*.txt"') DO (
IF "%ERRORLEVEL%"=="0" MOVE %%A "%p1%">nul
MOVE %%A.pdf %p1%
FOR /f "tokens=*" %%A IN ('FINDSTR /i /m "%c2%" "c:\test\*.txt"') DO (
IF "%ERRORLEVEL%"=="0" MOVE %%A "%p2%">nul
MOVE %%A.pdf %p2%
Now I'm trying to use input file with variables :
for /F "delims=^ tokens=1,2" %%A in (c:\test\db\input.cdb) do (
SET /A vidx=!vidx! + 1
set c!vidx!=%%A & set p!vidx!=%%B
It works good, but the problem starts when I try to use for loop for all this variables. Literally search for all (c1,c2,c3...) variables and move them to (p1,p2,p3,...) paths.
Input.cdb looks like this :
xxx ^ c:\test\xxx
yyy ^ c:\test\yyy
Code:
setlocal ENABLEDELAYEDEXPANSION
set vidx=0
for /F "delims=^ tokens=1,2" %%A in (c:\test\db\input.cdb) do (
SET /A vidx=!vidx! + 1
set c!vidx!=%%A & set p!vidx!=%%B
FOR /f "tokens=*" %%D IN ('FINDSTR /i /m "%c!vidx!%" "c:\test\*.txt"') DO (
IF "%ERRORLEVEL%"=="0" MOVE %%D "%p!vidx!%">nul
MOVE "%%D.pdf" "%p!vidx!%"
ECHO %%D File is being procesed now
)
)
I don't know why this code doesn't work, maybe because there is loop inside loop?
Or how to create loop to do the same from 0 to how many lines of variables has been read from input.cdb?
I've tried to run this in separate for loop with temporary variable but it's still not using my string and path variables.
Thank you in advance for any help!
With Regards
Blackfusion
Ugh. More than one problem, and what is the appropriate solution?
First item is a matter of logical design. Let's assume that the batch worked as intended. c!vidx! would be set to %%A and p!vidx! to %%B, so unless there's some unpublished use for c... and p... then you could simply substitute as follows:
setlocal ENABLEDELAYEDEXPANSION
for /F "delims=^ tokens=1,2" %%A in (c:\test\db\input.cdb) do (
FOR /f "tokens=*" %%D IN ('FINDSTR /i /m "%%A" "c:\test\*.txt"') DO (
IF "%ERRORLEVEL%"=="0" MOVE %%D "%%B">nul
MOVE "%%D.pdf" "%%B"
ECHO %%D File is being procesed now
)
)
Now - there's a common fundamental misconception about %var% where var is any variable (including %ERRORLEVEL% and %C!vidx!%...) - a %VAR% occurring in a compound statement (or "block") - within the parentheses if a FOR...DO (in here) or IF...(here) ELSE (or here) is replaced with that variable's value at the time the outermost statement is parsed - before it is executed. If delayedexpansion is invoked (as you have) then !var! refers to the run-time value and %var% to the parse-time value.
Hence, if "%errorlevel%"=="0" would be evaluated according to the state of errorlevel when the for...%%A was parsed, not the value as set by the findstr.
This can be corrected by
setlocal ENABLEDELAYEDEXPANSION
for /F "delims=^ tokens=1,2" %%A in (c:\test\db\input.cdb) do (
FOR /f "tokens=*" %%D IN ('FINDSTR /i /m "%%A" "c:\test\*.txt"') DO (
IF not errorlevel 1 MOVE %%D "%%B">nul
MOVE "%%D.pdf" "%%B"
ECHO %%D File is being procesed now
)
)
Where IF not errorlevel 1 is interpreted as if the CURRENT (run-time) errorlevel is NOT (1 or greater than 1)

cmd bat file, set output file name as source file

I have the below code which gets the last modified file from "AX copy" and compares it with the last modified file in "Reflex copy". It then outputs the difference in Error.
I need the output copy to have the same name as the input file from "AX copy". I have used something to declare the file name again and assign it to the output. however I have a problem, when delcaring it in the below code, it uses the oldest file name.
Please can someone adjust the code so the output copy has the same name as the input from the "AX copy" folder. My code is:
#echo off
cd /d C:\Users\Important Structure\Development\AX copy
for /f %%a in ('dir /b /o-d /a-d /tw') do (set latest=%%a)
setlocal enableextensions disabledelayedexpansion
call :getLatestFileInFolder "C:\Users\Important Structure\Development\AX copy" latestC
call :getLatestFileInFolder "C:\Users\Important Structure\Development\Reflex copy" latestD
if not defined latestC ( echo NO File in C & exit /b )
if not defined latestD ( echo NO File in D & exit /b )
for /f "tokens=1,*" %%a in (
'diff "%latestC%" "%latestD%" ^| findstr /r /c:"^<" /c:"^>"'
) do (
>> "C:\Users\Important Structure\Development\Error\%latest%" echo(%%b
)
endlocal
exit /b
:getLatestFileInFolder folderToSearch variableToReturn
setlocal
set "folder=%~1" & if not defined folder set "folder=%cd%"
set "latest="
pushd "%folder%"
for /f "tokens=*" %%a in ('dir /b /o-d /a-d /tw 2^>nul') do (set "latest=%%~fa" & goto :latestFileFound)
:latestFileFound
popd
endlocal & set "%~2=%latest%" & goto :eof
In this line
for /f %%a in ('dir /b /o-d /a-d /tw') do (set latest=%%a)
The dir command in listing file in date descending order. On first iteration (the first file), the latest file is assigned to the variable, but for command does not stop here, and continues iterating over the rest of the files. So, at end, the variable contains the oldest file in folder.
To solve it , you can list the files in ascending date order (change /o-d to /od), or use the included subroutine
call :getLatestFileInFolder "C:\Users\Important Structure\Development\AX copy" latest
Which, as the code is working, is the same that Matt has indicated to you. %latest% is the same file that %latestC%
Why does it not work. diff command has to open the file to read, so, probably, you can not write to it while diff has it locked.
To solve it, select a temporary file (set "tempFile=%temp%\%~nx0.tmp"), write the generated diff to this temporary file and then append this temporary file to the original file (type "%tempFile%" >> "%latest%") or overwrite it if the previous content is not needed (type "%tempFile%" > "%latest%")

Batch failure set value to a variable

this is my code:
#echo off
Setlocal EnableDelayedExpansion
set log=C:\mylog.log
set ftpFolder=C:\contributor\ftp
set rotterdamFolder=C:\rotterdam
cd /D C:\contributor
echo [FTP Folder: %ftpFolder%] >> %log%
cd /D %ftpFolder%
for /D %%f in (*) do (
cd %%f
for %%i in (*) do (
echo [FTP, %%f] Sending %%i >> %log%
for /f "tokens=1,2" %%a in (C:\input.txt) do (
if %%a==%%f (
set et=%%b
)
)
copy %ftpFolder%\%%f\%%i %rotterdamFolder%\%et% >> %log%
)
cd .. >> %log%
)
input.txt file is something like:
007 87855
008 87823
015 87830
it's not important how the two columns are divided (tab or space).
my problem is that %et% variable is not defined. how is it possible? I need to use variable "b" as destination folder. where is the error?
%et% is expanded when the line is parsed, and the entire outer parenthesized FOR loop is parsed all at once. So the value you get is the value that existed before your loop was entered.
You could solve the problem by enabling delayed expansion and using !et! instead. Type HELP FOR or FOR /? from the command line for more information. The section about delayed expansion is about halfway down.
But there really isn't any need to use the et variable at all. I believe you only want to copy the file when the first column of the inner loop matches the folder name. Simply move and substitute the COPY command for the SET statement. Use %%b instead of %et% in the COPY statement.
It wasn't necessary, but I substituted PUSHD/POPD for the CD commands. The redirection with CD is pointless when a path is specified.
You really should quote your paths in case they contain spaces or special characters.
for /D %%f in (*) do (
pushd "%%f"
for %%i in (*) do (
echo [FTP, %%f] Sending %%i >> %log%
for /f "tokens=1,2" %%a in (C:\input.txt) do if "%%a"=="%%f" (
copy "%ftpFolder%\%%f\%%i" "%rotterdamFolder%\%%b" >> %log%
)
)
popd
)

How to delete a line of a text file, given the line number using batch (.bat) script?

I want to write a batch script where the user can enter the line number and the script will delete that line of a text file.
Eg: tmp.txt
1. aaa
2. bbb
3. ccc
4. ddd
I want when i execute my script and user inputs 3, the tmp.txt to be
1. aaa
2. bbb
4. ddd
I am trying something like this:
#ECHO OFF
SET /P LINENUMBER=Enter the Line No:
FOR /F "tokens=1* delims=[]" %%a IN ('FIND /n /v "" ^< tmp.txt') DO (
IF %%a equ %LINENUMBER% (
????????????????????
)
)
You cannot modify the file directly. You must create a new file with the desired content and then replace the original file with the new file.
You create the new file by redirecting output and ECHOing the desired content. I use ECHO(%%b in case %%b is empty because the line was blank. ECHO %%b would produce ECHO is off. if b is empty. Most people use ECHO., but that can fail under esoteric conditions. ECHO( will never fail.
You use MOVE to replace the original file with the new.
You can eliminate the need to check for the number within your FOR /F loop by using FINDSTR after FIND to filter out the unwanted line.
Note that your code, as written so far, will strip any leading [ and ] characters from each line. Because the line filter is applied before the FOR iteration, you can improve the situation slightly by setting DELIMS to ] only.
#ECHO OFF
SET /P LINENUMBER=Enter the Line No:
>tmp.txt.new (
FOR /F "tokens=1* delims=]" %%a IN (
'FIND /n /v "" ^<tmp.txt^|FINDSTR /VLB "[%LINENUMBER%]"'
) DO ECHO(%%b
)
>nul MOVE /y tmp.txt.new tmp.txt
If any of your original lines already begin with ] then you can use FINDSTR instead of FIND to introduce the line number. That uses nn: format instead of [nn]. This solution will strip leading : from your lines.
#ECHO OFF
SET /P LINENUMBER=Enter the Line No:
>tmp.txt.new (
FOR /F "tokens=1* delims=:" %%a IN (
'FINDSTR /n "^" tmp.txt^|FINDSTR /VLB "%LINENUMBER%:"'
) DO ECHO(%%b
)
>nul MOVE /y tmp.txt.new tmp.txt
If your text file contains lines that begin both with : and ] then you can't use FOR /F to parse out the line number. Instead you use search and replace. Delayed expansion is toggled on and off within the loop to protect any ! that may exist in the file.
#ECHO OFF
setlocal disableDelayedExpansion
SET /P LINENUMBER=Enter the Line No:
>tmp.txt.new (
FOR /F "delims=" %%a IN (
'FINDSTR /n "^" tmp.txt ^| FINDSTR /VLB "%LINENUMBER%:"'
) DO (
set "ln=%%a"
setlocal enableDelayedExpansion
echo(!ln:*:=!
endlocal
)
)
>nul MOVE /y tmp.txt.new tmp.txt
try out this:
#echo off & setlocal
set "InFile=Z:\Text.txt"
set "OutFile=Z:\Erg.txt"
set /p LineToDelete=
if exist "%OutFile%" del "%OutFile%"
for /f "tokens=1* delims=:" %%i in ('findstr /n $ "%InFile%"^|findstr /b "%LineToDelete%:"') do findstr /v /b /c:"%%j" "%InFile%">>"%OutFile%"