# Solved: Batch File - For Loop Counting Characters within XCopy



## scrfix

*1st Object:*
Count the number of characters in a pathway for XCopy.

I have figured a way out to do this. It is the execution I need help with. Mine keeps crashing on me.

*2nd Object:*
This is just an if/else statement so it shouldn't be hard. If I have issues after this resolution I will bring it up.

*Notes:*
The below code is just a snippet from my code and I have removed most Variables to make things easier to read. This program just hangs. I know the counting works by itself. I know the xcopy works by iteself. What am I doing wrong?



Code:


@echo off
set MyVar=xcopy /L /Y C:\Windows G:\Windows
for /f %%a in ('%MyVar%') do (
set #=%MyVar%
set length=0
:loop
if defined # (set #=%#:~1%&set /A length += 1&goto loop)
echo %MyVar% is %length% characters long!
)

Thank You for any insight and help.

Wayne


----------



## TheOutcaste

Hi Wayne.

A few things
Best to comment out the echo off statements so you can see what it is doing. That can help you find things.
What I do is use this:
@Echo %dgb%Off

To turn off echo I just type set dbg=1, to turn it on type Set dbg=0 (I actually have two batch files with the appropriate set statement, and just type dbon and dboff)

1:
for /f %%a in ('%MyVar%') do (
You have not specified the delimiters, so the default of space and tab will be used. Any paths/filenames with spaces will be truncated at the first space. So use this:
for /f "delims=" %%a in ('%MyVar%') do (
2:
set #=%MyVar%
echo %MyVar% is %length% characters long!
You are setting # equal to the Xcopy command. This should be the loop variable. Same with the Echo statement:
Set #=%%a
echo %%a is %length% characters long!
2:
set #=%#:~1%
If inside a loop you must use delayed expansion. When %#:~1% is expanded, # is actually undefined so this line becomes set #=~1
The ~ vanishes because it's not escaped, so you''ll get these three lines for the output:


Code:


C:\test1>if defined # (set #=1  & set /A length += 1  & goto loop )

C:\test1>if defined # (set #=  & set /A length += 1  & goto loop )

C:\test1>if defined # (set #=~1  & set /A length += 1  & goto loop )

Length will actually get set to three as the goto statement actually ends the For loop, so delayed expansion is not needed after the first one, but by then the damage is done.
3:
You can't use goto inside a For statement. The goto ends the For loop, even if you goto a label inside the for loop. So you need to call a routine to get the length.

Once the For loop is ended by goto, you don't need delayed expansion, so the # variable will be processed, but the 1st time through sets it to 1. Not quite sure why it isn't seen as undefined after the 2nd line though
4:
If G:\Windows doesn't exist, it will hang as Xcopy is asking if G:\Windows is a file or directory and waits for your response. This question is never output to the screen though, it's output to the loop variable. Just need to specify the /I switch on the Xcopy command.

Give this a try. It uses a more efficient length routine, it checks blocks of 9 characters instead of every character so is a bit faster.


Code:


@Echo %dbg%Off
SetLocal EnableDelayedExpansion
Set MyVar=xcopy /L /I /Y C:\Windows G:\Windows
For /F "delims=" %%a In ('%MyVar%') Do (
Call :_GetLength "%%a" _Length
Echo %%a is !_Length! characters long!
)
Goto :EOF
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Subroutines
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:_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

HTH

Jerry


----------



## TheOutcaste

Sorry about that, didn't realize I'd hit send. Also wanted to add that if you have a path longer than 250 or so, xcopy may give an error will quit since the /C switch isn't specified. And the length of the long path won't be calculated.


----------



## scrfix

Jerry,

Thank you for that explanation. That tells me exactly where I went wrong. It also explains to me why these two items work perfectly separate but I could not get them to work together.

Now, if I remember from my last quesiton, the :eof is an internal function necessary to break out of for loops or was that just eof with the :?

Thank you for the tip on the /C. This whole test is for exactly that.

Let me ask you this. If I have a pathway that is 300 characters long. (Just ran into this and it errored out, of course.) The file was some HP random file located in the drivers directory in system32 something like HP_ASDFTRRADFFGASDWFSTGSDR... etc etc etc etc and it had special characters in it as well. What I am attempting to do is to count that file or pathway to the file. If that file or pathway to the file reaches 241 or More (I forget off the top of my head how to write equal to or greater than in batch) characters, skip the file, write to a log file stating that the specific pathway and the actual fille were not able to be copied and if it is 240 or LSS characters go ahead and copy it.

With the /C the file that I am talking about will it do that or will it error out.

Here is what I am running into. Let's take that example from above:
1. The xcopy starts to copy it hits that file and stops copying that line of code and goes to the next line in the batch. This is bad because it stops copying altogether. Let's say that line in the batch is (xcopy /C C:/Windows G:/Windows) [I have left out all other switches just for example sake]. This means I have have of the drivers directory saved and the other half does not get copied.

Here is what I need:
1. I need for it to just skip that one file and continue copying that line in the batch file.

I could probably do this:
1. I could store a portion of the name (The first 20 characters should be enough) name of each and every file being copied into a variable (%namestore%) and if Error Level 1 (I think is the error that is returned) occurs while copying then (set filename=%namestore%) and let it error out however restart the copying loop from the beginning and add exclude:%filename% that xcopy. I use a time stamp to copy so it will not start copying from the beginning of every file again. This way when it restarts, it will start back at any files that have not been copied. When it gets to the super log file that it cannot copy it skips it. Now the issue with that is that is great for those super long file names as mentioned above however it does nothing for those people that save their documents within folders within folders within folders withing folders and utilize names such as "My super really extra long no reason for it being this long except that I want to look cool folder name". When they have 10 of those stored within each other then there is a problem or would there be. With the described above, it should still store the name into the variable and then write to the log file. I could then create a batch, put it in the start up of the computer to warn them that they had errors and then auto delete that batch once they push any key on their keyboard.

One item at a time I guess.

Does it sound like we have to go that far or is there another way of performing what I want to perform.
I just want to skip the line item that is being copied if it is over 240 characters and continue copying the rest of the files.

Thanks,

Wayne

*Computer Repair*
*Gift Baskets*
*Modular Gold Plants*


----------



## Squashman

scrfix said:


> Jerry,
> 
> Now, if I remember from my last quesiton, the :eof is an internal function necessary to break out of for loops or was that just eof with the :?


Use GOTO :EOF will basically go the end of a batch file or end a subroutine. So if you called a subroutine and want to end it you can goto :eof. If you want to terminate your batch file you can use goto :eof.

CMD processor understands the label :EOF as being the end of a process and terminates it.


----------



## Squashman

Two alternate options for finding string length.
http://cypressor.twoday.net/stories/4479271/


----------



## scrfix

Squashman,

Thanks for those pages. I believe the second one is exactly what I am looking for but I am having a hell of a time trying to read this thing.



Code:


@echo off
:: Calculates the string length of the first parameter.
:: PARAM
:: 1 String to return length of
:: RESULT Integer length of tring
:STRLEN
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
SET I=-1
SET T=%~1
:STRLEN_LOOP
SET /A I += 1
IF NOT "!T!" == "" IF NOT "!T!" == "!T:~0,%I%!" (
    GOTO :STRLEN_LOOP
)
ENDLOCAL & SET RESULT=%I%
GOTO :EOF
@pause

*Note:*
I added the @echo off and the @pause to the code.

Breaking this down:
:STRLEN - Label for STRLEN (No problem)

SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION - (I understand how to read this. Just not sure what it does or why it is important.)

SET I=-1 - Set the variable I to negative one?

SET T=%~1 - Set the variable T to (I have no idea)

:STRLEN_LOOP - Label for STRLEN_LOOP (No problem)

SET /A I += 1 - Set the variable I to be an expression and += (no clue) to 1

IF NOT "!T!" == "" IF NOT "!T!" == "!T:~0,%I%!" ( - I have no clue what this line says. I do not know why there are 2 IF NOT "!T!" nor do I understand why there is two !'s on either side of the T.

GOTO :STRLEN_LOOP - After Verification go back to STRLEN_LOOP and start over again. (No problem)
)

ENDLOCAL & SET RESULT=%I% - I am unsure why we are setting the result to = to %I%.

GOTO :EOF - Leave the batch file

I am also unsure as to where they are getting string length or how to incorporate string length into this.

Any help would be wonderful. I believe from reading this that this is exactly what I am looking for. It looks at every character and counts it individually until finished. If I can put an IF statement within the loop to say If the characters go over 240, skip this file and write to a log file and if not go ahead and copy that file, this resolves this issue. I just have to learn how to read this and how to incorporate a string into it.

Wayne


----------



## TheOutcaste

I see Squashman beat me to it while I've been playing with long path names, but here's my take on the :EOF

:EOF is a predefined label that is always at the end of the file.
So this exits the current script, same as using *Exit /B*
If you are familiar with BASIC, *Goto :EOF* is equivalent to *RETURN*

The /C switch will continue on copy errors like Access Denied, Sharing Violations (file in use), or File not found (deleted/moved/renamed before xcopy got to it).
It will not continue when it hits a long path. You get an Insufficient memory error and Xcopy quits. So you can't use it to find long paths.

Easiest solution is to use Robocopy as I mentioned in post 9 in the previous thread, which doesn't have that path length limitation. It's included with Vista, and can be downloaded for XP/Win2K as part of the Win2K3 Resource Kit. (This Vista version won't work on XP)
The Resource kit is an 11.8 MiB download, but you only need to download it once. If you have multiple PCs, you can have the batch file check to see if robocopy.exe exists on the system, and if not copy it to the Windows\system32 folder from a network share, robocopy.exe is only 78 KiB. Or copy it at the same time as the batch file is copied to the system

If you have to use xcopy, there's a couple of options. All depends on if you have multiple nested folders, or just a couple levels of folders, and a few files with a very long name.
You can use Dir to do a directory list. It will send this error message to STDERR when it finds a long path:
*The directory name <path listed> is too long.*
You can run a DIR command and check for that error message.
*Dir /B /S "C:\Path\*.*" >Dirlist.txt 2>LongPath.txt*

You can then create an exclude file to exclude that path. However, this may not find some long paths.
I created a group of nested folders, then renamed the top level folder to make the path too long. With just the right length, Dir will not give an error, nor will it list all the files/folders. In my test, it will list the 2nd to last folder, but not the files it contains, or the folder. And Xcopy doesn't cause an error, but the files in that 2nd to last folder and it's subfolder are not copied.
Added a couple more characters, and Dir now gives an error, but Xcopy still runs, but again doesn't copy the last two folders.

I then created the longest filename I could in the root of the drive, which is 256 characters (260 counting the C:\ at the front, and the <NULL> at the end)
Created a folder named New Folder, used Robocopy to move the file.
Dir will list the file, and Xcopy gives an error.
Moved it to C:\Test Dir\New Folder, and Dir will list it. Xcopy won't copy, nor does it give an error. Tried copying using the short file name, and xcopy doesn't give an error, but still won't copy the file.
You can't exclude the filename. Even trying a partial match of the first few characters fails, because Xcopy has to read the filename before it can try to match it to what is in the exclude file.

Renaming using the short name also fails with this error:
The system cannot find the path specified.

Then the Command Prompt generates an error and closes. The dump file indicates this:
*Stack buffer overflow - code c0000409
*
So, you can check to make sure Xcopy won't fail, but you can't guarantee that you haven't missed some files.
You use Dir to find any long paths, then exclude the path so Xcopy won't cause an error.
You then check the total length of the files Dir finds, and exclude those paths.
Then process the list to create a list of files in the excluded folders that you can copy.
You then individually copy everything in the excluded folders that is not too long, or create a share at the lowest level folder and then repeat all the above starting from that share.

But, if the filename itself is over 256 characters, there is nothing you can do with it with a batch file. I'm not even sure if Dir would be able to list it if it's too long.
You can't create a name longer than 256 characters using the Command Prompt or Explorer, but any program can by using the Windows API, which allows up to 32,767 characters in the path.

Much much easier to just copy Robocopy to each system.
This is what I've got so far. Still need to go through the Will Copy list and remove any files that are in excluded paths and add them to the Excludedlist file, then decide how to copy them.


Code:


@Echo Off
SetLocal EnableDelayedExpansion
Set _SrcFld=C:\Test Dir
Set _DstFld=D:\Test Dir
PushD %_SrcFld%
Set _TPath=C:\Test1\
If Exist "%Temp%\*.}x{" Del /F "%Temp%\*.}x{"
Dir /B /S /A-D >"%Temp%\Dirlist.}x{" 2>"%Temp%\LongPath.}x{"
For %%I In ("%Temp%\LongPath.}x{") Do If %%~zI==0 Goto _NoLong
For /F "Usebackq Delims=" %%I In ("%Temp%\LongPath.}x{") Do (
Set _Dir=%%I
>>"%Temp%\Exclude.}x{" Echo !_Dir:~19,-13!
)
:_NoLong
:: Now check dirlist for names/paths over 256 characters
For /F "Usebackq Delims=" %%I In ("%Temp%\Dirlist.}x{") Do (
Call :_StrLen "%%I"
If !_Result! LSS 256 (
>>"%Temp%\WillCopy.}x{" Echo %%I
) Else (
>>"%Temp%\Exclude.}x{" Echo %%~dpI
>>"%Temp%\excludedlist.}x{" Echo %%I
)
)
Copy "%Temp%\Exclude.}x{" C:\Exclude.}x{>Nul
Xcopy /CEHIRYL "%_SrcFld%" "%_DstFld%" /Exclude:C:\Exclude.}x{
Del /F C:\Exclude.}x{
:_End
PopD
::If Exist "%Temp%\*.}x{" Del /F "%Temp%\*.}x{"
EndLocal
Goto :EOF
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: This routine does a divide by 2 type check.
:: Faster than counting each character for strings longer than 16
:: Returns the string length.
:: PARAM
:: 1 String to get length of
:: RETURN Integer string length in _Result

:_StrLen
SetLocal EnableExtensions EnableDelayedExpansion
Set _Str="%~1"& Set _Str=!_Str:~1,-1!
Set _Len=0
:: Max string length is 8191, see http://support.microsoft.com/kb/830473
Set _Pos=8192
:_StrLenLoop
Set /A _Pos /= 2
If NOT "!_Str!" == "" (
  If NOT "!_Str:~%_Pos%,1!" == "" (
	Set /A _Len += %_Pos% + 1
	:: work on rest, that is after _Pos+1
	Set _Str=!_Str:~%_Pos%!
	Set _Str=!_Str:~1!
  )
Goto :_StrLenLoop
)
EndLocal & Set _Result=%_Len%
Goto :EOF

Jerry


----------



## TheOutcaste

Actually the first one is more efficient for strings longer than 16 characters. I'm not sure how well either will deal with strings that contain any of these: !%^|<>
Check the help for these commands:
Set /?
SetLocal /?
Call /?
For /?
That covers most of what is in the routine.
Also don't turn off echo. Then you can see what the variables expand as for each line, which might make it clearer.
You would call the routine like this:
*Call :STRLEN "what is the length of the string inside these quotes"*
The quotes are requited, and the length of the characters in blue is stored in the variable RESULT


scrfix said:


> Breaking this down:
> :STRLEN - Label for STRLEN (No problem)
> 
> SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION - (I understand how to read this. Just not sure what it does or why it is important.)
> 
> SET I=-1 - Set the variable I to negative one?


Yes, start at -1


scrfix said:


> SET T=%~1 - Set the variable T to (I have no idea)


Sets T equal to the passed parameter, first removing the surrounding quotes


scrfix said:


> :STRLEN_LOOP - Label for STRLEN_LOOP (No problem)
> 
> SET /A I += 1 - Set the variable I to be an expression and += (no clue) to 1


Increment (+=) by 1. To increment by two use +=2 and so on



scrfix said:


> IF NOT "!T!" == "" IF NOT "!T!" == "!T:~0,%I%!" ( - I have no clue what this line says. I do not know why there are 2 IF NOT "!T!" nor do I understand why there is two !'s on either side of the T.


The ! are just like the % except it indicates the expansion of the variable is delayed until the line is actually executed. The Command processor first expands all variables then executes the line. If you change a variable inside a loop, the change will not be seen unless you delay the expansion of the variable until right before the line is executed. In this case though, it's needed so you can specify the length of the string you are extracting.
*%Var:~0,%length%%* doesn't work, you have to use *!Var:~0,%length%!* which requires DelayedExpansion
Might be easier to understand if it's split up a bit.


Code:


IF NOT "!T!" == "" (
    IF NOT "!T!" == "!T:~0,%I%!" (
        GOTO :STRLEN_LOOP
    )
)

If the string (T) is not null, you then do the 2nd If. It compares the string to a substring of length I. If they are not equal, you loop back and increment I
So if the string is ABC, the 1st time though I is 0, you compare:
"ABC" to "" - not equal, loop, increment, I now 1
"ABC" to "A" - not equal, loop, increment, I now 2
"ABC" to "AB" - not equal, loop, increment, I now 3
"ABC" to "ABC" - equal, don't loop, I is the length.



scrfix said:


> GOTO :STRLEN_LOOP - After Verification go back to STRLEN_LOOP and start over again. (No problem)
> )
> 
> ENDLOCAL & SET RESULT=%I% - I am unsure why we are setting the result to = to %I%.


The SetLocal at the beginning means all variables in the routine are local. When the EndLocal is executed, the variables vanish. However, since variables are expanded _before_ the line is actually run the line is really this:
*ENDLOCAL & Set RESULT=3*
The variables are released, then you set result to 3, the length of the string.


scrfix said:


> GOTO :EOF - Leave the batch file


If it's a separate file, Yes, but if it's called as a subroutine, it simply returns to the statement after the CALL.



scrfix said:


> I am also unsure as to where they are getting string length or how to incorporate string length into this.


The String length is the I variable that is being incremented, and is returned in the variable Result.



scrfix said:


> Any help would be wonderful. I believe from reading this that this is exactly what I am looking for. It looks at every character and counts it individually until finished. If I can put an IF statement within the loop to say If the characters go over 240, skip this file and write to a log file and if not go ahead and copy that file, this resolves this issue. I just have to learn how to read this and how to incorporate a string into it.
> 
> Wayne


As long as you can get the file path/file name. Sometimes hard to do as I showed in my previous post.
However you get the path string, you would just use this to check it:


Code:


Call :STRLEN "%pathtocheck%"
If %Result% GTR 240 (
:: Skip this file
) Else (
:: Lenght ok, copy
)

HTH

Jerry


----------



## scrfix

First of all,

Thank you very much for testing all of that and the complete answers.

Second,

Leave it to MS to have a limitation is their software and allow their other software to create items over that limitation. Ahhhhhhhhhhh

If I understand this correctly with XCopy:
1. You cannot grab the path of over 256 characters to be able to put into the count. It will simply error out.

*Question:*
Is there something that I can utilize to grab the pathway all the way to the file that is readily available in XP that is not xcopy and does not have that limitation of 256 Characters?

*Note:*
I agree with you on Robocopy that it would be a lot easier and I am currently looking into how to integrate those into all of the systems and I am in the middle of studying Robocopy for all of the proper switches as well as the bugs within Robocopy. However the script I am writing will be utilized on thousands of machines that I am not always going to be sitting at the helm. On a majority of these XP machines, we are not the IT department for them and the rights to install software have been removed by a group policy from the server. So I am forced to work within the limitations that I am provided. As I understand it, I have to install Robocopy onto the machine. It is not a stand alone program I can utilize from a thumb drive or a CD.

Wayne


----------



## Squashman

Robocopy just needs to exist some where that the user has access to. If all the users have access to a network drive, then put it there. If you can install it locally then put it there.

You could also compile the batch file and robocopy into a single executable. There is a couple of freeware programs out there that do this.


----------



## scrfix

Squashman,

Whoa... hold on... you just opened a new world to me here. I knew that I can the batch into an executable. I have a freeware program that when I convert to exe, the exe crashes. I am not sure if it is:
1. The program I am using to convert.
2. The fact that I am using :: for comment files.
3. Something is not compliant in the code and it is having issues.

You are telling me that I can take Jerry's suggestion of downloading it and combine it into an exe file with the bat file. This I did not know.

This hosts a number of new issues and questions however. The first being that as Jerry mentioned that the Vista version of Robocopy will not run on XP. This means that there are two separate versions of Robocopy. Can I integrate them both into 1 executable? Say I download the XP version of Robocopy and name it xprobocopy.exe and the Vista version of robocopy and rename it to vistarobocopy.exe?

How would that work?
If the bat and both exe's were combined, would that cause any issues or would I be able to in the code distinguish between which version of robocopy to utilize. Would I not need to distinguish, I merely utilize the robocopy and the switches I need for either operating system?

If those can be answered perhaps I can look at that road instead. Otherwise, my question still stands, is there another command that I can utilize in a batch to look at the length of a pathway of a file other than xcopy so we can get around this issue with the 256 character limitation?

Wayne


----------



## Squashman

That bat2.exe utility I use for some of my scripts combine several batch files and executables files into one executable and they work just fine.

This is the one I use.
http://www.f2ko.de/English/b2e/download.php

If you determine the OS version in the batch file you could call a different subroutine to use the appropriate robocopy version.


----------



## scrfix

I have Bat To Exe Converter 1.4. I am attempting to download yours now.

So if I understand this correctly, it will matter what exe's are combined.

Now, out of curiousity to see if we can answer this. I was looking up other functions within XP.

I found 
Copy & Move.

Do either one of those commands have the 256 character limitation that XCopy has?

I also answered a couple of my other questions not related to this by finding a source for bat file commands. What a great website. http://www.ss64.com/nt/

This website just taught me a ton.

I am going to attempt to download the exe files and combine my bat and both robocopy's into 1 exe. If I can do that and still get this to work then I can have 1 file that I run everything from. This would be great.

Wayne


----------



## TheOutcaste

scrfix said:


> Leave it to MS to have a limitation is their software and allow their other software to create items over that limitation. Ahhhhhhhhhhh


All part of progress I guess. In this case, instead of re-working Xcopy, they came up with Robocopy. Doesn't explain why they haven't done something with Explorer.exe to allow the longer paths though.



scrfix said:


> If I understand this correctly with XCopy:
> 1. You cannot grab the path of over 256 characters to be able to put into the count. It will simply error out.


Or in some cases it seems it just pretends it doesn't see it at all



scrfix said:


> *Question:*
> Is there something that I can utilize to grab the pathway all the way to the file that is readily available in XP that is not xcopy and does not have that limitation of 256 Characters?


Dir will find and list some, and will give an error on others. With my test folders I've gotten Dir to list a total path of 511 characters, using a file with a 256 character name. And it will list a path of 270 characters. Might go a few more, it lists that folder, then the following line, which would list the contenets, says the path is too long. I suspect it means that something IN that folder is too long and it can't actually display what it is complaining about. Haven't done enough testing to pin down the exact length.

Done a little testing with VB Script, it doesn't list as much as the DIR command does. I think that you can directly access the Win32 APIs though, so there might be a way using VB Script. It can be done using .Net. Haven't done much testing with WMIC yet either, but I suspect it would also have to access the APIs to deal with long paths.

I've also tried using \\?\ which denotes using unicode, but it doesn't make a difference. (These might be an interesting read:Long Paths in .NET, Part 1 of 3 and File Names, Paths, and Namespaces)
I'm currently at the point where Dir lists 94 files (when I have 100 files), but Xcopy only lists 79. Xcopy doesn't give an error though. Do Dir will find some that Xcopy will miss, but doesn't get them all.
And I haven't figured out how to create a file with a name longer then 256 to test with yet


scrfix said:


> *Note:*
> I agree with you on Robocopy that it would be a lot easier and I am currently looking into how to integrate those into all of the systems and I am in the middle of studying Robocopy for all of the proper switches as well as the bugs within Robocopy. However the script I am writing will be utilized on thousands of machines that I am not always going to be sitting at the helm. On a majority of these XP machines, we are not the IT department for them and the rights to install software have been removed by a group policy from the server. So I am forced to work within the limitations that I am provided. As I understand it, I have to install Robocopy onto the machine. It is not a stand alone program I can utilize from a thumb drive or a CD.
> 
> Wayne


That can be a hassle, but as Squashman has already said, Robocopy actually is a standalone app. If this script will run from a CD/Thumbdrive, you can just include it on the CD/Thumbdrive. Then use *%~dp0Robocopy.exe* from the bath file to run it. (*%0* is the batch file name, and the *~dp* modifiers give just the *d*rive and *p*ath).
If the script is something they will be installing then I'd have to second this:


Squashman said:


> You could also compile the batch file and robocopy into a single executable. There is a couple of freeware programs out there that do this.


I've not done this myself, but Squashman can certainly guide you here.
You could include both the XP and Vista versions of Robocopy (with different names) then have your script check the OS and use the version that is appropriate. The Vista version is 109KiB, XP is 78 KiB.
Odd though, the Win7 Beta Robocopy claims its the same version as Vista (XP*027*, 5.1.10.1*027*) but is only 92.5 KiB).

And here's a few more links:
DOS and VB Scripting Links
Command-line reference A-Z
Using batch parameters
Windows 95/98/ME Batch file Tutorial (Still a good basic reference for WinNT/2K/XP)
Batch File Functions for NT4/2000/XP/2003
Rob van der Woude's Scripting Pages
Microsoft Script Center
Beginners Guides: WindowsXP Command Prompt
Beginners Guides: Understanding and Creating Batch Files

Jerry


----------



## scrfix

Squashman said:


> That bat2.exe utility I use for some of my scripts combine several batch files and executables files into one executable and they work just fine.
> 
> This is the one I use.
> http://www.f2ko.de/English/b2e/download.php
> 
> If you determine the OS version in the batch file you could call a different subroutine to use the appropriate robocopy version.


I downloaded and am using the above. It is much better and easier to use than version 1.4 which is what I had.

However, when I convert over to an EXE it crashes. I have pinpointed it to the following line:
rmdir %temp% /S /Q

I have attempted to write this as:
rmdir "%temp%" /S /Q

It makes no difference. It still crashes.
I believe this is because when run the exe files creates temporary exe files in the temporary directory. I saw there is an option to run from the current directory but that made no difference when compiling.

What are some of the other programs you are aware of that may allow that line after converted?

Thanks,


----------



## TheOutcaste

Have you tried *RmDir /S /Q "%temp%"*
Or use a subfolder in %temp%:
*Set _tmp=%temp%\ScrFixTmp%Random%*
Then *RD /S /Q "%_tmp%"*

Jerry


----------



## scrfix

Jerry,

The first one still crashes.
The next one returns back
The system cannot find the file specified.

It appears the second one just creates a temporary folder and then removes that folder.

It crashes out with the error:
The system cannot find the path specified.
The system cannot find the file specified.


----------



## scrfix

Jerry, Squashman,

I found something that will work just as good. I did some testing and I can accomplish almost the same task with



Code:


del %temp%\* /F /S /Q

This one doesn't crash the exe.


----------



## scrfix

I spoke too soon. That does not work. However I figured out why it doesn't work.

When a batch file "compiler" compiles yours file, it merely encrypts the shell of the file. It creates files in the temp directory that then get wiped out by deleting them and bam crash.

Wayne


----------



## Squashman

Don't use Environmental Variables that the Operating System already uses as your variable names!


----------



## Squashman

I compiled this and it worked just fine.


Code:


set _tmp1=E:\test1
rmdir /S /Q "%_tmp1%"
pause


----------



## scrfix

Boy, I don't know what programming they have in these forums but whever I do a copy and a paste for some code to this quick reply it locks up Internet Explorer tighter than a drum and I end up having to close out and lose everything I typed.

Now, if I copy and paste it to notepad and then from notepad to here it is just fine.

The reason yours works is because you are creating a temp directory and then removing that directory.

What these so called compilers do is to encrypt the shell of the file. When you run the program, it creates a <random-name-here>.tmp that hosts your renamed <random-name-here>.bat file (unencrypted for the world to see)

There are no options to tell it to mark the bat file as hidden or to tell it where to save it. It automatically saves it in the %temp% directory.

I was using the actual system variable %temp% not a variable of my own.

So when you click on that exe to execute your command, it creates a file in the %temp% directory (other compilers do it differently but basically the same). This will be either a hidden temp folder, a temp folder.tmp or the actual bat file all renamed to a random name. It then executes the bat file to run.

Because I am cleaning out the %temp% folder within my bat file before executing my other commands, it then removes the bat file being used to run the program therefore the ending result; crash.

For a test either create the temp folder, store your bat file in the temp folder, then run the rmdir /S /Q on the temp directory and you will see the same effect.


----------



## Squashman

Would be helpful from now on if you could really explain what you are trying to do instead of us guessing all the time. I had to assume you were using an Environmental Variable improperly again because you had been warned about that before.


----------



## Squashman

I can't replicate your problem. I ran my batch file and then looked in the directory of the temp variable and there is nothing with a time stamp or even close to a time when the batch file started. And I still have the batch file running.


----------



## scrfix

Not a problem. I will try to make things clearer however being warned about it before would signify that I now know not to do that.

If the following code was compiled and ran the issue would have been repeated.



Code:


rmdir %temp% /S /Q
pause

There is no sense in inputting 600 lines of code into here when all I am speaking about is 1 line that if ran can be repeated very easily.

I just read your above statement about not being able to repeat the error. Compile and run the following code from the exe file:



Code:


@echo off 
rmdir %temp% /S /Q 
echo succesfully removed temp directory
@pause

It will start and error out with:
The system could not find the specified path.
The system could not find the specified file.
The window will then disappear instead of going to a pause screen.

Now compile and run the following code with the exe file:


Code:


@echo off 
del %temp%\* /F /S /Q
echo succesfully removed temp directory
@pause

It will start to run and then the following error will come up:
Deleted File - pathway\name of bat file.bat
The window will then close without reaching the pause

This is because the compiler as I explained above creates an executable shell around the bat file and then when executed it stores the real unencrypted bat file in the system %temp% directory. So when you are cleaning up the junk files on the computer and you are cleaning the %temp% directory, it removes the files those compilers create. I have attempted 4 different compilers attempting to work like this and they all do the same thing in various formats.

The only way I was able to catch the errors was by precisely timing a print screen. I am sure there are other ways however I captured the errors nevertheless.

I have only tested this on a Vista system.


----------



## Squashman

I changed my original batch file to the %temp% variable and it deleted everything except for files and directories in use by programs that are already running. I am running XP.


----------



## scrfix

If you compile exactly what I posted above, you should get the same errors as described.

If you are running it from a batch, it works just fine.
You have to run it from the exe to repeat the error.


----------



## scrfix

I can always exclude the %temp% directory from being copied altogether with /XD %temp% with robocopy or /EXCLUDE:%temp% in xcopy and then just clean the temp directory at the end of the batch. It is an ugly work-a-round but it will work. Of course this is all theory right now and has not been tested.

Unless anyone has anything better?


----------



## Squashman

scrfix said:


> If you compile exactly what I posted above, you should get the same errors as described.
> 
> If you are running it from a batch, it works just fine.
> You have to run it from the exe to repeat the error.


That is basically what I did and I didn't get any errors.
This is what was in my batch file that I compiled.


Code:


rmdir /S /Q %temp%

Must be a vista issue.


----------



## scrfix

You do not have a pause in there to be able to tell if there are any errors. Since the window automatically closes after it finishes and the window closes upon crashing, it will seem like there are no errors.

Compile the below and run the exe. The pause will never happen.



Code:


rmdir /S /Q %temp%
pause

Now, the weird thing is that when I only use the del function with what I did above, it actually works and gets to the pause but if I have any commands beyond that it crashes. Still the same concept as explained above.


----------



## Squashman

_Posted via Mobile Device_
If it deleted everything in the folder except for files in use what errors do you think I am going to get. you have to remember that the vista cmd prompt does not run with full administrative privileges by default.

I don't need to put a pause into the batch to know whether it worked or not. If it clears out all the folders and files it worked which it has done for me every time. Any remaining files and folders it didn't delete I know are in use with another app I have on my pc.


----------



## scrfix

If it doesn't get to the pause it doesn't work. It won't continue onto the rest of the batch file.

I mentioned above the errors you will get.

I am confused. You asked me to be more clear about what I am asking yet you are not willing to go forward with it when I am?


----------



## TheOutcaste

When you execute the compiled EXE file, it creates a randomly named folder in the Users Temp folder (%temp%), then extracts the batch file it contains into that folder. Any other files you included will be extracted to the Working Directory, which will be this temp folder if you chose Temporary Directory for the Working Directory. The folder is named HHHH.tmp, where H is a hexadecimal number. (Just a FYI, there is no predefined variable for the System temp folder C:\Windows\Temp).

If you delete everything in the %temp% folder, or remove the folder, the program is going to crash, that's just the way it works.

You can use Del to delete all _files_ in %temp% then a For loop to remove all folders _except_ the one containing the extracted file(s). A bit convoluted to exclude the root %temp% folder and the one created, but doable:


Code:


Del /F /Q *.*
For /R "%temp%" %%I In (.) Do If /I Not "%temp%\."=="%%I" If /I Not "%%~sI\"=="%~dps0" RD /Q /S "%%I"

You could also do it this way but there's no guarantee that the names compared in the If statement will consistently be short or long.


Code:


PushD %~dp0
Set _batpath=%CD%
cd ..
Del /F /Q *.*
For /F "Delims=" %%I In ('dir /AD /B') Do If /I NOT "%_batpath%"=="%%~fI" RD /Q /S "%%I"
PopD

Why do you need to clear out the folder before running your program?
If it's to avoid copying the contents as part of a backup, it's best to just exclude the folder using the xcopy/robcopy switch. If not excluded, your batch file and any other files wrapped up with it will be copied, which you may not want to have happen.

If it's to remove temp files _you've_ created, just use a subfolder for your temp files and then delete the subfolder.

If you want to clean out the %temp% folder as part of a system cleaning application, you'd need to create a batch file in another folder (like the System temp folder) and execute it as the last step in your batch file. It would then clear the User temp folder on exit, and then delete itself.

So the last three lines before the file ends would be:


Code:


>%systemroot%\temp\cleantmp.cmd Echo RD /Q /S "%temp%"
>>%systemroot%\temp\cleantmp.cmd Echo Del %systemroot%\temp\cleantmp.cmd
%systemroot%\temp\cleantmp.cmd

HTH

Jerry


----------



## scrfix

Hi Jerry,

Once again, thank you for being precise. I don't know if you read all of my posts however that is what I came up with too. I explained that is what was happening two or three times above. I also mentioned in one of them that I could just exclude and then copy and then at the end of it then delete and who cares at that point if it deleted it.

In one compiler, there is an option to change working directories however it doesn't work. I have wrote the maker but who knows if they will get back to me. They make it sound like months before they get back to you if they get back to you.

I would rather those compilers be a true compile and turn the code into a DLL or something written in C#, etc etc. I guess we live with the limitations we are provided.

I am still checking into the Robocopy switches and other items so I have not switched it from xcopy to robocopy yet. I wanted to take care of this issue first because it didn't matter what command I was going to utilize, it was still going to crash.

I did attempt to utilize /EXCLUDE:%temp% and it crashed.
Is there a better way?



Code:


PushD %~dp0
Set _batpath=%CD%
cd ..
Del /F /Q *.*
For /F "Delims=" %%I In ('dir /AD /B') Do If /I NOT "%_batpath%"=="%%~fI" RD /Q /S "%%I"
PopD

So if I understand this correctly.
*PushD %~dp0 *(Change the directory to the current working directory where the batch is being run from.])
*Set _batpath=%CD% *(Set the variable _batpath equal to the current directory)
*cd ..* (Go up one level)
*Del /F /Q *.** (Silently delete all of the files in that directory however do not touch sub directory files)
*For /F "Delims=" %%I In ('dir /AD /B') Do If /I NOT "%_batpath%"=="%%~fI" RD /Q /S "%%I"* (Run a For Loop, Look for the delimiters ?? in the directory only listings with names only and store each one in the variable %%I then verify that the directory is not equal to the _batpath variable case insensitive [not sure what the ~f in %%~fI does] and if it is not equal to that, then run the command to remove the directory silently.)
*popD* (This changes directory again as I understand it however without a directory following it when I enter it into a command line all it returns is the same directory)

Do I have that correct?

This will work only if 
1. The maker does not change where they store the bat file. If they decide to store it in the program files directory, this would be disasterous.

2. If we are compiling utilizing that compiler. Other comilers store the bat directly in the temp directory without a folder and that would be disasterous as well to delete all of the files and directories before the temp directory.

So we have two issues.
a. Where someone is making the current working directory.
b. Are we in the temp folder before we execute certain commands.

The following should resolve this issue.


Code:


PushD %~dp0
If %CD%==%temp% (
For /F "Delims=" %%I In ('dir /B') Do If /I NOT "*.bat"=="%%~fI" Del /F /S /Q *.* "%%I"
) Else (
Set _batpath=%CD%
cd ..
If %CD%==%temp% (
Del /F /Q *.*
For /F "Delims=" %%I In ('dir /AD /B') Do If /I NOT "%_batpath%"=="%%~fI" RD /Q /S "%%I"
) Else ( rmdir /S /Q %temp% )
)
PopD

Unless there is a better way to write that.
It should state:
1. Change to the current working directory
2. Check to see if the current working directory is actually the %temp% folder.
3. If it is the %temp% folder then go ahead and delete all files from the current directory and sub-directories that are not .bat files.
4. If the current working directory is not the %temp% folder then let's at this time presume we are in the .tmp folder and let's set the variable _batpath equal to the current directory.
5. Let's go up one level so that we should not be in the %temp% folder however we need to double check this.
6. Let's verify once again that we are in the %temp% directory and not in some program files directory or some other directory of the editors choice.
7. If we are in the %temp% directory, go ahead and delete all files and then delete all sub directories that are not equal to the _batpath variable.
8. If we are not in the temp directory then go ahead and remove the %temp% directory and continue on with the rest of the batch because we are in no danger of deleting the actual bat program.

Hopefully that is what I wrote. Please correct me if I am incorrect or if I have formatting errors or even if there is a much more effecient way of writing this.


----------



## Squashman

I can't explain why it is working on my system and not yours. The batch file executes and leaves 1 directory and 4 files in my %TEMP%. Those 4 files are temp files used by our ADC Time Tracking system and the folder is used by Lotus Notes which I always have open. It can't delete those files because they are in use by the application. I can see how it would not work if it is deleting the source batch file but it shouldn't if the file is in use. It doesn't on my system.

I have never cleared out my TEMP folder ever. I literally had hundreds of files and directories in there before I executed the compiled batch file.


----------



## Squashman

Another option. Put the rmdir /s/q in another batch file. Then compile your original batch file but use the includes to add in the batch file with just the rmdir. In the orginal batch file use the start cmd to start the 2nd batch file. to remove the temp directory. The original batch file should terminate after it starts the 2nd batch as long as that is the last cmd in the 1st batch file.


----------



## TheOutcaste

Squashman said:


> I can't explain why it is working on my system and not yours.


I have to admit, I ran it more than once on both XP and Vista before it sank in Not saying how many times, just more than once.

Maybe this will make it easier to see
Compile the following, run the exe, and note the start and end times:


Code:


@Echo Off
Echo Start time is %time%
Ping 127.0.0.1 -n 1
:: RD /S /Q %temp%
Echo End time is %time%
Pause

Now compile the following, run the exe, and note the start and end times:


Code:


@Echo Off
Echo Start time is %time%
Ping 127.0.0.1 -n 1
RD /S /Q %temp%
Echo End time is %time%
Pause




Squashman said:


> I can see how it would not work if it is deleting the source batch file but it shouldn't if the file is in use. It doesn't on my system.


The Command Processor doesn't lock a running batch file, so it is possible for a batch file to delete itself.
The compiled exe can't be deleted while it's running, but the extracted batch file can be deleted.
Give this one a try:


Code:


@Echo Off
Echo Hi, my name is %0
Echo Try to delete the compiled exe file. I'll wait
Pause
Echo OK, I will now delete myself as soon as you
Pause
Del /F /Q "%0"
:: Comment line to be cached
Echo I've deleted myself so you'll never see this line, and I won't pause
Pause

The line immediately after the Del command _may_ be executed, as it might be cached; without the comment line I can sometimes see the last echo, but the pause is never executed.


----------



## TheOutcaste

scrfix said:


> 1. The maker does not change where they store the bat file. If they decide to store it in the program files directory, this would be disasterous.
> 
> 2. If we are compiling utilizing that compiler. Other comilers store the bat directly in the temp directory without a folder and that would be disasterous as well to delete all of the files and directories before the temp directory.


I tested using this bat to exe compiler, version 1.5.0.0:


Squashman said:


> This is the one I use.
> http://www.f2ko.de/English/b2e/download.php


Once the file is compiled, #1 and #2 won't be an issue. If you recompile using a different version, or a different complier, then you'd definitely need to re-test for both those situations. Good idea to check though. A year from now you may make a minor addition and forget that you're using a different version/compiler.

The first version using the *For /R %temp%* would be a better choice in this case. That *For* loop will delete all subfolders in the %temp% directory except for the one containing the batch file, if it exists. Doesn't matter if the batch file is in the temp tree, or someplace else, and it forces the comparison of the paths to use the short names, so no issues if a different compiler/newer version calls the batch file using long names. (more on this below)

You _would_ need to check before running the Del command. I left out the CD %temp% (or PushD %temp%), but the Del command assumes that the batch file and any included files are not in %temp%. If you include other files, and they are extracted into the %temp% folder rather than to a subfolder of %temp%, you'd need to check for and exclude all of them as well.

The 2nd example assumes the batch file is in a subfolder of %temp% and that the subfolder is not a long name, so it would need some modification as well to be more universal.


scrfix said:


> In one compiler, there is an option to change working directories however it doesn't work.


In Kodak's compiler (the one linked above) the Working Directory option does work. It's not explained very well though.


Current Directory means the current directory will be set to the directory from which the compiled exe file is run.
Any included files are copied to the same folder as the exe file when it is run.
Temporary Directory will use the Temp folder (HHHH.tmp) that the compiled batch file is extracted into.
Any included files will be extracted to that Temp folder as well.
The *Submit current directory* option will pass the Current Directory as defined above to the batch file on the command line as a quoted string. This will let you use %1 to refer to the folder that contains the compiled exe file.
You can test with this:


Code:


@Echo Current Directory=%CD%
@Echo Parameters=%1
Pause


There are two ways to refer to a path, using long names or using short names. They are equivalent, but they are not equal.
Example, on XP, my temp folder long name is:
*C:\Documents and Settings\TheOutcaste\Local Settings\temp*
%temp% expands using the short names to this:
*C:\DOCUME~1\THEOUT~1\LOCALS~1\temp*
Same path, but an If statement will not see those as equal as they are compared as strings.
On Vista it's
*C:\Users\TheOutcaste\AppData\Local\Temp
C:\Users\THEOUT~1\AppData\Local\Temp*
If your user name is less than 8 characters long, the long and short names will be the same.

The difficulty arises due to the fact that how %CD% is expanded depends on how you switched to the current directory. This is easiest to see on XP as the long and short names for the normal Temp folder will always be different.
Open a command prompt and type the following (using your username of course) and you'll see the difference:


Code:


CD "C:\Documents and Settings\TheOutcaste\Local Settings\temp"
Echo %CD%
CD %temp%
Echo %CD%

This also assumes that %temp% always expands using short names, which seems to be the case for Win2K-Vista. Haven't tested on Win7 yet. I do have a vague memory of having to use quotes around %temp% because of the space in Documents and Settings, but I may be thinking of one of the other variables. All the rest use the long name format
%0 is the name of the batch file _*as called*_. So if called using short names, %dp0 will be the short name version. If called using long name, it will be long. The case _should_ match, but best to use the /I switch just in case.
Short names can be forced using the ~s modifier. See the last section in For /? and Call /? for info on the modifiers.



scrfix said:


> So if I understand this correctly.
> *PushD %~dp0 *(Change the directory to the current working directory where the batch is being run from.])
> *Set _batpath=%CD% *(Set the variable _batpath equal to the current directory)
> *cd ..* (Go up one level)
> *Del /F /Q *.** (Silently delete all of the files in that directory however do not touch sub directory files)
> *For /F "Delims=" %%I In ('dir /AD /B') Do If /I NOT "%_batpath%"=="%%~fI" RD /Q /S "%%I"* (Run a For Loop, Look for the delimiters ?? in the directory only listings with names only and store each one in the variable %%I then verify that the directory is not equal to the _batpath variable case insensitive [not sure what the ~f in %%~fI does] and if it is not equal to that, then run the command to remove the directory silently.)
> *popD* (This changes directory again as I understand it however without a directory following it when I enter it into a command line all it returns is the same directory)
> 
> Do I have that correct?


Pretty much. *PushD* and *PopD* work together. *PushD* saves the current directory on a stack, then does a *CD /D* to the specified directory. *PopD* pops the saved directory off the stack, and does a *CD /D* to the saved directory. This way you don't have to save the current directory in a variable to restore it. *PopD* will do nothing if there is nothing on the stack. If you are already in the "popped" directory it will seem to do nothing as well.
* PushD %CD%* doesn't change the current directory, but it does save it onto the stack, which can be useful.
This line should really be *PushD %~dps0*. This will force it to use the short name version, just in case the compiler calls the file using the long name format. Not likely, but best to eliminate the possibility. This will allow you to compare %CD% to %temp%, as %CD% will use the short name format.
The For loop:
*For /F "Delims="* means use no delimiters instead of the default delimiters of tab and space. This means the loop variable will be set to the entire line. Same result as using *"tokens=*"* 
*dir /AD /B* lists directory names only, not including the path. Does not list files.
*"%%~fI"* The *~f* expands to the fully qualified name. Equivalent to using *~dpnx*. The output of this Dir command doesn't include the drive or path, just the name (and extension if any), so the *~f* will add the current drive and path to the directory name.
One thing to remember when using modifiers with a loop variable, or with batch parameters *other* than %0, is that they parse the content of the variable to extract drive, path, name, and extension. The drive, path, filename, file extension don't have to actually exist. You can make up a string for a non-existent drive, path, file name and extension, and the modifiers will extract the pieces as if it actually existed. The drive letter doesn't even have to be a letter. Any single character in the correct location will be seen as the drive letter.
The trick here is that if there are parts that are not included in the variable (in this case no drive or path, just a name and (maybe) an extension), it will substitute the _current_ path info. This is expanded using the same format as %CD%, so should match the Drive and Path in the _batfile variable.

Rather than switching to the directory that contains the batch file, then verifying if it's the temp directory, just switch to the temp directory, and check if the file is there. This avoids the issue of the compiler extracting it into Program Files or Windows

So to clear the Temp folder we have 3 possibilities:


Extracted batch is in a subfolder under %temp%, along with all included files.
Delete all files in %temp%, and all subfolders except the one containing the batch file
Extracted batch and included files are in %temp%
Delete all subfolders, then delete all files in %temp% except the batch file and the included files.
You can include a list of files, or exclude extensions (.bat, .cmd, .exe). Excluding extensions may exclude many files other than the ones you've included though.
Extracted batch and included files are not in Temp.
Same process can be used as in #1, as the subfolder isn't there to be excluded



Code:


PushD %temp%
:: Delete all subfolders in temp except the one containing the batch file, if it exists.
For /R "%temp%" %%I In (.) Do If /I Not "%temp%\."=="%%I" If /I Not "%%~sI\"=="%~dps0" RD /Q /S "%%I"
:: Check if batch file is in the temp folder. If not, delete all files
If Not %~dps0==%temp% Del /F /Q *.*&Goto _tempcleared
:: batch and included files are in the temp folder
:: If a filelist wasn't included, create one to exclude the batch file.
If not exist "%~n0Filelist.txt" (
>"%~n0Filelist.txt" Echo %~nx0
>>"%~n0Filelist.txt" Echo %~n0Filelist.txt
)
For /F "Delims=" %%I In ('dir /A-D /B ^|findstr /I /V /G:"%~n0Filelist.txt"') Do Del "%%I"
:_tempcleared
PopD

This requires that you include a list of all included files in a file named *<filename>FileList.txt*, where *<filename>* is the name portion of the batch file, i.e., MyApp.cmd would include MyAppFileList.txt. Files are listed one per line, unquoted.

Breaking down the For loop into multiple lines:


Code:


 For /R "%temp%" %%I In (.) Do (
  If /I Not "%temp%[COLOR=Red][B]\.[/B][/COLOR]"=="%%I" (
    If /I Not "%%~sI\"=="%~dps0" RD /Q /S "%%I"
  )
)

This returns all the folder names starting at %temp%. It tacks on a *\.* to each one, so we have to add that to the %temp% variable in the first If, which excludes the root directory (%temp%)
The next If compares the short name of the folder to the short name of the path to the batch file. If not equal, it deletes the folder. Using the ~s modifier removes the trailing *\.*. This is because of the way the variable is parsed, so we have to add a \ to match the batch file path, which includes a trailing slash.

Jerry


----------



## scrfix

Jerry,

Thanks. You answered why when I do the DEL *.* /F /Q that it still works as mentioned in one of my other posts. It is cached however it never gets past that next line. I am going to read over your above post again when I get home tonight.

I however, have a different issue happening that has been driving me nuts for over a week now. You helped me out with a For statement to detect directories automatically and copy them over to a destination. I made some changes to the code however it is pretty much in tact.



Code:


:_COPYUSERSDIR
for /f "tokens=*" %%a in ('dir "%main%" /b /ad-L') do call :_COPYUSERSDIRPROCESS "%%a"
goto _NextSectionOfCode
 
:_COPYUSERSDIRPROCESS
if [%1]==["Default"] GOTO :EOF
if [%1]==["Default User"] GOTO :EOF
if [%1]==["LocalService"] GOTO :EOF
if [%1]==["NetworkService"] GOTO :EOF
::%1 is the file name from the for loop
xcopy /(Switches Here) "(Documents and Settings OR Users Folder Here)" "(Destination Here)"
GOTO :EOF

In the code above, I have put the xcopy as
xcopy /(xcopy Switches Here) "(Documents and Settings OR Users Folder Here)" "(Destination Here)"

It was easier to do this since we all know what xcopy does rather than to keep the local variables that I had in there.

This code says list all of the directories in short mode and do not include junctions. (I added the junctions for Vista)

Once again, I have not quite moved over to Robocopy yet because of this temp problem. I want to get that repaired before I move to robocopy. However the issue that I am having is that because of the /ad it is only listing directory names and not the loose files within those directories. Some of our computer users actually save their files within those directories. Telling them not to is not an option. I can shout it until I am blue in the face and they are still going to do it.

I have tried every combination I can think of for the directory listings and the best that I have been able to come up with is if I remove the d however then it asks me for every file if it is a directory or a file even though it has an extension. This happens on both Vista and XP.

*How do I tell this to not only look at directories but to look at individual files as well and then ignore the ones that I have listed?*

I even took two days off of work in an attempt to figure this one out. It didn't happen. I am not sure what to put in so that it doesn't ask me if debug.txt is a directory or a file.

I will attempt that code for the temp folder tonight as well. The vista system I have keeps getting the insufficient memory error so I need to switch this over to robocopy however just one thing at a time.

Thanks again for everything.

*FYI*
Quick Batch Compiler is one that creates the renamed bat directly in the %temp% directory.

Batch Compiler Professional actually has the best solution for this wrap around these companies are calling compiled. They store it into a hidden sub-directory of the %temp% folder.

Bat to Exe supposedly has an option to create the file in a separate directory to store in however it doesn't work nor is their good documentation to tell you whether or not that is really what it is supposed to do.

Jerry,

*!!Warning!! Start Babble Talk... Nothing Important !!Warning!!*
Amongst all of your free time , you should make an actual compiler. With your knowledge of these Bat files, just pick up a second language if you already do not know one such as C#. Pull the bat into your converter, convert all of the commands to C# commands and create an EXE output that does everything the user is attempting in the bat *without* the cheap knock off wrap around these companies output of oh look, we encrypt your file, whoops psyche, it is not really encrypted because when we run it we are actually going to show your unencrypted bat file to the world and we are going to put it in a place where anyone can get to it at the time of execution and not give you an option to change that. Ahhhhhhhhhh How stupid!!! (Yes, they do have their advantages such as you can include other exe files to run from the batch. That is pretty awesome. But these people *advertise with Encryption*.). If they cannot truly convert it to a semi-safe encrypted executable (I say semi-safe because as I am sure we are all aware of you can read that executable in a hexadecimal editor if you know how to read hex.), then at least they should give you the option of not utilizing the %temp% directory to store your file in. If you had that option at least that would make your file a little safer because you could choose anywhere to save it on the computer vs. being forced to utilize the %temp% directory which is the first place you look when trying to steal or hack files. I am just complaining about lack of attention to detail for companies like these.
*!!Warning!! End Babble Talk... Nothing Important !!Warning!!*


----------



## TheOutcaste

scrfix said:


> Some of our computer users actually save their files within those directories. Telling them not to is not an option.


So you have users who are actually saving files in *C:\Documents and Settings* (XP) or *C:\Users* (Vista)? Unless someone has tweaked the permissions, only members of the Administrators group should be able to create files or folders there. And on Vista, even my Admin account can't save files there, though I can move them there. And a right click | New only has *Folder* as an option.
If they have Admin privileges, not much you can do to stop it.

As for copying, just and a second Xcopy line after the For loop and before the *goto _NextSectionOfCode*, but do not use the */s* or */e* switch. That will just copy the files and won't give the File or directory prompt:
*Xcopy /c "C:\Documents and Settings" "Destination Root"*
or
*Xcopy /c "C:\Users" "Destination Root"

*


scrfix said:


> I can shout it until I am blue in the face and they are still going to do it.


There's the trouble. Need to work on the "Green in the face" option.
If you're blue in the face, they'll think you are suffocating. If you suffocate and die, they can spend all day on the social pages and other non-work sites, so they won't care.
If you're red in the face, they figure you'll have a stroke. Same as above.

If you're green in the face however, they may think you're going to upchuck on them and their keyboard, and that'll usually get their attention.


----------



## scrfix

LOL... @ Green Face

I use the xcopy in a variable that already has those switches built in so I was hoping there was a way to modify that code so that it will copy the directories and the loose files.

I get computers all the time that people save their own files into. What happens is the other technicians do not think to look there when backing up and I end up having to perform data recovery to get the data back after the format of the operating system. Most of these are stand alone systems not on the domain so there is no domain policy on those computers.

I have even tried the following to no avail


Code:


:_COPYUSERSDIR
for /f "tokens=*" %%a in ('dir "%main%" /b /ad-L') do call :_COPYUSERSDIRPROCESS "%%a"
goto _NextSectionOfCode
 
:_COPYUSERSDIRPROCESS
if [%1]==["Default"] GOTO :EOF
if [%1]==["Default User"] GOTO :EOF
if [%1]==["LocalService"] GOTO :EOF
if [%1]==["NetworkService"] GOTO :EOF
::%1 is the file name from the for loop
xcopy /c "c:\documents and settings\" "z:\backup\"
xcopy /c "c:\documents and settings\*.*" "z:\backup\"
GOTO :EOF

However I can put the switches into a variable and the xcopy into a variable and do it that way. That is probably what I will do. That will work.


----------



## scrfix

I made the one switch into a variable and just wrote out another for statement. I like the fact that I can stop items from being copied. If I just utilize the xcopy after the for statement I lose that functionality.

I also utilized /b /a-d so that it will only show files.


----------



## TheOutcaste

Are there specific files you don't want to copy, or just the Non User folders?
If you don't need to exclude specific files that may be in the Documents and Settings/User folder, then you'd just need to add one line after the For statement without using the switches variable so the /S or /E are not included.
If there are specific file names you need to exclude, then a 2nd for loop would be easiest.
This will copy all user folders, but excludes Default* and *Service, and copies all files in the C:\Documents and Settings or C:\User folder (whatever Main is set to).
If skips the Temp Internet files, Firefox and Java cache folders, User Temp folders, and User History folder, Thumbs.db files, works with Robocopy or Xcopy by detecting the OS:


Code:


Set _Backup=U:\Backup\%ComputerName%
Set _exFold="Temporary Internet Files" Temp History Cache
Set _exfiles=thumbs.db
If /I %_OS%==XP (
  Set main=C:\Documents and Settings
  Set _cpycmd=Xcopy /CDHIKOQRY
  Set _switches=/E /Exclude:C:\Scripts\exclds
  ) Else (
  Set main=C:\Users
  Set _cpycmd=RoboCopy /R:3 /W:5 /copy:DATS /NJH /NJS /NFL /NDL
  Set _switches=/E /XD %_exFold% /XF %_exfiles%
)
:_COPYUSERSDIR
for /f "tokens=*" %%a in ('dir "%main%" /b /ad-L') do call :_COPYUSERSDIRPROCESS "%%a"
[COLOR=DarkRed] :: This next line copies only files, it will not copy any folders[/COLOR]
%_cpycmd% "%main%" "%_Backup%"
goto _NextSectionOfCode

:_COPYUSERSDIRPROCESS
if [%1]==["Default"] GOTO :EOF
if [%1]==["Default User"] GOTO :EOF
if [%1]==["LocalService"] GOTO :EOF
if [%1]==["NetworkService"] GOTO :EOF
::%1 is the file name from the for loop
%_cpycmd% "%main%\%~1" "%_Backup%\%~1" %_switches%
GOTO :EOF
:_NextSectionOfCode

This is the exclds file used with Xcopy:


Code:


\cache\
\Temporary Internet Files\
\Temp\
\History\
thumbs.db

Jerry


----------



## scrfix

*Update*
I was typing this the same time you were typing yours. I will look into yours when I wake up. It is 3:35am here. Time for bed.

Damn, it still asks me that stupid question.

Does G:\Spectacular Computer Repair\desktop.ini specify a file name or directory name on the target
(F = file, D = directory)?

Does G:\Spectacular Computer Repair\This is a test.txt specify a file name or directory name on the target
(F = file, D = directory)?

The strange thing is that it is asking me for the destination directory, not the copying directory?



Code:


:COPYUSERSFILES
for /f "tokens=*" %%a in ('dir "users" /b /a-d') do call :COPYUSERSDIRPROCESS "%%a"
goto ROOTDIR
 
:COPYUSERSFILESPROCESS
:: if [%1]==["Default"] GOTO :EOF
:: if [%1]==["Default User"] GOTO :EOF
:: if [%1]==["LocalService"] GOTO :EOF
:: if [%1]==["NetworkService"] GOTO :EOF
::%1 is the file name from the for loop
xcopy /c "c:\users\" "z:\backup\
GOTO :EOF

I am going to try it with c:\users\*.*
Perhaps that will work.


----------



## TheOutcaste

scrfix said:


> Code:
> 
> 
> ::%1 is the file name from the for loop
> xcopy /c "c:\users\" "z:\backup\
> 
> I am going to try it with c:\users\*.*
> Perhaps that will work.


Missing quote after backup\"
And you aren't using the %1 variable so the passed file name isn't even being used.

So I think that should be *xcopy /c "c:\users\%~1" "z:\backup\%~1"*

I don't know where the G:\Spectacular Computer Repair\ message is coming from as this section of code is searching C:\Users, so that would seem to be from another sectin of code.
If you are copying multiple files to a destination folder that doesn't exist, it will ask the File/Directory question. Just add the /I switch to avoid that.


----------



## scrfix

Jerry,

Sorry. I did not catch that. Those were typing mistakes on my part. Those items are there in the code. I took out my variables and put in the code to make it easier. It looks almost exactly like you have it with the exception of variables.

I am going to look at your other code and see if I can utilize something from there.

I am using the /I switch for that reason so it should not be asking me.

G:\Spectacular Computer Repair\ is the destination. It could be z:\backup\ or whatever else. I just copied and pasted the code directly from the command prompt.


Thanks,


----------



## scrfix

Jerry,

This line doesn't make any sense to me



Code:


Set _switches=/E /Exclude:C:\Scripts\exclds

I understand that is for Exclude parameter and I remember reading soemwhere that in order to utilize Exclude you have to include a text file somewhere, I believe, to make Exclude work for XCopy however this points to a directory.

Is the text file within that directory?
Does it have to be named anything specific?

Wayne


----------



## TheOutcaste

*exclds* is the text file, it's in the *C:\Scripts* folder. I just didn't give it an extension. Just wasn't in the mood I guess

You can name it anything you want. It doesn't have to have an extension, or the extension can be anything. The only purpose for an extension is to associate files with programs so you can open the file by double clicking.
Here we are specifying a file to open, and Xcopy will treat it as a text file.

You can name the file sunset.jpg and it will still work.


----------



## scrfix

Thank You. I will want to create that file on the fly so I will create that in the beginning of the program and then delete it when I am done or simply use it as an include from the working directory. I wonder if I can include hidden files. I will have to test that. If I include a file with hidden attributes will they will remain hidden when executed. Something to try.

Oh, I figured out why it was asking me about the file or folder on a file. In my code I needed a \%~1 after the for statement. As soon as I added it, it copied perfectly.


----------



## scrfix

Hey Jerry,

This question is off the subject a little bit but I am curious as to how a batch works when it comes to exit codes.

Let's say I am running a batch file and I push the CTRL+C and terminate the batch.

By default the exit codes for xcopy get set to:



Code:


Exit Code | Description 
0 | Files were copied without error.
 
1 | No files were found to copy.
 
2 | The user pressed CTRL+C to terminate xcopy.
 
4 | Initialization error occurred. There is not enough memory or disk space, or you entered an invalid drive name or invalid syntax on the command line.
 
5 | Disk write error occurred.

I also understand that I can set the exit codes to whatever I want by set %ERRORLEVEL%=6039

Is it possible for me to run code if a user pushes CTRL+C in a bat file?

If so, how would that be accomplished? Anywhere in the bat file or would I have to wrap an if statement around the bat file?



Code:


If %ERRORLEVEL% == 2 (
REM Run this code here
) Else (
REM Run the Bat file here )

or just put this code anywhere in the bat file?



Code:


If %ERRORLEVEL% == 2 (
REM Run this code here
)

or a third option, I am totally screwed up and neither of the suggestions will work because they are extreme newb suggestions and Jerry has a much more effecient way of accomplishing what we want to accomplish 

I did try the Green face for getting people not to save their files in documents and settings. They ran away screaming something about Mr. McGee and the Incredible Hulk.. I don't know. I didn't quite get it ( sad face  ). I guess I will just stick to the code of copying those files as well.


----------



## scrfix

I figured out it is the latter of the two options. The same place I found the exit code levels had an example at the bottom of the page on this subject:

http://technet.microsoft.com/en-us/library/bb491035.aspx


----------



## TheOutcaste

scrfix said:


> I also understand that I can set the exit codes to whatever I want by set %ERRORLEVEL%=6039


If you use a set statement to assign a value to ERRORLEVEL, you can't use *IF %ERRORLEVEL%==X* to check the errorlevel after other commands. The Set statement will create a variable named ERRORLEVEL and will not use the variable to hold the actual errorlevel after a command. You'd have to use the *If ERRORLEVEL X* format.

You can use EXIT to assign an errorlevel when exiting a batch file or subroutine:


Code:


C:\>exit /?
Quits the CMD.EXE program (command interpreter) or the current batch
script.

EXIT [/b] [exitCode]

  /B          specifies to exit the current batch script instead of
              CMD.EXE.  If executed from outside a batch script, it
              will quit CMD.EXE

  exitCode    specifies a numeric number.  if /B is specified, sets
              ERRORLEVEL that number.  If quitting CMD.EXE, sets the process
              exit code with that number.


----------



## scrfix

I was just breaking this down and decided to test something. If I utilize one of the other compilers, it creates the bat file directly into the temp directory.



Code:


PushD %temp%
:: Delete all subfolders in temp except the one containing the batch file, if it exists.
For /R "%temp%" %%I In (.) Do If /I Not "%temp%\."=="%%I" If /I Not "%%~sI\"=="%~dps0" RD /Q /S "%%I"
:: Check if batch file is in the temp folder. If not, delete all files
If Not %~dps0==%temp% Del /F /Q *.*&Goto _tempcleared
:: batch and included files are in the temp folder
:: If a filelist wasn't included, create one to exclude the batch file.
If not exist "%~n0Filelist.txt" (
>"%~n0Filelist.txt" Echo %~nx0
>>"%~n0Filelist.txt" Echo %~n0Filelist.txt
)
For /F "Delims=" %%I In ('dir /A-D /B ^|findstr /I /V /G:"%~n0Filelist.txt"') Do Del "%%I"
:_tempcleared
PopD

:: Check if batch file is in the temp folder. If not, delete all files
If Not %~dps0==%temp% Del /F /Q *.*&Goto _tempcleared

That line is supposed to prevent that, correct?
Does it need to be:
If Not %~dps0=="%temp%" Del /F /Q *.*&Goto _tempcleared

If I copy and paste the bat into the %temp% directory and run it, the batch still gets removed. I am not sure that line is working.

With this other compiler, the bat gets renamed to something such as ~b25.bat and stored directly into the %temp% directory.

What I did to test, was to rename my bat file as that, mark it as hidden and run it directly from the %temp% directory. It was still removed. I didn't actually run it from the exe. I merely recreated the steps as to what the compiler would do.

The weird thing here is when I ran it the first time directly from the %temp% folder it removed it. I attempted it a second time and it did not remove it? huh? The only change I made was a 1 liner that said
echo I am in %temp%
pause

I put that directly after pushd so that I could troubleshoot it down. I was expecting a crash afterwards but it did not. Now every subsequent time it doesn't remove it. Weird. I will try to repeat it again but it appears to be working great now. I even removed the lines above and it is working flawlessly. I am not sure why the first time ran it removed the .bat from the temp directory.

I am able to repeat this when I use a different bat file that is not hidden. When I utilize the ~a.bat hidden it works perfectly however when I utilize the ~a2.bat that is not hidden the bat file is removed. It doesn't matter if it is in there by itself or not. Both bat files are exactly the same code.

*Okay Figured Out*
The line does not appear to be working unless the bat file is hidden. If the bat file is marked as hidden, this works perfectly. I attempted to perform a rmdir /S /Q %temp% directly in command line and it removes the hidden bat file. If the bat file however is not hidden, it gets removed and the program then crashes.

Any ideas?


----------



## TheOutcaste

Turned on echo and found the culprit:
%~dps0 = C:\DOCUME~1\THEOUT~1\LOCALS~1\*t*emp*\*
%temp%= C:\DOCUME~1\THEOUT~1\LOCALS~1\Temp

Forgot the /I switch on the If statement to make it case insensitive, and the batch file path (Using %0) has a trailing backslash, whereas %temp% doesn't.

So this line:
* If Not %~dps0==%temp% Del /F /Q *.*&Goto _tempcleared*
Should be this:
I*f /I Not %~dps0==%temp%\ @Echo Del /F /Q *.*&Goto _tempcleared*
I thought I tested with the batch file in the %temp% folder, but either I didn't, or those pieces got left out somehow.


scrfix said:


> *Okay Figured Out*
> The line does not appear to be working unless the bat file is hidden. If the bat file is marked as hidden, this works perfectly. I attempted to perform a rmdir /S /Q %temp% directly in command line and it removes the hidden bat file. If the bat file however is not hidden, it gets removed and the program then crashes.
> 
> Any ideas?


The Del command won't remove Hidden files unless you use the */AH* switch, but RmDir does.

Add the /I and the trailing backslash, and that should get it working.

Jerry


----------



## scrfix

I figured it out what I was asking. I will test and get back to you here if I have further issues.

I have switched everything over to robocopy. I still have yet to test on a XP box however Vista is working good except copying those loose files in the specified directories and the command after the for statement does not work. It skips right past it as if it didn't exist and doesn't copy loose files.

I think I have this worked out however.

*Update 06-24-09*
Yes, I had a stupid / at the end of the directory name I was attempting to copy to and it was screwing everything up. I added a folder after that and everything is working perfectly. I can now copy indicidual files and have the ability to edit certain files out which is what I was looking for.

Thanks to you and Squashman, I can now successfully troubleshoot these errors as well. Thank you very much for all of your assistance. It may take me much longer than you guys to troubleshoot however I feel I have learned a lot. I refer back to your explanations in these forums every time I am working with this batch file.

Hopefully, when I feel that I am at least closer to both of your levels of expertise, I can help other people as you have helped me.


----------



## scrfix

*Update 06-25-09*
I saw where the problem was at. The source still thought that it was in the %CD%.

I did a PushD %systemdrive%\ and then ran the code with the proper switches and that works great. It copies all of the loose files in the root directory.

If there is a more effecient way of accomplishing getting to the root directory I am all ears, well eyes, or if there is any special rules I need to follow when utilizing PushD. I also used PopD at the end of it. I am not sure if that is necessary or not however.


----------



## Squashman

What do your mean by "ROOT"?
If you mean where the batch file exists then you can do this.
cd %~dp0

If you mean the root of your hard drive
cd \


----------



## scrfix

The root of the %systemdrive%, ie C or D or whatever the systemdrive happens to be on that particular computer.

I will try cd \

Thanks and thanks again for telling me about the compilers. After figuring out those weird effects it was having, they are working out great.

I am testing the code my XP box now with Robocopy.
Okay just tested with xp inside the compiler. It will read the compiled exe however has massive amounts of errors. Time to start troubleshooting. Here goes the next day... lol


----------



## Squashman

scrfix said:


> The root of the %systemdrive%, ie C or D or whatever the systemdrive happens to be on that particular computer.
> 
> I will try cd \


cd \ takes you back to the root of the current drive you are in.
The environmental variable %systemdrive% will take you to whatever that variable is set to. If your OS is installed to C: then it will take you to the root of the C: drive.

C:\>set S
SystemDrive=C:
SystemRoot=C:\WINDOWS

E:\Sea\TEST>pushd %systemdrive%

C:\>


----------



## scrfix

*Update 06-25-09*
I figured out the issue. The program removed the temp directory and did not recreate it so therefore the next time I attempted to go there, it wasn't there at all and I was getting the error. That is why PushD %temp% was not working in the command line because it that system literally could not find the path specified because it didn't exist.

*Old Data* 
The error here is PushD %temp%
When in the command line on an XP box, the error is returned:
*The system cannot find the path specified.*


----------



## Squashman

I would think you would want to remove everything inside the %TEMP% directory and not remove the whole thing.


----------



## TheOutcaste

Keep in mind when using the *CD* command, if you are switching drives, you must use the */D* switch to actually change to that drive. Without the* /D*, it changes the current directory on the specified drive, but doesn't switch to it.
For example, assuming you are in C:\ and D:\ is the current directory on D: (Keep in mind D:\ is two parts, the Drive D: and the directory \)
Try these four commands (using real folders of course)
C:\>CD D:\Windows\System32
C:\>Dir D:
c:\>Dir D:\
C:\>copy test.txt D:

Note the prompt doesn't change, you are still in C:\.
The Dir D: command will display a directory for D:\Windows\System32, not the root of the D: drive. This is because you specified the drive letter without a directory, so it uses the Current Directory for that drive.
Dir D:\ will display the root of D: as you have specified a directory
The copy command copies to D:\Windows\System32, not to D:\

PushD will change drive letters, and should be followed by a PopD to clear the stack, but isn't really needed. It will be cleared when the Command Prompt closes. PushD also doesn't need to have paths with spaces quoted

As you've discovered, if you delete the Temp folder (or any other system folder), probably best to immediately re-create one to avoid it being MIA.


----------



## scrfix

Jerry,

Thank you for that. I did discover that and that is how I rectified it by checking to see if it was there and if it wasn't then creating it again.


----------

