Batch file for loop string replacement - windows

I have a batch file that take a directory path as a parameter.
In that file folder, there are any number of .ai.pdf or .pdf files that I need to convert to jpg's. The conversion is not my issue (I am using imageMagick) , but lopping off the full extension is.
I need to be able to either take off the full .ai.pdf (7 characters) or .pdf (4 characters) from the file name and replace it with .jpg I cannot use just ~n in the for loop because it will not take off the .ai in an instance with there is an .ai.pdf (results in file name.ai where I need just the file name)
There are quite a few posts on StackOverFlow about this
StackOverFlow Example
but no matter what I attempt to try, I get an error when truncating the appropriate amount of extension off of the file name.
Here is my code. This is the first major batch file I have ever created, so I am open to anything, other than installing more programs to do the work.
The thing that kills me, is I had this working and in the shuffle from one server to another and a week of vacation, the working code got....misplaced.
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
set dir1=%1
echo recieved !dir1!
for /R %dir1% %%a in (*.pdf) DO (
echo file found !a!
set b=th_%%~nxa
if x%b:ai.pdf=%==x%b% set b=%%~dpa!b:~0,-7!
if not x%b:ai.pdf=%==x%b% set b=%%~dpa!b:~0,-4!
REM convert -density 64 "%%a" +matte -resize 15%% "!b!.jpg"
#echo !b! converted
)
ENDLOCAL
the file tells me that %~dpa!b:~0,-7! is an invalid substitution
Any ideas? Thanks for the help!

A few things first:
!a! and %%a are two different variables.
if x%b:ai.pdf=%==x%b% does not mean what you think it does. That will only be true when %b% does NOT contain .ai.pdf.
Again, if not x%b:ai.pdf=%==x%b% does not mean what you think. This is true when %b% DOES contain .ai.pdf.
There is no need to do any verification and cutting, just search and replace. ( That is what the %y:x=z% notation does, in this example it replaces every x within %y% with a z.) Let search and replace do the verification. It will only replace what matches the search. That will speed up the your batch file.
Lastly, since you are inside a () code block you will need to use the delayed Expansion turned on with your setlocal statement. This is because everything inside a code block is treated as if it were on a single line. That means that if you change a variable inside a code block, you will not be able to see the new value using the % notation. Instead you need to replace the %'s with !'s. For instance...
setlocal enableDelayedExpansion
set x=Hello
(
set x=Goodbye
echo I don't know why you say "!x!", I say "%x%".
)
...will give you the old Beatles lyric...
I don't know why you say "Goodbye", I say "Hello".
Anyway, on to your answer:
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
set dir1=%1
echo recieved !dir1!
for /R %dir1% %%a in (*.pdf) DO (
:: Adding a colon to the end of the filename allows the extension to be ID'd
: without explicitly looking at it with an IF statement, while simultaneously
:: avoiding similar looking character groupings inside the filename.
set b=th_%%~nxa:
:: No need to check for case, the substitution will do that for you.
set b=!b:.ai.pdf:=.jpg!
set b=!b:.pdf:=.jpg!
REM convert -density 64 "%%a" +matte -resize 15%% "!b!"
echo %%a converted into !b!
)
ENDLOCAL
The drawback is that both the files...
X.ai.pdf
X.pdf
...will be translated into th_X.jpg, creating the possibility of duplicate filenames for two different files.
But that's intrinsic to your concept of treating both types of filenames the same. If you don't have a method for avoiding this sort of duplication it might not be a bad idea to leave the .ai on the file, thereby creating two files: th_X.jpg and th_X.ai.jpg, eliminating the possibility of duplicate filenames.

Hm... would this work for you:
for /R %F in (*.pdf) do #for %G in ("%~nF") do #echo %~nxF ==^> %~nG.jpg
(as executed directly from cmd, if run from batch, replace % with %%).
This has a peculiar effect of changing the case of a file to case of a directory if there exists one with the same name as base name of your file (file.pdf will become FILE.jpg if you happen to have a subdirectory called FILE), but that's it (I think).
This also assumes your base names differ (so no file.ai.pdf and file.pdf in same directory)

Related

Parsing every file in submap recursively to output folder whilst maintaining relativity

I'm using the following batch code to convert all files in a certain directory if the target file doesn't already exist however I'm stuck at getting this to run through every submap and file within that (and keep the output relative with that submap)
So I currently use this:
for %%f in (input/textures/*.*) do ( IF NOT EXIST "ouput/textures/%%~nf.dds" (
"bin/ThempImageParser.exe" "input/textures/%%f" "ouput/textures/%%~nf.dds"
)
)
This works perfectly for a single folder (as was intended), it takes all the files in that specific folder, and passes them as arguments to my executable, which then outputs the file on the path of the second argument.
However this also contains a flaw (this is an additional problem though..) as it does not work if the output -folder- does not exist, so if possible I'd also want it to create the folder if need be.
I've found some batch documentation (I really don't have much experience with Batch) showing me a command called FORFILES and the /R parameter, however I couldn't adjust this so it'd keep the relative paths for the output too, it'd require string manipulation and I have no clue on how to do that.
So the result I'm after is something like this, it takes any file deeper than "input/textures/ for example:
input/textures/some/very/deep/submap/why/does/it/go/on/myfile.anyExtension
it should then take that file (and relative path) and basically change "input" with "output" and replace the file extension with .dds like this:
ouput/textures/some/very/deep/submap/why/does/it/go/on/myfile.dds
and pass those two strings to my executable.
#ECHO Off
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir\t w o"
SET "destdir=U:\destdir\wherever\something"
FOR /f "delims=" %%a IN ('xcopy /y /L /s "%sourcedir%\*"') DO (
SET "destfile=%%a"
SET "destfile=!destfile:*%sourcedir%=%destdir%!"
IF /i "%%a" neq "!destfile!" (
FOR %%m IN ("!destfile!") DO IF NOT EXIST "%%~dpm%%~na.dds" (
ECHO MD "%%~dpm"
ECHO "bin\ThempImageParser.exe" "%%a" "%%~dpm%%~na.dds"
)
)
)
GOTO :EOF
You would need to change the settings of sourcedir and destdir to suit your circumstances.
First, perform an xcopy with the /L option to list-only the individual fullnames of files that would be copied by the xcopy.
Assign each name found from %%a to destfile, then remove all characters before the source-directoryname from that filename, and replace that string with the destination directoryname.
This will yield the destination name for the file (with the original extension). The only exception will be the very last output line, which is a count-of-files report. Since this line will not contain the source directoryname, the replacement will not take place, so %%a will be the same as !destfile! - so we eliminate that.
Now assign the destination filename to a metavariable so we can select its various parts, and if the filename made from the destination drive and pathname, the name part of the original file and .dds does not exist, then make the destination directoryname and execute the imageparser, providing the desired output filename.
Note that these last two are ECHOed instead of being executed for testing purposes. Remove the ECHOes to actually perform the command.
Note that / is a switch-indicator, \ is a directory-separator.
Note that MD will report an error if the directory already exists. Append 2>nul to the end of the md command to suppress that error message.

Using dos, how can I rename existing folders switching the date parts around?

I have a set of folders already named mm-dd-yyyy (for example 01-01-2014) but they will be more useful named yyyy-mm-dd (for example 2014-01-01). Using dos, how can I rename the folders switching the date parts around?
Assuming that you're talking about cmd.exe rather than the ancient MS-DOS command.com, you can do this reasonably easily(1).
The idea is to iterate over all directories in the current directory, using for /d. Then, for each one that matches a specific pattern (99-99-9999), you can use string manipulation to construct a rename command.
The checking for a correct format can be done with the regular expression capability of findstr.
#echo off
#setlocal enableextensions enabledelayedexpansion
rem Process every file. For each file with a valid format,
rem construct a rename command.
for /d %%a in (*) do (
set full=%%a
call :isvalidfmt !full!
if !res!==true (
set d2=!full:~0,2!
set m2=!full:~3,2!
set y4=!full:~6!
echo ren !full! !y4!-!m2!-!d2!
)
)
endlocal
goto :eof
rem Use findstr regex option to see if file name is correct format.
:isvalidfmt
set res=true
echo %1|findstr /r "^[0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9]$" >nul:
if errorlevel 1 set res=false
goto :eof
Note that the script above (let's call it rengen.cmd) simply outputs the rename commands, it does not actually rename the files. That's good practice so you can see what it's going to do before you destroy your directories :-)
To actually rename the files (once you've confirmed it's safe), simply capture the output to another file then run that:
rengen >nextren.cmd
nextren
(1) If you are talking about command.com, I can't help you out there. The command interpreter has come a long way since those bad old days and the one that came with MS-DOS is seriously under-powered.
use dir and export to a file. Take it to Excel and create two columns, one with current name and one with new name and then bring it back to notepad and add RENAME command to each row as a prefix.
RENAME cuurentfilename newfilename is the command syntax.

Batch wildcard strange behavior

I have a list of files that I'm looping through that match a certain size letter (A,B,C,D). The files are of the form ###T#####A###_# rev 1.dxf, where the rev 1 is only there some of the time, and A refers to the size, which is A, B, C, or D. When I try to loop through these in a set D.dxf or B.dxf, some A files are also found. I currently use the pattern ?????????A*.dxf, but would like to expand this to more file types without having to make multiple batch files. Interestingly, if I use the pattern TA*.dxf, the wildcard behaves normally.
Why does this happen, and how can I fix it while still being to catch files where the A may be at the beginning, end, middle, etc? If you need any clarification or extra information, feel free to ask.
Here is my relevant code:
FOR %%S IN (A,B,C,D) DO (
echo Converting size %%S. . .
FOR %%F in ("%filepath%\?????????%%S*.dxf") DO (
echo Converting %%~nxF to PDF, size %%S
SET %%S=!%%S! "%%~pF%%~nF.pdf"
"C:\Program Files\AutoDWG\AutoDWG DWG to PDF Converter\d2p.exe" /InFile %%~fF /OutFile %%~nF.pdf /Watermark %~dp0%%Swatermark.wdf /InConfigFile %~dp0%%S.ddp
)
echo:
echo Combining %%Ss. . .
pdftk !%%S! cat output "%filepath%\print\%%Ss.pdf"
echo Combined
echo:
)
EDIT: I'm running this on 32-bit Windows XP. Does this have anything to do with this thread? I will investigate when I get home.
EDIT 2: I've now figured out what the problem is. When I have several files with the same beginning characters, the 8.3 short names contain a hexadecimal number, which may match one of the letters I'm searching for. How can I discard short name matches in my for loop?
Your link to the Strange Windows DIR command behavior thread seems to be a good thought. From RBerteig's thorough answer: Wild cards at the command prompt are matched against both the long file name and the short "8.3" name if one is present.... Try next approach:
SETLOCAL enableextensions enabledelayedexpansion
:::
pushd %filepath%
FOR %%F in ("*.dxf") DO (
set "fname=%%~nF"
set "fmatch="
set "char04=!fname:~3,1!"
set "char10=!fname:~9,1!"
if /I "!char04!"=="T" (
FOR %%S in (A B C D) do if /I "!char10!"=="%%S" set "fmatch=!fname!"
)
if defined fmatch (
echo Converting %%~nxF to PDF, size !char10!
rem another stuff here
)
)
popd

How to rename and add incrementing number suffix on multiple files in Batch Script?

I have 500 files coming in and I need to first check if any file(s) exist then rename all of them regardless of what their filename is (the files are named in a different language).
No need to process them in any order.
Rename:
1. “¦X¼d¬f-20110703-¦+¦dñHÑ-ª-¦=¬¦.xls”
2. “¦X¼d¬f-20110707-¦+¡¦-+¡8.xls”
3. “¦X¼d¬f-20110707-¦+¡¦ñj¦«.xls”
4. “¦X¼d¬f-20110708-¦+¡¦¬M¼n.xls”
5. “¦X¼d¬f-20110713-¦d¼O¼n¦hÑP.xls”
.
.
.
500
To:
“TWN_CH_INV_VISIT_FORM_01.xls”
“TWN_CH_INV_VISIT_FORM_02.xls”
“TWN_CH_INV_VISIT_FORM_03.xls”
“TWN_CH_INV_VISIT_FORM_04.xls”
“TWN_CH_INV_VISIT_FORM_05.xls”
.
.
.
“TWN_CH_INV_VISIT_FORM_500.xls”
Hope you could help me on this one. I’ve been trying to do this for weeks.
a simple FOR with a count (SET /A) should do what you need.
setlocal enabledelayedexpansion
SET /A COUNT=0
FOR %%A IN (*.xls) DO (
SET /A COUNT+=1
REN "%%A" "TWN_CH_INV_VIST_FORM_!COUNT!.xls"
)
See HELP FOR and HELP SET
This is a deceptively difficult question to solve.
The 5 year old PA answer has a few problems.
1) The FOR loop begins iterating without buffering the entire directory tree, so it has the potential to rename a file that has already been renamed. I believe that is why the 7 file is missing within r0mmel's comment.
2) Delayed expansion occurs after for variables are expanded, so the file name will be corrupted and the rename will fail if the name contains a ! character.
3) A rename can fail if there already exists a TWN_CH_INV_VIST_FORM_n.xls file with the same number.
At first I thought I could solve the problem using the following:
#echo off
for /f "delims=: tokens=1*" %%A in (
'dir /b *.xls ^| findstr /n "^"'
) do ren "%%B" "TWN_CH_INV_VIST_FORM_%%A.xls.new"
ren *.txt.new *.
I use DIR /B to list the files, and pipe that result to FINDSTR to prefix each file name with a line number, followed by a colon.
I then use FOR /F to iterate and parse the results into the number and the file name. FOR /F buffers the entire result before iterating, so I don't need to worry about renaming the same file twice.
I first give the renamed files a .xls.new "extension", just in case your directory already has files that meet the TWN_CH_INV_VIST_FORM_n.xls pattern. You don't want any name collisions. The final REN command then simply removes the .new extension to leave the desired .xls.
BUT, I just noticed that the original file names have lots of weird characters that could involve unicode that is not in the current code page. FOR /F does not play well with unicode.
There is one other minor issue in that the above does not pad the number to a fixed width. (this could have been solved easily enough)
So at this point it is time to break out my JREN.BAT regular expression renaming utility. It is pure script (hybrid batch / JScript) that runs natively on any Windows machine from XP onward. It has a built in facility to incorporate a fixed width incrementing number in the new name, and it works fine with unicode. I still temporarily give the new name the ".xls.new" extension to avoid any name collisions.
#echo off
call jren "^.*" "'TWN_CH_INV_VIST_FORM_'+$n+'.xls.new'" /j /npad 3 /fm *.xls
ren *.xls.new *.
I chose to pad the incrementing number to 3 digits instead of 2 because the OP said there could be 500 files.
Full documentation for JREN.BAT is available from the command line via jren /?, or jren /?? if you want paged output.

What does this batch file code do?

What does this bat code do?
for /f %%i in ('dir /b Client\Javascript\*_min.js') do (
set n=%%~ni
set t=!n:~0,-4!
cp Client\Javascript\%%i build\Client\Javascript\!t!.js
)
What does %%~ni,~n:~0,-4!,%%i,!t! mean?
Keep in mind that in batch files, you need to escape percentage signs unless you're referring to arguments given to the batch file. Once you remove those, you get
for /f %i in ('dir /b Client\Javascript\*_min.js') do (
set n=%~ni
set t=!n:~0,-4!
cp Client\Javascript\%i build\Client\Javascript\!t!.js
)
%i is the declaration of a variable used to place the current file for has found. %~ni extracts the filename portion of %i. !n:~0,-4! uses delayed expansion to remove the last four characters from %n% (set in the previous line) !t! is simply delayed expansion of the %t% variable set in the previous line.
Delayed expansion is used because otherwise, the variables will be substituted as soon as the line is encountered, and future iterations will not re-expand the variable.
for /f %%i in ('dir /b Client\Javascript\*_min.js') do (
Iterate over every file in the Client\Javascript folder that match "*_min.js". Thedircommand andfor /f` are totally unneeded here, though and only complicate things, especially when file names contain spaces, commas and the like. A more robust and simpler alternative would be
for %%i in (Client\Javascript\*_min.js) do (
But that's just beside the point. People tend to write unelegant batch files sometimes, ignoring the pitfalls and common errors. That's just one example of that.
set n=%%~ni
Creates a variable n, containing the file name (without any directory information or extension) of the file currently processed. We remember that the for statement iterates over every file it finds. With this line starts what it does with those files.
set t=!n:~0,-4!
Creates a second variable, t, containing everything but the last four characters of the file name. This essentially strips away the "_min"
cp Client\Javascript\%%i build\Client\Javascript\!t!.js
Finally, this copies the original file to the directory build\Client\Javascript with the new name, just constructed. So a file like Client\Javascript\foo_min.js will be copied to Client\Javascript\foo.js. The !t! here is just a delayed-evaluated environment variable. More on that below. Here it should suffice that it just inserts the contents of said variable at that point in the line.
Again, bad practice here that will break in numerous interesting ways:
cp is not a command on Windows so this batch will assume cygwin, GNUWin32 or similar things installed. I tend to avoid having too many unneeded dependencies and stick to what Windows provides; in this case the copy command. Two bytes won't kill anyone here, I think.
No quotes are around either argument. Leads to interesting results when spaces start appearing in the file name. Not good, either.
As for why delayed expansion was used (! instead of % surrounding the variables: The for command consists of everything in the block delimited by parentheses here as well. The entire block is parsed at once and normal variable expansion takes place when a line/command is parsed. That would mean that every variable in the block would be evaluated before the loop even runs, leaving just the following:
for /f %%i in ('dir /b Client\Javascript\*_min.js') do (
set n=%%~ni
set t=
cp Client\Javascript\%%i build\Client\Javascript\.js
)
which is certainly not what you want in this case.
Delayed expansion is always needed when creating and using variables in a loop such as this. A workaround not needing delayed expansion would be to offload the loop interior into a subroutine:
for /f %%i in ('dir /b Client\Javascript\*_min.js') do call :process "%%i"
goto :eof
:process
set n=%~n1
set t=%n:0,-4%
copy "Client\Javascript\%~1" "build\Client\Javascript\%t%.js"
goto :eof
Since the subroutine is not a single "block" (something delimited by parentheses) it will be parsed line by line as usual. Therefore it's safe to use normal expansion instead of delayed expansion here.
A complete help for the FOR command can be found on the Microsoft TechNet site. See here for more information on delayed expansion :
// Pseudo code
for each file named *_min.js in the specified directory
n is set to the file name (*_min)
t is set to the file name, excluding the last 4 characters (*)
the file is copied and renamed t.js to the specified directory
%~ni expands to just the filename part of i.
!n:~0,-4! expands to all but the last four characters of n.
In general, help for at the command prompt will give an overview of the multitude of ways for can expand variables these days.

Resources