Link to home
Start Free TrialLog in
Avatar of kulisncc
kulisncc

asked on

VBS Script to Scan for Duplicate Files Using UNC Path

I got this script from the Hey Scripting Guy Blog and it works great but I need it to scan a system remotely rather than having the script ran locally using a UNC path.  How can I get this script to use a UNC path rather than a local drive like it shows on line 4? Thanks.
Set objDictionary = CreateObject("Scripting.Dictionary")
Set objFSO = CreateObject("Scripting.FileSystemObject")

strStartFolder = "C:\Scripts"

Set objFolder = objFSO.GetFolder(strStartFolder)

Set colFiles = objFolder.Files

For Each objFile in colFiles
    strName = objFile.Name
    strPath = objFile.Path

    If Not objDictionary.Exists(strName) Then
        objDictionary.Add strName, strPath  
    Else
        objDictionary.Item(strName) = objDictionary.Item(strName) & ";" & strPath
    End If
Next

ShowSubfolders objFSO.GetFolder(strStartFolder)

For Each strKey in objDictionary.Keys
    strFileName = strKey
    If InStr(objDictionary.Item(strFileName), ";") Then
        arrPaths = Split(objDictionary.Item(strFileName), ";")
        Wscript.Echo strFileName
        For Each strFilePath in arrPaths
            Wscript.Echo strFilePath
        Next
        Wscript.Echo
    End If
Next

Sub ShowSubFolders(Folder)
    For Each Subfolder in Folder.SubFolders
        Set objFolder = objFSO.GetFolder(Subfolder.Path)
        Set colFiles = objFolder.Files

    For Each objFile in colFiles
        strName = objFile.Name
        strPath = objFile.Path

        If Not objDictionary.Exists(strName) Then
            objDictionary.Add strName, strPath  
        Else
            objDictionary.Item(strName) = objDictionary.Item(strName) & ";" & strPath
        End If
    Next
        ShowSubFolders Subfolder
    Next
End Sub

Open in new window

Avatar of prashanthd
prashanthd
Flag of India image

Try changing to following on line no 4

strStartFolder = "\\servername\C$\Scripts"
Avatar of kulisncc
kulisncc

ASKER

I tried that but aparently the "Set objFSO = CreateObject("Scripting.FileSystemObject")" runs only on the loal system. I researched a bit and I read that I need to utilize WMI for what I need. Does that make any sense to you? Thanks
Hi Kulisncc,
Thats not entirely true. FileSystemObject is capable of using UNC paths (as prashant states above) to gain access to a remote machine but...
- the c$ share must be available
- you will need to run your vbscript file as either a domain admin, or as someone who will have admin rights on the remote computer, and the local computer you are running it from.

What exactly do you want to do with your script? I may have something already that you can use!
Thanks for your input Scottyworld. I want to script to run on a remote system and scan for duplicate files either on the entire system or a drive that I specify using a UNC path and then write a list of all the duplicate files to a csv. It would be great to have the script do the following:

1. scan the system for duplicate files
2. when it finds duplicate files have the script create a directory structure where the names of the directories are <computername>\<drive> and export its findings to a csv file where the name of the file is the date it was ran.  
Actually, this is what I need help with:

1. Check to see if drives exist on remote system (D$, etc.)
2. If drives exists then scan each drive for duplicate files
3. Export the findings in the \\servername\\duplicate_files\D$\whatever.csv

**I do not need the script to create the directory structure for me as in step 3 because I have taken care of this with a batch.

Thanks
Hi Kulisncc,

I have something that works well, except if it comes across a folder it doesn't have access to (for e.g: c:\system volume information) then it stops checking the current drive and moves to the next.
Am happy to post the code for you, or if you can wait a day I'll see if I can get the error checking working correctly
Try the following. I've got it to output to a txt file - you can easily import that into excel
 
Set objDictionary = CreateObject("Scripting.Dictionary")
Set objFSO = CreateObject("Scripting.FileSystemObject")

Set fs = CreateObject("Scripting.FileSystemObject")
Set a = fs.CreateTextFile("c:\temp\resultsfile.txt", True) 'this is your new output file

strComputer = "myPC1"

'Get a list of all the drives on the PC
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colItems = objWMIService.ExecQuery ("Select * from Win32_LogicalDisk")

For Each objItem in colItems
	If objItem.DriveType = 3 then 'ignore cd-rom and network drives
		strStart = objItem.Name & "\" 'this will make the script start at the root of every drive, change if requierd e.g \temp
		CheckForDuplicates(strStart)
		On Error Resume Next 'if the strStart folder does not exist on the drive, script will skip to next physical drive
	End If
Next

Function CheckForDuplicates(strStartFolder)
	Set strName = nothing
	Set strPath = nothing
	
	a.WriteLine "Now checking drive " & strStartFolder
	Set objFolder = objFSO.GetFolder(strStartFolder)
	Set colFiles = objFolder.Files

	For Each objFile in colFiles
		strName = objFile.Name
		strPath = objFile.Path

		If Not objDictionary.Exists(strName) Then
			objDictionary.Add strName, strPath  
		Else
			objDictionary.Item(strName) = objDictionary.Item(strName) & ";" & strPath
		End If
	Next

	ShowSubfolders objFSO.GetFolder(strStartFolder)

	For Each strKey in objDictionary.Keys
		strFileName = strKey
		If InStr(objDictionary.Item(strFileName), ";") Then
			arrPaths = Split(objDictionary.Item(strFileName), ";")
			For Each strFilePath in arrPaths
				If Left(strFilePath,1) = Left(strStartFolder,1) then
					a.WriteLine strFilePath
				End If
			Next
		End If
	Next
End Function

Sub ShowSubFolders(Folder)
	For Each Subfolder in Folder.SubFolders
		On Error Resume Next 'if the folder is inaccessible, skip to next folder
		Set objFolder = objFSO.GetFolder(Subfolder.Path)
		Set colFiles = objFolder.Files
		
		For Each objFile in colFiles
			strName = objFile.Name
			strPath = objFile.Path
			If Not objDictionary.Exists(strName) Then
				objDictionary.Add strName, strPath  
			Else
				objDictionary.Item(strName) = objDictionary.Item(strName) & ";" & strPath
			End If
		Next
		ShowSubFolders Subfolder
	Next
End Sub

Open in new window

Very sorry, ignore the above code - that was from my test PC and will only work locally. Use this code for checking remote PCs...
 
Set objDictionary = CreateObject("Scripting.Dictionary")
Set objFSO = CreateObject("Scripting.FileSystemObject")

Set fs = CreateObject("Scripting.FileSystemObject")
Set a = fs.CreateTextFile("c:\temp\testfile.txt", True) 'this is your results output file

strComputer = "myPC1" 'name of the remote computer

'Get a list of all the drives on the PC
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colItems = objWMIService.ExecQuery ("Select * from Win32_LogicalDisk")

For Each objItem in colItems
	If objItem.DriveType = 3 then 'ignore cd-rom and network drives
		strStart = "\\" & strComputer & "\" & Left(objItem.Name,1) & "$\" 'create the complete remote PC path then make the script start at the root of every drive, change if required e.g $\temp
		CheckForDuplicates(strStart)
		On Error Resume Next 'if the strStart folder does not exist on the drive, script will skip to next physical drive
	End If
Next

Function CheckForDuplicates(strStartFolder)
	Set strName = nothing
	Set strPath = nothing
	
	a.WriteLine "Now checking drive " & strStartFolder
	Set objFolder = objFSO.GetFolder(strStartFolder)
	Set colFiles = objFolder.Files

	For Each objFile in colFiles
		strName = objFile.Name
		strPath = objFile.Path

		If Not objDictionary.Exists(strName) Then
			objDictionary.Add strName, strPath  
		Else
			objDictionary.Item(strName) = objDictionary.Item(strName) & ";" & strPath
		End If
	Next

	ShowSubfolders objFSO.GetFolder(strStartFolder)

	For Each strKey in objDictionary.Keys
		strFileName = strKey
		If InStr(objDictionary.Item(strFileName), ";") Then
			arrPaths = Split(objDictionary.Item(strFileName), ";")
			For Each strFilePath in arrPaths
				strCurrFolder = Left(strFilePath,(InStrRev(strFilePath,"$")-1))
				strCurrDrive = Left(strStartFolder,(InStrRev(strStartFolder,"$")-1))
				If StrComp(UCASE(strCurrFolder),UCASE(strCurrDrive)) = 0 then
					a.WriteLine strFilePath
				End If
			Next
		End If
	Next
End Function

Sub ShowSubFolders(Folder)

	For Each Subfolder in Folder.SubFolders
		On Error Resume Next 'if the folder is inaccessible, skip to next folder
		Set objFolder = objFSO.GetFolder(Subfolder.Path)
		Set colFiles = objFolder.Files
		
		For Each objFile in colFiles
			strName = objFile.Name
			strPath = objFile.Path
			If Not objDictionary.Exists(strName) Then
				objDictionary.Add strName, strPath  
			Else
				objDictionary.Item(strName) = objDictionary.Item(strName) & ";" & strPath
			End If
		Next
		ShowSubFolders Subfolder
	Next
End Sub

Open in new window

Awesome, I will definitely try this out. Thanks for your help Scottyworld.
Hey bro, I run the script but the testfile.txt just reads: "Now checking drive \\computername\C$\"

and that's it. The testfile.txt does not increase in size, it just stays at 1KB.
Here is code I got off the "Hey Scripting Guy Blog" from Microsoft's site. It scans a remote system for all directories and files, but I just want to change it to where it scans only for duplicate files. That's it. Can someone help me change this code to do what I need. Thanks everyone.
strComputer = "WN2790"
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")

strFolderName = "C:\"

Set colSubfolders = objWMIService.ExecQuery _
    ("Associators of {Win32_Directory.Name='" & strFolderName & "'} " _
        & "Where AssocClass = Win32_Subdirectory " _
            & "ResultRole = PartComponent")

Wscript.Echo strFolderName

arrFolderPath = Split(strFolderName, "\")
strNewPath = ""
For i = 1 to Ubound(arrFolderPath)
    strNewPath = strNewPath & "\\" & arrFolderPath(i)
Next
strPath = strNewPath & "\\"
 
Set colFiles = objWMIService.ExecQuery _
    ("Select * from CIM_DataFile where Path = '" & strPath & "'")

For Each objFile in colFiles
    Wscript.Echo objFile.Name 
Next

For Each objFolder in colSubfolders
    GetSubFolders strFolderName
Next

Sub GetSubFolders(strFolderName)
    Set colSubfolders2 = objWMIService.ExecQuery _
        ("Associators of {Win32_Directory.Name='" & strFolderName & "'} " _
            & "Where AssocClass = Win32_Subdirectory " _
                & "ResultRole = PartComponent")

    For Each objFolder2 in colSubfolders2
        strFolderName = objFolder2.Name
        Wscript.Echo
        Wscript.Echo objFolder2.Name
        arrFolderPath = Split(strFolderName, "\")
        strNewPath = ""
        For i = 1 to Ubound(arrFolderPath)
            strNewPath = strNewPath & "\\" & arrFolderPath(i)
        Next
        strPath = strNewPath & "\\"
 
        Set colFiles = objWMIService.ExecQuery _
            ("Select * from CIM_DataFile where Path = '" & strPath & "'")

        For Each objFile in colFiles
            Wscript.Echo objFile.Name 
        Next

        GetSubFolders strFolderName
    Next
End Sub

Open in new window

Hi,
Can you try running it locally on a PC - just put the local PC name in te script instead of a remote one.
The text file results would appear like that if you didn't have permissions on the remote PC.
You need to be runing the script while logged on as either a domain admin, or an account that has admin rights on the remote PC.
At line 76 if may be worth throwing in the following so you know when it finishes. Mine took ages to run on the whole of a 500gb C drive. Try setting up some test folders and duplicate files and change line 15 to $\temp just to limit down the search criteria to make it quicker

76:        wscript.echo "Finished"
Okay, I will give it another shot.
DUDE!! It does work - AWESOME!! Is there anyway to have it space between each of its findings and have the name of the duplicate files it finds as the header before the list of the locations? For instance:

somefile1.txt

c:\somefile.txt
c:\windows\somefile.txt

somefile2.txt

c:\somefile2.txt
c:\windows\somefile2.txt

Thanks again bro.
:) glad I could help. Will get back to you with the modifications in a while, but its just a case of putting a couple of    a.writeLine    lines in to put in the blank lines if you wanted to try in the meantime
Slightly more complicated than I thought as you need to keep track of the last filename checked and the current one. Try the attached script. If you don't want the "Now checking drive" lines, just comment them out.(lines 24-26)

 
Set objDictionary = CreateObject("Scripting.Dictionary")
Set objFSO = CreateObject("Scripting.FileSystemObject")

Set fs = CreateObject("Scripting.FileSystemObject")
Set a = fs.CreateTextFile("c:\temp\testfile.txt", True) 'this is your results output file

strComputer = "myPC1" 'name of the remote computer

'Get a list of all the drives on the PC
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colItems = objWMIService.ExecQuery ("Select * from Win32_LogicalDisk")

For Each objItem in colItems
	If objItem.DriveType = 3 then 'ignore cd-rom and network drives
		strStart = "\\" & strComputer & "\" & Left(objItem.Name,1) & "$\" 'create the complete remote PC path then make the script start at the root of every drive, change if required e.g $\temp
		CheckForDuplicates(strStart)
		On Error Resume Next 'if the strStart folder does not exist on the drive, script will skip to next physical drive
	End If
Next

Function CheckForDuplicates(strStartFolder)
	Set strName = nothing
	Set strPath = nothing
	a.WriteLine
	a.WriteLine
	a.WriteLine "Now checking drive " & strStartFolder
	Set objFolder = objFSO.GetFolder(strStartFolder)
	Set colFiles = objFolder.Files

	For Each objFile in colFiles
		strName = objFile.Name
		strPath = objFile.Path

		If Not objDictionary.Exists(strName) Then
			objDictionary.Add strName, strPath  
		Else
			objDictionary.Item(strName) = objDictionary.Item(strName) & ";" & strPath
		End If
	Next

	ShowSubfolders objFSO.GetFolder(strStartFolder)

	For Each strKey in objDictionary.Keys
		strFileName = strKey
		If InStr(objDictionary.Item(strFileName), ";") Then
			arrPaths = Split(objDictionary.Item(strFileName), ";")
			For Each strFilePath in arrPaths
				strCurrFolder = Left(strFilePath,(InStrRev(strFilePath,"$")-1))
				strCurrDrive = Left(strStartFolder,(InStrRev(strStartFolder,"$")-1))
				strCurrFileName = Right(strFilePath,(Len(strFilePath) - InStrRev(strFilePath,"\")))
				If StrComp(UCASE(strCurrFolder),UCASE(strCurrDrive)) = 0 then
					If strLastFileName = strCurrFileName then
						a.WriteLine strFilePath
					Else
						a.WriteLine
						a.WriteLine
						a.WriteLine strCurrFileName
						a.WriteLine
						a.WriteLine strFilePath
						strLastFileName = strCurrFileName
					End If
				End If
			Next
		End If
	Next
End Function

Sub ShowSubFolders(Folder)

	For Each Subfolder in Folder.SubFolders
		On Error Resume Next 'if the folder is inaccessible, skip to next folder
		Set objFolder = objFSO.GetFolder(Subfolder.Path)
		Set colFiles = objFolder.Files
		
		For Each objFile in colFiles
			strName = objFile.Name
			strPath = objFile.Path
			If Not objDictionary.Exists(strName) Then
				objDictionary.Add strName, strPath  
			Else
				objDictionary.Item(strName) = objDictionary.Item(strName) & ";" & strPath
			End If
		Next
		ShowSubFolders Subfolder
	Next
End Sub

Open in new window

Okay, I will try this out. Thanks brotha for your help
I swear I will leave you alone after this one last thing. How can I use a UNC path rather than a local drive to save the .txt to as in the following line:

Set a = fs.CreateTextFile("c:\temp\testfile.txt", True) 'this is your results output file

I would like to save it to something like: \\myserver\share\test.txt

Thanks alot Scottyworld

I told you I suck at VBS
There should be no reason against just putting that path into that line of code. UNC paths should work fine, as long as the user account running the script has write access to that location.

Set a = fs.CreateTextFile("\\myserver\share\test.txt", True) 'this is your results output file

Scottyworld, it worked - Sorta. I swear if you can help me out with this one last issue, I will cease any further inquiries. Here is my issue:

There are multiple drives on the system I need to scan for duplicate files, but the script that you put together seems to only scan the C: (from what I can tell from the csv file I have it exporting to) and is there any way I can get the script to prompt for a computer name to scan instead of having to edit the .vbs file itself, and to also create a folder in the UNC path I specify, then name the folder as the computer name of the system it is scanning? The problems that I seem to have with VB scripting is that I know some of the code, but I am not too skilled at piecing them together. I am very good at batch scripting but this of course is an older scripting technology and along with even VBS are becoming obsolete in the Windows world due to the advent of Powershell. So anyway, I don't understand why it is not scanning the other drives, but creating folders named after the computer names of the systems being scanned, I am presuming I would use the following code:


Would I use this code to type the computer name in an input box?
-------------------------------------------------------------------
strComputer = inputbox("Type a computer name","Enter computer name")  

if strComputer = "" then strComputer = "."  

-------------------------------------------------------------------
Then would I use this code to create a folder and then name it the computer name?
-------------------------------------------------------------------
if Not fso.FolderExists("strComputer") then
	
fso.CreateFolder "strComputer"

End If
-------------------------------------------------------------------
Plus, I am assuming I would set the above code prior to the following line of course:
-------------------------------------------------------------------
Set a =  
fs.CreateTextFile
("\\strComputer\strComputer_folder\find_duplicate_files.csv", True) 'this is your results output file

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of Scottyworld
Scottyworld
Flag of New Zealand image

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
I just ran it but again it seems to only pull from the C: - other than that, it works perfectly. I then commented out lines 29 and 33 as you suggested and it didnt run.
very odd!

can you just run the following code on your PC and see what drives it comes back with
This will lst all drives, their type, and their description. Just note down the type and description for the drive letters you want to check. I'm wondering if they are not being seen as a drivetype of 3 for some reason. This will run on a remote PC too - just change the PC number

 
strComputer ="."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colItems = objWMIService.ExecQuery ("Select * from Win32_LogicalDisk")

For Each objItem in colItems
	wscript.echo objItem.Name & ", " & objItem.DriveType & ", " & objItem.Description
Next

Open in new window

It shows that all the drives are in fact drive type 3 - I think will try running it locally and see what happens
I forgot to mention that there are two HDD's in this system. One is the C: which this script has no problem seeing but the other HDD has multiple partitions (4 to be exact). Could this be why it is not picking up the other drives?
As long as the mini script I gave you above lists all of the drives you want to check, then the full script should check them all also.
Did you try running it locally to see if it made a difference?

Have just run the script from my local PC to a remote server(which has several hard drives in it) and it has run across all of them
Well damn, I don't get it then. Maybe if I walk over to the data center, find the server I need to scan and kick the hell out of it, that will work - LOL. The output to the csv file only shows that the C$ was scanned. I will keep playing with it. I should have admin privileges on the system I need to scan. I will double check my permissions again.  
Thanks for the points - I honestly can't think of what may be causing it to stop on the C drive. Even if it can't access any files on the other drives it should still put a log in the txt file saying "now checking drive xx" (assuming you've left line 41 in)
Admin rights shouldn't be the problem, as you can access one physical drive, so the rest should be fine also.
You could try commenting out the "on error resume next" lines (maybe one at a time) to see if you can pinpoint where it is stopping - be warned though, doing this will make the script fail when it encounters a folder it cannot access (like the 'System Volume Information' on Win XP)