--- ---
Question
I have to remove files named 'Master1', 'Master2' etc. (no extensions) from a given directory. The problem is that I never know how many of files Master1, Master2, etc, will be created at runtime. It should never be more than 10, but no matter how big number I would arbitrary set up, that number always could be exceeded. That's why I would prefer to use wildcards. Is it possible?

## Answer 1:

You will be sorely dissappointed, because depending on the order in which files placed into your directory structure, the deletion of a file causes the FindNext to skip existing files that match and thus you will not end up deleting all the ones you want. What you have to do is to set up a list to delete. Then once the list is setup, you can then delete them.

Note: Please note that a lot of people think that all you have to do is to build a list then delete them. This is not true. There are many files that can have a read/only attribute set etc... So, you must clear this first, otherwise the delete will fail. So, if you notice in my routine, I clear any attributes that may have been set for the file. Here is an example:

    procedure DeleteTempRAW(S1: String);
    var
      SearchRec: TSearchRec;
      X: Integer;
      Path: String;
      ListToDelete: TStringList;
      Ok: Boolean;
    begin
      ListToDelete := TStringList.Create;
      Path := ExtractFilePath(S1);
      X := FindFirst(S1, faAnyFile - faDirectory - faVolumeID, SearchRec);
      if X = 0 then
      begin
        while X = 0 do
        begin
          ListToDelete.Add(Path + SearchRec.Name);
          X := FindNext(SearchRec);
        end;
        FindClose(SearchRec);
      end;
      for X := 0 to ListToDelete.Count - 1 do
      begin
        FileSetAttr(ListToDelete[X], 0);
        DeleteFile(ListToDelete[X]);
      end;
      ListToDelete.Free;
    end;

You would call it like so: `DeleteTempRaw('C:\TEMP\MASTER*.*');`

Tip by Anon.

## Answer 2

First, fill a list with the filenames you want to delete. Here's a general purpose function to do that. Given a path (APath) and a filemask (AMask), the routine will fill AList with the full pathnames of all files matching AMask.

    procedure GetFiles(APath, AMask: string; AList: TStrings);
    var
      searchRec: SysUtils.TSearchRec;
    begin
      APath := IncludeTrailingBackslash(APath);
      {Get all of the directories in this path}
      if FindFirst(APath + '*.*', faDirectory, searchRec) = 0 then
        repeat
          with searchRec do
          begin
            if (Name <> '.') and (Name <> '..') then
              if (Attr and faDirectory > 0) then
                GetFiles(APath + Name, AMask, AList);
          end;
          Application.ProcessMessages;
        until
          FindNext(searchRec) <> 0;
      SysUtils.FindClose(searchRec);
      {Get all of the files in this directory which match the file mask}
      if FindFirst(APath + AMask, faAnyFile, searchRec) = 0 then
        repeat
          with searchRec do
          begin
            if (Name <> '.') and (Name <> '..') then
              if (Attr and faDirectory <= 0) then
                AList.Add(APath + searchRec.Name);
          end;
          Application.ProcessMessages;
        until
          FindNext(searchRec) <> 0;
      SysUtils.FindClose(searchRec);
    end;

Here's how to use the routine:

    var
      MyFileList: TStringList;
      iCnt: integer;
    begin
      MyFileList := nil;
      try
        MyFileList := TStringList.Create;
        {Get all files in C:\ and its subdirectories that match "Master*."
        (the period with nothing after it only looks for files with no extension)}
        GetFiles('c:\', 'Master*.', MyFileList);
        {Go through each file in the list and delete it}
        for iCnt := 0 to MyFileList.Count - 1 do
          bRet := DeleteFile(MyFileList[iCnt]);
      finally
        MyFileList.Free;
      end;
    end;

Tip by Anon.

## Answer 3

The classic way to solve this problem is to use a FindFirst / FindNext / FindClose loop to find the files in the target directory and delete each one as you find it, something like this:

    procedure DeleteFilesWithWildCard(Dir, Prefix, Suffix: String);
    var
      SRec: TSearchRec;
    begin
      { Check the passed parameter, make sure it ends in a backslash }
      if Length(Dir) = 0 then
        Exit;
      if Directory[Length(Dir)] <> '\' then
        AppendStr(Dir, '\' );
      if FindFirst(Dir + Prefix + '*.' + Suffix, faAnyfile, SRec ) = 0 then
      try
        { We must call FindClose after the FindFirst succeeded, otherwise
        the program looses a system handle. So use a try finally block. }
        repeat
          { We have a hit. Check if it is a directory }
          if ( faDirectory and SRec.Attr ) = 0 then
          begin
            { It is a file, try to delete it. This may fail if the file
            has the read-only attribute. If it does we show a message but
            continue with other files if the user does not abort. }
            if not SysUtils.DeleteFile(Dir + SRec.Name ) then
            begin
              if MessageDlg(
                'Cannot delete ' + SRec.Name +
                ', the file may be read-only. Do you want to abort the ' +
                'operation?',
                mtError,
                [mbYes, mbNo, mbCancel],
                0
              ) <> mrNo then
              SysUtils.Abort;
            end;
          end;
        { Done with this hit, search for the next one. }
        until
          FindNext(SRec) <> 0;
      finally
        FindClose( SRec );
      end;
    end;

Tip by Peter Below and Eddie Shipman