Technical Article

sp_GetDirInfo

,

Returns information about the files in the specified directory and/or subdirectories (if requested) and the total bytes for those files. Directory names are excluded so that the total bytes can be computed using the compute clause but you can specify that they be included at the expense of no total bytes. This sp uses the bcp utility and tries to figure out the correct path when SQL 7.0 and SQL 2000 are both installed, however, you can include a bcp path parameter for non-standard installations.

Microsoft changed the output format results of "dir /a /X /-C /TC " from Windows 2000 to Windows 2003. This updates the code to reflect the changes so the code will work on either platform.

if exists (select * 
             from dbo.sysobjects 
            where id = object_id(N'[dbo].[sp_GetDirInfo]') 
              and OBJECTPROPERTY(id, N'IsProcedure') = 1)
   drop procedure [dbo].[sp_GetDirInfo]
Go

Create procedure sp_GetDirInfo 
       @vcPrimaryPath   varchar(255) = null,
       @tiIncludeSubDir tinyint      = 0,
       @vcBcpPath       varchar(255) = null,
       @cDateType       char(1)      = 'C',
       @tiIncludeDir    tinyint      = 0
As       

/*******************************************************************/--Name        : sp_GetDirInfo
--Server      : Generic 
--Description : Captures directory information into a table. Specify
--              a particular directory (and/or its subdirectories)
--              or gather all drive file information.
--
--Parameters  : @vcPrimaryPath - Path of specified directory. Can be 
--                 a mapped drive that the SQL Server startup account  
--                 has to access. If null then all local drives are
--                 included except the C drive. Use 1 to include C.
--              @vcBcpPath - Path to bcp.exe for the current SQL 
--                 version.
--              @tiIncludeSubDir - Set to 1 to include subdirectories. 
--              @cDateType - Specifies whether to get the creation 
--                 date, last access date or the last written to date:
--                 C - creation date, A - last access date, W - last
--                 written date.
--              @tiIncludeDir - when 1 then includes directories in the
--                 listing
--
--Note        : Microsoft changed the output format of 'dir' with  
--              certain switches which requires a change in the bcp 
--              format file based on the Windows version.
-- 
--Date        : 
--Author      : Clint Herring
--
--History     : 
/*******************************************************************/
Set nocount on

-- Declare variables.
Declare @vcCmd       varchar(255),
        @vcBcpFile   varchar(255),
        @vcPath      varchar(255),
        @vcFilePath  varchar(255),
        @dtDate      datetime,
        @iPos        int, 
        @vcValue     varchar(16),
        @cVersion    char(3),
        @cWVersion   char(3),
        @vcCollation varchar(28),
        @rtn         int
      
Select @dtDate = getdate()

-- Create a table in msdb to hold directory info.
If exists(select * from msdb.dbo.sysobjects where name = 'filelist' and uid = 1)
    Drop table msdb.dbo.filelist

-- What Windows version - 2k or 2003
If (PatIndex('%Windows NT 5.2%',@@version)) > 0 -- Win 2003
   Begin
      Exec ('Use msdb Create table dbo.filelist( ' +
            'date varchar(10) null, ' +
            'time varchar(14) null, ' +
            'info varchar(15) null, ' +
            'msdosname varchar(13) null, ' +
            'filename varchar(54) null, ' +
            'path varchar(255) null, ' +
            'prcsd tinyint null)')
   End
Else
   Begin
      Exec ('Use msdb Create table dbo.filelist( ' +
            'date varchar(10) null, ' +
            'time varchar(14) null, ' +
            'info varchar(15) null, ' +
            'msdosname varchar(16) null, ' +
            'filename varchar(50) null, ' +
            'path varchar(255) null, ' +
            'prcsd tinyint null)')
   End

       
-- If a temp table exists with the following name drop it.
If (Select object_id('tempdb.dbo.##fmtfile')) > 0
   Exec ('Drop table ##fmtfile')

-- Create a temp table to load format file string information. Make
-- it a global temp table so we can bcp the data into a format file.
Create table ##fmtfile(id int identity(1,1) not null, string varchar(96) null)

-- If a temp table exists with the following name drop it.
If (Select object_id('tempdb.dbo.#fileexists')) > 0
   Exec ('Drop table #fileexists')

-- Create a temp table to work with xp_fileexist.
Create table #fileexists(
       FileExists tinyint null,
       FileIsDirectory  tinyint null,
       ParentDirectoryExists  tinyint null)

-- If a temp table exists with the following name drop it.
If (Select object_id('tempdb.dbo.#fixeddrives')) > 0
   Exec ('Drop table #fixeddrives')

-- Create a temp table to work with xp_fileexist.
Create table #fixeddrives(
       Drive char(1) null,
       FreeSpace  varchar(15) null)

-- If a temp table exists with the following name drop it.
If (Select object_id('tempdb.dbo.#DefaultBcpPath')) > 0
   Exec ('Drop table #DefaultBcpPath')

-- Create a temp table to work with xp_fileexist.
Create table #DefaultBcpPath(Value varchar(255) null)

-- Check to see if we have enough space on drive C
Insert into #fixeddrives exec master.dbo.xp_fixeddrives
If (select convert(numeric(8), FreeSpace) from #fixeddrives where drive = 'C') < 1
   Begin
      Print 'There is not enough space on drive C to create temporary directory files.'
      Print 'Please delete some files to make room before running this procedure again.'
      Return
   End

-- Check if we want to include the C drive with all other local drives
If @vcPrimaryPath = '1' -- means to include the C drive
   Set @vcPrimaryPath = null
Else 
   Delete from #fixeddrives where drive = 'C'

-- Check SQL version
If (PatIndex('%7.00 - %',@@version) > 0) -- SQL 7.0
   Begin
      -- Set version specific variables
      Set @cVersion = '6.0'
      Set @vcCollation = ''

      -- if @vcBcpPath is null then set a default bcp file path (must be short name).
      If @vcBcpPath is null
         -- Set the SQL 7.0 bcp.exe path
         Select @vcBcpPath = 'c:\mssql7\binn\'
   End
Else -- assume SQL 2000
   Begin
      -- Set version specific variables
      Set @cVersion = '8.0'
      Set @vcCollation = 'SQL_Latin1_General_CP1_CI_AS'

      -- if @vcBcpPath is null then set a default bcp file path (must be short name).
      If @vcBcpPath is null
         Begin
             -- Get the short path for 'Program Files'
            Insert into #DefaultBcpPath
            Exec @rtn = master.dbo.xp_cmdshell 'dir /X /AD c:\"Program file?"'
            If @rtn <> 0 print 'Cannot get "Program file" short name.'
            Select @iPos = charindex('~', value) from #DefaultBcpPath where value like '%Program files'
            Select @vcBcpPath = 'c:\' + substring(value,@iPos-6, 8)  
              from #DefaultBcpPath 
             where value like '%Program files'
            Truncate table #DefaultBcpPath
            -- Get the short path for 'Microsoft SQL Server'
            Insert into #DefaultBcpPath
            Exec @rtn = master.dbo.xp_cmdshell 'dir /X /AD c:\"Program files"\"Microsoft SQL Serve?"'
            If @rtn <> 0 print 'Cannot get "Microsoft SQL Server" short name.'
            Select @iPos = charindex('~', value) from #DefaultBcpPath where value like '%Microsoft SQL Server'
            Select @vcBcpPath = @vcBcpPath + '\' + substring(value,@iPos-6, 8) + '\80\tools\binn\' 
              from #DefaultBcpPath 
             where value like '%Microsoft SQL Server'
         End
   End

-- Check for bcp file.
Select @vcBcpFile = @vcBcpPath + 'bcp.exe'
Insert into #fileexists exec master.dbo.xp_fileexist @vcBcpFile

If (select ParentDirectoryExists from #fileexists) = 0
   Begin
      Select @vcCmd = 'The bcp.exe file path, ' + @vcBcpPath + 'bcp.exe, is not valid.'
      Print @vcCmd
      Return
   End
Else
   Begin
      If (select FileExists from #fileexists) = 0
         Begin
            Select @vcCmd = 'The bcp.exe file cannot be found at ' + @vcBcpPath + '.'
            Print @vcCmd
            Return
         End
      Else
         Begin
            -- See if the path contains spaces
            If (charindex(' ', @vcBcpPath) > 0)
               Begin
                  Print 'The bcp path is valid but it contains spaces.'
                  Print 'This may cause some problems running bcp.'
                  Print 'Please replace the bcp path with the msdos or short path name.'
                  Return      
               End
         End
   End

-- Set variable path. 
Select @vcPath = @vcPrimaryPath

-- Check the directory path variable.
If @vcPrimaryPath is null
   Begin

      -- The variable was null so get all fixed drives.
      Insert into msdb.dbo.filelist
      Select null, null, '<DIR>', 'Root','Root', Drive + ':\', null
        from #fixeddrives
   End
Else
   Begin
      -- We have a specific path.
      Truncate table #fileexists

      -- Remove double quotes for use with xp_fileexists.
      Select @vcPath = Replace(@vcPrimaryPath, '"', '')
      
      -- Insure we have a trailing '\'.
      If right(@vcPrimaryPath,1) <> '\'
         Begin
            Select @vcPrimaryPath = @vcPrimaryPath + '\'
            Select @vcPath = @vcPath + '\'
         End
            
      -- The variable is not null so check for a valid path.
      Insert into #fileexists exec master.dbo.xp_fileexist @vcPrimaryPath
      If ((select ParentDirectoryExists from #fileexists) = 0) or
         ((select ParentDirectoryExists from #fileexists) = 1 and
          (select FileIsDirectory from #fileexists) = 0)
         Begin
            Select @vcCmd = 'The specified path does not exist.'
            Print @vcCmd
            Return
         End
      Else
         Begin
            -- See if the path contains spaces
            If (charindex(' ', @vcPath) > 0 and charindex('"',@vcPrimaryPath) = 0)
               Begin
                  Print 'The specified path is valid but it contains spaces.'
                  Print 'This may cause some problems running bcp.'
                  Print 'Please use double quotes around folder names that contain spaces.'
                  Return
               End
            Else
               Begin
                  Insert into msdb.dbo.filelist
                  Values (null, null, '<DIR>', '<N/A>', '<Specified>', @vcPrimaryPath, null)
               End
         End
   End

-- Build a format file for directory info.
-- What Windows version - 2k or 2003
If (PatIndex('%Windows NT 5.2%',@@version)) > 0 -- Win 2003
   Begin
      Insert into ##fmtfile(string) values(@cVersion)
      Insert into ##fmtfile(string) values('7')
      Insert into ##fmtfile(string) values('1       SQLCHAR       0       10       ""        1       date      ' + @vcCollation)
      Insert into ##fmtfile(string) values('2       SQLCHAR       0       14       ""        2       time      ' + @vcCollation)
      Insert into ##fmtfile(string) values('3       SQLCHAR       0       15       ""        3       info      ' + @vcCollation)
      Insert into ##fmtfile(string) values('4       SQLCHAR       0       13       ""        4       msdosname ' + @vcCollation)
      Insert into ##fmtfile(string) values('5       SQLCHAR       0       54       "\r\n"    5       filename  ' + @vcCollation)
      Insert into ##fmtfile(string) values('6       SQLCHAR       0       0        ""        0       path      ' + @vcCollation)
      Insert into ##fmtfile(string) values('7       SQLCHAR       0       0        ""        0       prcsd     ' + @vcCollation)
   End
Else 
   Begin
      Insert into ##fmtfile(string) values(@cVersion)
      Insert into ##fmtfile(string) values('7')
      Insert into ##fmtfile(string) values('1       SQLCHAR       0       10       ""        1       date      ' + @vcCollation)
      Insert into ##fmtfile(string) values('2       SQLCHAR       0       14       ""        2       time      ' + @vcCollation)
      Insert into ##fmtfile(string) values('3       SQLCHAR       0       15       ""        3       info      ' + @vcCollation)
      Insert into ##fmtfile(string) values('4       SQLCHAR       0       16       ""        4       msdosname ' + @vcCollation)
      Insert into ##fmtfile(string) values('5       SQLCHAR       0       50       "\r\n"    5       filename  ' + @vcCollation)
      Insert into ##fmtfile(string) values('6       SQLCHAR       0       0        ""        0       path      ' + @vcCollation)
      Insert into ##fmtfile(string) values('7       SQLCHAR       0       0        ""        0       prcsd     ' + @vcCollation)
   End

-- select * from ##fmtfile order by id -- for testing

-- Bcp out the format file
Select @vcCmd = @vcBcpFile + ' "select string from ##fmtfile order by id" queryout c:\dir.fmt /c /S' + @@servername + ' /T' -- SQL 2K
-- Select @vcCmd  -- for test
Exec @rtn = master.dbo.xp_cmdshell @vcCmd, no_output
If @rtn <> 0 print 'Failed to bcp out the fmt file.'

-- exec xp_cmdshell 'type c:\dir.fmt'  -- for test            
      
-- Get inital directory
Select @vcPath = min(path) 
  from msdb.dbo.filelist 
 where info = '<DIR>' 
   and prcsd is null
   and charindex(' ', filename) = 0


-- Loop through all directories and subdirectories
While @vcPath is not null
   Begin
      -- create a text file containing directory info
      Select @vcCmd = 'dir /a /X /-C /T' + @cDateType + ' ' + @vcPath + 
                      '>c:\dir.txt'  -- location of temp file

      --Select @vcCmd  -- for test
      Exec @rtn = master.dbo.xp_cmdshell @vcCmd, no_output
      If @rtn <> 0 print 'Failed to create the dir file for path ' + @vcPath
   
      -- use findstr to create a headerless file with fixed length records
      Select @vcCmd = 'findstr /i /r /c:"/../" c:\dir.txt>c:\fdir.txt' 
      --Select @vcCmd  -- for test
      Exec @rtn = master.dbo.xp_cmdshell @vcCmd, no_output
      If @rtn <> 0 print 'Findstr failed.'
--exec xp_cmdshell 'type c:\fdir.txt'  -- for test            
      -- bcp the directory data info msdb.dbo.filelist
      Select @vcCmd = @vcBcpFile + ' msdb.dbo.filelist in ' + 
                      'c:\fdir.txt /fc:\dir.fmt /S' + @@servername + ' /T '
      --Select @vcCmd  -- for test
      Exec @rtn = master.dbo.xp_cmdshell @vcCmd, no_output
      If @rtn <> 0 print 'Loading dir info failed.'
--select * from msdb.dbo.filelist -- for test     
      -- delete records whose filename matches any created files
      Delete 
        from msdb.dbo.filelist 
       where filename = 'dir.txt' 
          or filename = 'fdir.txt' 
          or filename = 'dir.fmt'
          or filename = '.'
          or filename = '..'
      
      -- Update the processed path
      Update msdb.dbo.filelist 
         set prcsd = 1 
       where path = @vcPath
      
      -- Update the path 
      Update msdb.dbo.filelist 
         set path = @vcPath + 
                    case when charindex(' ', filename) > 0 and info = '<DIR>' 
                         then '"' + filename + '"' 
                         else filename 
                    end +
                    case when info = '<DIR>' then '\' else '' end
       Where path is null
      
      skipit:      
      -- Stop if subdirectory info was not specified 
      -- and there are no more root directories.
      If @tiIncludeSubDir = 0 or @tiIncludeSubDir is null
         Begin
            Select @vcPath = min(path) 
              from msdb.dbo.filelist 
             where info = '<DIR>' 
               and filename = 'root'
               and prcsd is null
         End
      Else
         Begin
            -- Else get next directory path if any
            Select @vcPath = min(path) 
              from msdb.dbo.filelist 
             where info = '<DIR>' 
               and prcsd is null
               and filename <> 'System Volume Information'
         End
   End

-- Remove temp files.
Select @vcCmd = 'del c:\dir.txt'
Exec master.dbo.xp_cmdshell @vcCmd, no_output
Select @vcCmd = 'del c:\fdir.txt'
Exec master.dbo.xp_cmdshell @vcCmd, no_output
Select @vcCmd = 'del c:\dir.fmt'
Exec master.dbo.xp_cmdshell @vcCmd, no_output

-- select * from msdb.dbo.filelist --for test

-- Remove directories.
If @tiIncludeDir = 0
   Delete from msdb.dbo.filelist where info = '<DIR>'

Set nocount off

-- If no file data display a message else return the data.
If (select count(*) from msdb.dbo.filelist) = 0
   Begin
      Print 'There are no files in the specified directories.'
   End
Else
   Begin
      If @tiIncludeDir = 0
         Begin
            Select date,
                   time,
                   convert(numeric(15),info)[file size (bytes)],
                   filename,
                   path  
              from msdb.dbo.filelist
             order by path, filename
           compute sum(convert(numeric(15),info))
         End
      Else
         Begin
            Select date,
                   time,
                   info,
                   filename,
                   path  
              from msdb.dbo.filelist
             order by path, filename
         End

      Set nocount on
      Select str(datediff(ss, @dtDate, getdate())/60.0,8,3) + ' minutes' [Run Time]
   End

Go

Grant Execute on dbo.sp_GetDirInfo to public
Go

Rate

You rated this post out of 5. Change rating

Share

Share

Rate

You rated this post out of 5. Change rating