Link to home
Start Free TrialLog in
Avatar of Bob Stamm
Bob StammFlag for United States of America

asked on

Batch Programming Help - Search and Delete Files

I have a simple batch file application that transfers our CAD files from our server to an individual’s laptop PC.  This is working fine.

md c:\NetworkDsgn\CoaxMaps\DWF >Nul 2>&1
xcopy "\\agoc-files-5\networkDsgn$\CoaxMaps\DWF\*.*" "c:\NetworkDsgn\CoaxMaps\DWF" /D/Y/Q

I have a structured format for our file names and I need a batch routine that will help me resolve an issue.  I need to search a DIR and all the subfolders for any files that have a tick mark (‘) included in them.
Example:
S_SXBG65'.dwf
S_BTLR23'.dwf

The same folders that have these files will have other files named identically with the exception of the tick mark.
Example:
S_SXBG65.dwf
S_BTLR23.dwf

What I hope to accomplish is to delete the identical files that do not include the tick mark.  Is this possible to do within a batch application?

FYI - This is going to be an ongoing issue that I will need to deal with for a few months.  If I can pull off a batch file solution it will resolve many issues.

Any help is greatly appreciated.
Thanks,
Bob
Avatar of ozo
ozo
Flag of United States of America image

rm `find . -name "*'.dwf" -print |sed "s/'//"`
Avatar of Bob Stamm

ASKER

ozo
I'm new at this so excuse the question but how do I use your statement in my batch file?
Bob
Bob,

I think ozo's solution would only work on a *nix system but it is a neat script.

If you're prepared to us a shareware tool, take a look a a tool I released recently at:

http://uk.geocities.com/xshareware/download/xdir.zip

There's a manual here:

http://uk.geocities.com/xshareware/manuals/xdir_manual.htm

Use a command line something like:

XDir *'* "/Form=DEL `*1*2`" /Execute

To test it, leave out the /Execute. It will print out all the commands it would execute. I just tried it and it looks like it should work. If you'd prefer a tidier output, try:

XDir *'* "/Form=IF EXIST `*1*2` DEL `*1*2`" /Execute

Essentially, its doing a Dir of the folder looking for file names with a tick in it and, for each one found, generate a delete command for a file with the parts of the original name that were matched by te two '*'s.

Good luck.

Paul
Hi Bob,

The solution above uses Unix commands so will not work in your windows environment.

You either need a batch program using MS-DOS commnads or you need to install a scripting language.

Perl would be a good choice, if you are happy to install it ( get a widows version here: www.activesatate.com), I can write a small script to achieve your deletions.

I don't know enoughMS-DOS batch to give a solution. Not sure if it has the function to do this type of pattern matching in file names.

A third approach is to install a windows port that will let you run  the unix commands listed above. http://www.cygwin.com/

Kim
teraplane,
I would like to take you up on your offer to help by using Perl.  This is new to me but I enjoy learning new stuff.  I  loaded ActivePerl 5.8.8 and I'm ready.
Thanks,
Bob

 
Good choice, Perl is great for these type of tasks. This should get you started.

use File::Find;

sub delete_dwg
{
    # if item is a file and  name does not end with '.dwf
    # $_ holds the name of the current file
     if ( -f $_ and $_ !~ /.*'\.dwf$/    )
     {
        print("Deleting  $File::Find::name\n");
        # only uncomment this when you are sure it's deleting the right files!!
        # unlink($File::Find::name); # remove the file
     }
}

# This assumes that you are running your program form the C: drive, Perl always uses forward slashes. It's a Unix convention
# but still applied to Perl under Windows.
my $start_dir = '/NetworkDsgn/CoaxMaps/DWF';

find(\&delete_dwg,$start_dir);
perl -MFile::Find -e "find(sub{-f and s/'(\.dwf)$/$1/ and unlink || warn q'$File::Find::dir/$_ $!', shift)" /NetworkDsgn/CoaxMaps/DWF
Avatar of Caudax
Caudax

You asked for a batch file.

For future reference, these types of requests should be posted under OS -> MS-DOS for a faster response. However, since you haven't, I get to steal the points for answering the original request for a batch file.

------------------------------------------------------------------------------------------------------------------
@ECHO OFF
SETLOCAL
FOR /F "usebackq delims=" %%A in (`DIR /B *'.dwf 2^>NUL`) DO CALL :DELCHECK "%%A"
GOTO :EOF
:DELCHECK
SET LOCALVAR=%~1
IF EXIST "%LOCALVAR:~,-5%%LOCALVAR:~-4%" DEL "%LOCALVAR:~,-5%%LOCALVAR:~-4%"
------------------------------------------------------------------------------------------------------------------

This batch only works for Windows NT computers. True DOS will not support this due to the use of the /F flag for FOR and the special substring use made. There is also the use of :EOF but that's easily replaceable with :END and adding a :END label at the end of the batch.
I forgot to mention; SETLOCAL is also specific to NT DOS. If you didn't guess it already, it sets all new variables to be local variables.

I hope this helps. I tested it out and I vouch that it works (but I'll disclaim liability anyway just because I'm scared of that).

-Caudax
If the tick mark is a backquote (`) instead of a single quote ('), you'll need this batch instead:

------------------------------------------------------------------------------------------------------------------
@ECHO OFF
SETLOCAL
FOR /F "delims=" %%A in ('DIR /B *`.dwf 2^>NUL') DO CALL :DELCHECK "%%A"
GOTO :EOF
:DELCHECK
SET LOCALVAR=%~1
IF EXIST "%LOCALVAR:~,-5%%LOCALVAR:~-4%" DEL "%LOCALVAR:~,-5%%LOCALVAR:~-4%"
------------------------------------------------------------------------------------------------------------------
Looks good Caudax, I didn't expect it could be done using "NT-DOS".
Caudax,

I know only enough to be dangerous with batch.  Here is what I have done with you code and I don't seem to be getting any results.  How or what do I need to do with your statement?  I am currently copying all of the files to local PCs.  I'm not sure how to edit your code to work with what I am doing.
Thanks,
Bob


 @ECHO off

:start
cls
ECHO Armstrong Grid Transfer
md c:\test\NetworkDsgn\CoaxMaps\DWF >Nul 2>&1
xcopy "\\agoc-files-5\networkDsgn$\CoaxMaps\DWF\*.*" "c:\test\NetworkDsgn\CoaxMaps\DWF" /D/Y/Q

ECHO ALL Systems updates in Progress.
xcopy "\\agoc-files-5\networkDsgn$\coaxMaps\*.*" "c:\test\NetworkDsgn\CoaxMaps" /E/D/Y
pause

SETLOCAL
FOR /F "usebackq delims=" %%A in (`DIR /B *'.dwf 2^>NUL`) DO CALL :DELCHECK "%%A"
GOTO :EOF
:DELCHECK
SET LOCALVAR=%~1
IF EXIST "%LOCALVAR:~,-5%%LOCALVAR:~-4%" DEL "%LOCALVAR:~,-5%%LOCALVAR:~-4%"

pause
exit

They made it a lot harder than it needs to be:

@ECHO off
FOR /R %%i in (*'.dwf) do del %%i
Dan7el,
I want to make sure you understood the original question.  I have a parent folder with many sub-folders.  From the parent folder (in this example c:\test) I need to run a command that will search all the subfolders and find the following conditions.  If a file named *'.dwf exist, check to see if a similar file name exist without the tick mark.  If so, remove the file name without the tick mark.

Example:
c:\test\Saxonburg
S_SXBG65.dwf
S_SXBG65'.dwf

c:\text\Butler
S_BTLR23.dwf
S_BTLR23'.dwf

End result will look like:
c:\test\Saxonburg
S_SXBG65'.dwf

c:\text\Butler
S_BTLR23'.dwf

To me it looked like you were deleting only the files with *'.dwf
Bob



Hi Bob,

My propsoed answer didn't check for a matchcing dwf  file with a tick mark. This should work.

sub delete_dwg
{
    # if item is a file and  name does not end with '.dwf
    # $_ holds the name of the current file
     if ( -f $_ and $_ !~ /(.*)'\.dwf$/    )
     {
        # take name without tick and add tick befor .dwf
        my $current_tick_file = $File::Find::name ;
        $current_tick_file =~ s/\.dwf/'\.dwf/;
        # is there a matching file name but with tick mark?
        if ( -e $current_tick_file )
        {
            # remove file without tick mark
            print("Deleting  $File::Find::name\n");
            # only uncomment this when you are sure it's deleting the right files!!
            # unlink($File::Find::name); # remove the file
        }
        else
        {
            print "Not deleting $File::Find::name\nNo matching file: $current_tick_file\n";
        }
      }
}


# This assumes that you are running your program form the C: drive, Perl always uses forward slashes. It's a Unix convention
# but still applied to Perl under Windows.
my $start_dir = '/NetworkDsgn/CoaxMaps/DWF';

find(\&delete_dwg,$start_dir);
The only problem I see is that it skips the pause and exit commands because :EOF references End Of File. Try replacing it with this:

SETLOCAL
FOR /F "usebackq delims=" %%A in (`DIR /B *'.dwf 2^>NUL`) DO CALL :DELCHECK "%%A"
GOTO :FIN
:DELCHECK
SET LOCALVAR=%~1
IF EXIST "%LOCALVAR:~,-5%%LOCALVAR:~-4%" DEL "%LOCALVAR:~,-5%%LOCALVAR:~-4%"
:FIN
Oh yeah, do NOT run Dan7el's code. He's thinking you just want to delete all files WITH the tick mark.
Caudax,
I still am not getting your code to work.  Here is what I think the batch should be doing. Tell me where I am going wrong.

#1 My batch statements create the directory structure on an individuals PC if it does not exist.
#2 My batch statements transfers all the files to the individuals PC.
'This is working fine so far.
#3 Is your batch statements intended to go to the Individuals c:\test\NetworkDsgn\CoaxMaps directory and remove the bad files?  This is what is not working for me.  When I run the batch I pause it after my code is complete.  As soon as I hit enter, the second pause is comming up.  To me it seems like nothing is happening.  As I look in the sub-folders, the bad files still exist.
Thanks,
Bob

This is my current code.  
========================================================================
@ECHO off

:start
cls
ECHO Armstrong Grid Transfer
md c:\test\NetworkDsgn\CoaxMaps\DWF >Nul 2>&1
xcopy "\\agoc-files-5\networkDsgn$\CoaxMaps\DWF\*.*" "c:\test\NetworkDsgn\CoaxMaps\DWF" /D/Y/Q

ECHO ALL Systems updates in Progress.
xcopy "\\agoc-files-5\networkDsgn$\coaxMaps\*.*" "c:\test\NetworkDsgn\CoaxMaps" /E/D/Y
pause

SETLOCAL
FOR /F "usebackq delims=" %%A in (`DIR /B *'.dwf 2^>NUL`) DO CALL :DELCHECK "%%A"
GOTO :FIN
:DELCHECK
SET LOCALVAR=%~1
IF EXIST "%LOCALVAR:~,-5%%LOCALVAR:~-4%" DEL "%LOCALVAR:~,-5%%LOCALVAR:~-4%"
:FIN

pause

exit
I'm a Tcl zealot - I often use it to rapidly develop little (and occasionally quite large) applications for Unix and Win32.  This little snippet doesn't use half the neat features of Tcl, but if it does what I think it does (it's untested; I'm lazy like that) it does exactly what's requested.

[code]
if {[llength $argv] - 1} { error "use: $argv0 <dir>" }
foreach file [read [set id [open "|cmd /c dir/s/b $argv" r]]] {
    if {[regexp {(.*?)'(\.[^\\]+)$}[gets $id] x name ext]} {
        file delete -force $name$ext
    }
}
close $id
[/code]

If it's called with anything but one argument it bails and complains.  If it gets one, it will first get cmd /c dir /s/b to give it a nice listing of it - one file per line, including subdirectories, giving full paths for everything.  Then it reads back all of these and uses the foreach loop to iterate over them, checking them against a regular expression.

The rough translation of what (I think) the RegExp means is "Nongreeidly match zero or more of anything such that you can match, if possible, an apostrophe followed by one literal period character which is followed by one or more non-backslashes leading up to the end of the line."  If the RegExp doesn't mean that I got it wrong but I think it's right.  The parentheses in the RegExp grab everything up to the apostrophe and put it in the "name" submatch variable, and everything after it (including the period) and put it in the ext variable.  The next line tries to delete a file named the same but without the apostrophe; because I used the -force option it doesn't care if it fails (no error is raised if the file doesn't exist for example).

Files without apostrophes are ignored, of course, because they don't match the regular expression.  One problem with this script is that it's a little risky to use file delete -force, which will merrily recursively delete a directory and all of its contents, but in the above script that should only happen in somewhat contrived circumstances where two directories are named in the way described (ie. identically except that one has an apostrophe just before the dot before the extension - which would be a little odd I'm guessing).  You could also have the script insist on only deleting files by ensuriing that "![file isdirectory]" (not file is directory) before being willing to delete.

Tcl is a Perl competitor, and while it's far less widely used and popular than Perl, I personally prefer it for 90% of the jobs people would say that Perl is good at.  Tcl has great regular expression and list processing support and has been massively underestimated.

Hope this helps!
Sorry, my batch is designed to solely delete all files in the current folder that the batch is located at.

I will post a solution later because I cannot access Windows Xp DOS at the moment.
Caudax,
I would appreciate that.  I really would like to keep this as a dos batch application.
Bob
Hi Bob, did you get a chance to try the perl solution?
Teraplane,
I did not pursue your solution at this point until I see if a MS-DOS solution is possible.  If I do not make any progress with it next week, I will attempt to understand and utilize your Perl suggestion.

Thank you for your effort.
Bob
My apologies for the negligence - I was unusually busy. I'll post a solution by the end of the day.
Caudax,
No problem. I appreciate your efforts.
Bob
Thanks. I've tested this batch file and, just like the last one, I vouch that this one works. I've tested it myself.

------------------------------------------------------------------------------------------------------------------
@ECHO OFF
SETLOCAL
FOR /R %%A IN (.) DO CALL :COMMAND "%%A"
GOTO :END
:COMMAND
SET TEMPLOC=%~1
CD%TEMPLOC:~2%
FOR /F "usebackq delims=" %%A in (`DIR /B *'.dwf 2^>NUL`) DO CALL :DELCHECK "%%A"
GOTO END
:DELCHECK
SET LOCALVAR=%~1
IF EXIST "%LOCALVAR:~,-5%%LOCALVAR:~-4%" DEL "%LOCALVAR:~,-5%%LOCALVAR:~-4%"
:END
------------------------------------------------------------------------------------------------------------------
ASKER CERTIFIED SOLUTION
Avatar of Caudax
Caudax

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Caudax,
This works great!!

Thanks for your help.
Bob
No problem.

Just remember to post in Operating Systems -> MS-DOS for DOS/Batch related questions next time.

Good luck,
Caudax
I'm trying to use this as well to delete Personal User Office 2003 shortcuts from PC's which we have upgraded to Office 2007.

My script is:

rem @ECHO OFF
cd "\Documents and Settings"
SETLOCAL
FOR /R %%A IN (.) DO CALL :COMMAND "%%A"
GOTO :END
:COMMAND
SET TEMPLOC=%~1
CD%TEMPLOC:~2%
FOR /F "usebackq delims=" %%A in (`DIR /B *2003.lnk 2^>NUL`) DO CALL :DELCHECK "%%A"
GOTO :EOF
:DELCHECK
SET LOCALVAR=%~1
IF EXIST "%LOCALVAR:~,-5%%LOCALVAR:~-4%" DEL "%LOCALVAR:~,-5%%LOCALVAR:~-4%"
GOTO :EOF
:END

But doesn't seem to delete any of the *2003.lnk shortucts - even running as local or domain administrator..

Anyone with an idea why it might not work for me?
I hope to call the batch file in the login script of a maintenance login we use on PC's for cleanup purposes.
Yes,

You don't know how the batch you are manipulating works and assumed it would adapt to do what you wanted to if you set:

FOR /F "usebackq delims=" %%A in (`DIR /B *'.dwf 2^>NUL`) DO CALL :DELCHECK "%%A"

into:

FOR /F "usebackq delims=" %%A in (`DIR /B *2003.lnk 2^>NUL`) DO CALL :DELCHECK "%%A"

But you neglect that my code specifically tries to check for the absence of the tickmark in this line:
IF EXIST "%LOCALVAR:~,-5%%LOCALVAR:~-4%" DEL "%LOCALVAR:~,-5%%LOCALVAR:~-4%"

Because you didn't fix that line of code, the new code now does nothing.

It looks to me like you just want to have a batch file that deletes all shortcuts ending in "2003.lnk" In that case, this line of code alone would be enough: @FOR /R %%A IN (*2003.lnk) DO @DEL "%%A"

For future reference, when asking for help, you're supposed to open new questions. If you post on old questions, people aren't likely to help you. That's why for a while people weren't allowed to post to answered questions (because people would do exactly what you just did - ask for new help and not get any).