Is this possible with Batch Scripting?

Status
This thread has been Locked and is not open to further replies. Please start a New Thread if you're having a similar issue. View our Welcome Guide to learn how to use this site.

bassmadrigal

Thread Starter
Joined
Dec 16, 2009
Messages
65
I am wanting to create a script to help simplify my life at work. Everyday we have to take between 15-50 pdf files and sort them into folders based off their filename. The filename is 12-13 characters long. The standard format is ABCABCABC123.pdf. Basically, I could care less about the first 9 characters of the filename. The key is the last 3-4 characters. They will be the ordinal date (the number of days from January 1st) with the possiblility of an 'a' after it (ie ABCABCABC123a.pdf). I want to somehow extract the number (ie ordinal date) from the filename so I can move it into a particular folder. The 'a' means it needs to go into a different folder structure altoghether.

Initially I thought this would be a fairly straightforward script after a bit of googling, but it has proven otherwise. I am still not used to the limited functionality of batch scripting vs bash shell scripting. It seems there is no windows equivalent for sed or cut (without installing software, which I don't have access to do on my works computer). Which seems to make what I want to do impossible

I am not wanting someone to create a script for me (I would rather learn the code myself), but I am wondering if it is even possible? I don't want to spend a ton of time trying to figure out how to do things, just to find out it is impossible. If it is possible, could you post the commands that I can use and maybe a possible resource to read up on it?

Thanks
Jeremy
 

Squashman

Retired Trusted Advisor
Joined
Apr 4, 2003
Messages
19,786
I believe it can be done. I was a bash shell scripting guy myself many years ago and it took me a while to figure out. If you have never done batch you might as well just start learning PowerShell instead. It is Microsoft's replacement for the cmd prompt and batch. It has a ton of more options

While it is true that there is no direct one to one relationship to SED and CUT in batch, it does come close. You can't really do regular expression replacing like SED can but you can do string replacement (SED) and substrings(CUT) wit the SET statement.

So to cut something from a variable.
Code:
H:\>set _Temp1=0123456789

H:\>set _Temp2=%_Temp1:~0,5%

H:\>echo %_Temp2%
01234

H:\>
And you can do string replacement similar to sed but you can't use regular expressions

Code:
H:\>set _Temp3=%_Temp1:0=1%

H:\>echo %_Temp3%
1123456789

H:\>
 

bassmadrigal

Thread Starter
Joined
Dec 16, 2009
Messages
65
Awesome. That is giving me some great footing to start on. I was thinking that the code you used in the examples below was only for space delimited strings (looking back, I am not sure why I came up with that idea).

As far as using powershell, I remember reading about that back when Vista was still considered Longhorn and they were going to implement it. Unfortunately though, the systems I use at work will not allow me to install software. I am limited to batch scripting.

And I normally don't do any batch scripting except for this and one other script at work. At home, I am running Slackware, so I can use bash.

One other quick question. Do you know of any easy way to count the number of characters in a string (ie, count the length of the filename)? There are ways around it if I can't count that, but it would simplify things with a couple of if/else statements.

Thanks again!!
Jeremy
 

Squashman

Retired Trusted Advisor
Joined
Apr 4, 2003
Messages
19,786
Why do you need to know the length. If the numbers are the last part of the filename you can just pull the last 3 or 4 characters of the filename.
Code:
H:\>set _Temp4=%_Temp1:~-4,4%

H:\>echo %_Temp4%
6789
 

Squashman

Retired Trusted Advisor
Joined
Apr 4, 2003
Messages
19,786
The second example in that link works just fine and is probably easier to understand. I think it will run faster as well because we are using smaller strings. If you were using variables that were a couple hundred characters in length then the divide and conquer method would probably be better.

I don't necessarily like his use of single character variable names. It can get confusing because For Loops use single characters for their variables in batch files.
 
Joined
Aug 7, 2007
Messages
9,028
I'm assuming the ordinal date is 001-366. Remember that numbers starting with a zero are treated as base 8, so you can get some strange results when comparing numbers, or get errors if trying to do math with them.
What I would do is check the last character of the filename. you don't need to know the length to do this. If not a number, then the a is present.
Then extract and convert the date to a useable number that won't be seen as octal. Again, you don't need to know the length to do this:
Set _Filedate=%_FileName:~-4,-1% will extract the 3 digit date and discard the ending a.
My first thought is to extract and convert the dates like this:
Dates without the a will be 1001-1366
Dates with the a will be 10010-13660
all done in one If Else statement, easy to tell apart, and easy to convert back to the date.

Or you can set a flag to show whether or not the a is present.

If you really want to get the length of a string, theres the two routines Squashman linked.

You can just count the characters. Slow for long strings
The divide by two is faster for strings longer than 16 characters.
Here's a 3rd method that I think is fastest for strings less than 150 characters or so:
Code:
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:_GetLength
:: Gets the length of a passed variable
:: Arguments : "string" rvar
:: Returns   : Length of string in specified return var
:: Usage
:: Call :_GetLength "%str%" rvar
::    "%str%"    : String to find length of. Must be quoted if contains spaces
::    rvar        : Name of variable to be used to return the length
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
SetLocal
Set _Len=0
Set _Str=%~1
If Not Defined _Str Goto :EOF
Set _Str=%_Str:"=.%987654321
:_Loop
If Not "%_Str:~18%"=="" Set _Str=%_Str:~9%& Set /A _Len+=9& Goto _Loop
Set _Num=%_Str:~9,1%
Set /A _Len=_Len+_Num
EndLocal&Set %2=%_Len%&Goto :EOF
 

bassmadrigal

Thread Starter
Joined
Dec 16, 2009
Messages
65
Why do you need to know the length. If the numbers are the last part of the filename you can just pull the last 3 or 4 characters of the filename.
Code:
H:\>set _Temp4=%_Temp1:~-4,4%

H:\>echo %_Temp4%
6789
Basically I was going to use the string length to determine if it had the 'a' at the end.

How we are currently doing this, is we sort each file based on the ordinal date (I needed the string extracted so I could move it into the proper folder). Then if it was 13 characters (well technically 17 with the period and the extension) it would put it into the LTF directories. I was originally thinking of just doing a simple if $char = 13 then move it to ltf elseif $char = 12 then move it to stf else do nothing (there will be files in there that do not match up to either naming convention).

Code:
FY10
->STF
  ->001
    ->ABCABCABC001
      ->ABCABCABC001.pdf
    ->ABCABCABB001
    ->ABCABCAAA001
  ->002
  ->003
->LTF
  ->001
    ->ABCABCABC001a
      ->ABCABCABC001a.pdf
    ->ABCABCABB001a
    ->ABCABCAAA001a
  ->002
  ->003
So I may have to pad the variable with zeros, depending on how they are interpreted (trial and error when I get back to work), but it shouldn't be too difficult. But there won't be any math done on them, so I am hoping it keeps it as char. I suppose to determine if the 'a' is present, I can just extract the 13th character and check to see if it is an 'a' (those will be the longest filenames in the directory). Otherwise if it is a period, I move it to the STF folder. And if it is anything else, it just stays in the directory.

I appreciate all the feedback, and I am looking forward to digging back into this at work tomorrow.
Jeremy
 
Joined
Sep 4, 2003
Messages
4,916
This can also be done using Visual Basic scripting. Let me know if you want to go this route.

Rollin
 

bassmadrigal

Thread Starter
Joined
Dec 16, 2009
Messages
65
This can also be done using Visual Basic scripting. Let me know if you want to go this route.

Rollin
I don't have administrator rights on the computer to install visual basic (if it is even needed). But I am fairly unfamiliar with this language, and it may be able to be programmed directly in the cmd window. I just don't know.

OK, so I got quite a bit taken care of today while at work. I had a few hiccups, like figuring out that variables within a for statement need '!' around them rather than '%' (still not sure why, but oh well).

But I am stumped on one thing. There is a possibility of multiple files having the same name (obviously not in the directory that I am starting from), so I am trying to figure out how to have it check if the file exists already and then have it change the name so it can store it properly. Ideally I would use a while loop, but they don't exist in batch programming, so I figured I could do it with some simple labels and goto statements with an if statement. However, it isn't working like I had hoped.

My makeshift while loop works properly, but after it exits, it decides to leave the whole for loop. Here is the full script:

Code:
@echo off
SETLOCAL EnableDelayedExpansion
set _filename=
set _ord=
for %%f in (*.pdf) do (
 set _filename=%%f
 set _ord=!_filename:~9,3!
 set _ltf=!_filename:~12,1!
 set _folder=!_filename:~0,12!
 set _extra=
 if !_ltf! == a (
  if NOT EXIST LTF\!_ord!\!_folder!\ (
   md LTF\!_ord!\!_folder!\
  )
  set _count=0
  :WhileLTF
  if exist LTF\!_ord!\!_folder!\!_folder!!_extra!.pdf (
   set /a count = !count! + 1
   set _extra=-!count!
   echo !count!
   goto WhileLTF
  )
  copy !_filename! LTF\!_ord!\!_folder!\!_folder!!extra!.pdf
 ) else (
 if !_ltf! == . (
  if NOT EXIST STF\!_ord!\!_folder!\ (
   md STF\!_ord!\!_folder!\
  )
  set _count=0
  :WhileSTF
  if exist STF\!_ord!\!_folder!\!_folder!!_extra!.pdf (
   set /a count = !count! + 1
   set _extra=-!count!
   echo !count!
   goto WhileSTF
  )
   copy !_filename! STF\!_ord!\!_folder!\!_folder!.pdf
 )
 )
)
Hopefully this makes some sense. I can't figure out why this is happening, and I haven't had much luck searching google (although I may not know the proper search terms). I am really close to this, and I just can't figure out why it doesn't work.

As always, any help would be greatly appreciated.
Jeremy
 

Squashman

Retired Trusted Advisor
Joined
Apr 4, 2003
Messages
19,786
My makeshift while loop works properly, but after it exits, it decides to leave the whole for loop. Here is the full script:
Use the CALL command instead of GOTO. Goto breaks out of the loop. CALL will jump to a label or another batch file and return to the same spot it left off.
 

Squashman

Retired Trusted Advisor
Joined
Apr 4, 2003
Messages
19,786
Use the CALL command instead of GOTO. Goto breaks out of the loop. CALL will jump to a label or another batch file and return to the same spot it left off.
I should elaborate on that. Any functions that work independent of main program should be moved outside of the for loop. Move all your functions (ie labels) to the bottom of your batch file and outside of the For Loop.

Also...
You are declaring the variable _count but incrementing the variable count. You should also use different variables for your counting for the LTF and STF.
 

Squashman

Retired Trusted Advisor
Joined
Apr 4, 2003
Messages
19,786
In your example above you are using the "a" at the end of the file name as part of the folder name. Your batch file is not using the "a" in the folder name.

If you set a variable to equal STF or LTF based on the 13 position being an "a" or "." you could shorten up your batch file by alot!!! You wouldn't need two separate if loops and gotos.

Your second copy statement isn't using the EXTRA varaible for the copy.
 

Squashman

Retired Trusted Advisor
Joined
Apr 4, 2003
Messages
19,786
Well I am going to post my code first but I like what TheOutcaste is going to post after me. I have not tested this. All I did was condense and fix some of your code.
Code:
@echo off
SETLOCAL EnableDelayedExpansion
set _filename=
set _ord=
for /f "tokens=*" %%F in ('dir /a-d /b *.pdf') do (
	set _filename=%%F
	set _folder=%%~nF
	set _ord=!_filename:~9,3!
	set _ltf=!_filename:~12,1!
	set _extra=
	set _Dsub=
	if !_ltf! == a set _Dsub=LTF
	if !_ltf! == . set _Dsub=STF
	If Not !_Dsub!=="" (
		If NOT EXIST !_Dsub!\!_ord!\!_folder!\ md !_Dsub!\!_ord!\!_folder!\
		Call :COPYFILE
	)
)
goto :EOF

:COPYFILE
set _extra=
set _count=0
:Loop
  if exist !_Dsub!\!_ord!\!_folder!\!_folder!!_extra!.pdf (
   set /a _count+=1
   set _extra=-!_count!
   echo !_count!
   goto Loop
  )
copy !_filename! !_Dsub!\!_ord!\!_folder!\!_folder!!extra!.pdf
 
Status
This thread has been Locked and is not open to further replies. Please start a New Thread if you're having a similar issue. View our Welcome Guide to learn how to use this site.

Users Who Are Viewing This Thread (Users: 0, Guests: 1)

As Seen On
As Seen On...

Welcome to Tech Support Guy!

Are you looking for the solution to your computer problem? Join our site today to ask your question. This site is completely free -- paid for by advertisers and donations.

If you're not already familiar with forums, watch our Welcome Guide to get started.

Join over 807,865 other people just like you!

Latest posts

Members online

Top