copy a directory tree without files.

Hello,

I am new to Powershell and I need some help. I have a root directory with a lot of sub directories and files that also contain sub directories and files and so on. It’s a quite big tree.
I actually want to write a script that tells me what types of files any directory contains.

Path\test
│ hallo.pptx
│ hallo.txt
│ hmm.bmp
│ refe.pub
│ set
│ ta ch.txt
│ tach.txt
│ test.docx
│ test1.txt
│ tree.txt
│ tzzz.xlsx
│ wwwet.zip

├───12
│ │ rv.docx
│ │ 443g.bmp
│ │
│ └───45gg
└───3da
│ rrr.pub
│ bla.docx
│ f.txt

├───32.45
└───3543
I wrote short function that just reads all the files in a test directory and create an extentions.txt with all types of extentions in that directory.

Now I would like to mirror this root directory with all sub directories just without files.
The next step would be to run my function for each sub directory.
so I later have something that looks like this:

mirror-Path\test
│ extentions.txt

├───12
│ │ extentions.txt
│ │
│ └───45gg
│ extentions.txt

└───3da
│ extentions.txt

├───32.45
│ extentions.txt

└───3543
extentions.txt

I actually have no idea how to create this mirror directory system. It would be great to get some help.

thanks
Amit

Robocopy is your friend for this.


Robocopy source destination /S /E /XF .

great!! only one short line.
is there also a way to run my function over those directories?

thanks a lot
Amit

Function get-extensions {....
}

Get-ChildItem | where {$_.PSIsContainer} | % {
Get-extensions $_.FullName
}

I tried to use this short way with the pipelines, but it went wrong and I don’t really know how to fit my function to the way with the pipelines.
I tried to pass the function an array as parameter but for some reason it is undefined inside the function but it contains everything it should outside the function

function write_extentions_to_txt([string]$mir_path, [string]$main_path, [string[]]$sub_directories){

	write-host "print array inside the function $sub_directories"
        $sub_directory = -join $sub_directories[0]
        dir -name -file -path "$main_path\$sub_directory" > "$mir_path\$sub_directory\dir.txt"

	#declare methode_variables
	$extentions = @()
	$helping_arr =@()
	$ext =@()

	#read dir.txt and write extentions to array
	foreach($line in Get-Content "C:\Users\amit\Desktop\test_mirror\12\dir.txt") {
		$helping_arr = $line -split ""
		[array]::Reverse($helping_arr)
		$file_name_reversed= -join $helping_arr
		$ext = @($file_name_reversed.split('.'))
		$ext_str_reversed= -join $ext[0]
		$helping_arr = $ext_str_reversed -split ""
		[array]::Reverse($helping_arr)
		$ext_str= -join $helping_arr
		$extentions += $ext_str
	}

	#write extentions from array to extentions.txt
	$extentions = $extentions | select -uniq
	foreach($i in $extentions){$i >> "C:\Users\amit\Desktop\test_mirror\12\extentions.txt"}
}

And here I fill the array

#create array of directory paths
$sub_directories =@()
dir -Path $source  -directory -name -recurse > directories.txt
foreach($line in Get-Content "directories.txt"){
	$sub_directories += $line
	write-host "hola $sub_directories"
}
write-host "print array outside the function $sub_directories"


write_extentions_to_txt($dest, $source, $sub_directories)

thanks
Amit

If you could give us an example of what the extension.txt file looks like that might help. If you’re just looking for a extensions.txt file in every folder listing the unique extensions you could just do this. If a folder is empty it doesn’t create a extensions.txt file. If you need empty directories included just remove the “-and $_.GetFiles().Count -gt 0” from the 1st line. I also exclude the extensions.txt from being included in the unique extensions list.

Get-ChildItem -Path $source -Recurse | where {$_.PSIsContainer -and $_.GetFiles().Count -gt 0 } | % {
Get-ChildItem -path $_.FullName | ? { $_.Name -notmatch "extensions.txt"} | select Extension -Unique | Out-File -FilePath "$($_.FullName)\Extensions.txt"
}

Hope this helps.

here you have an example for the test directory. It contains all the extentions listed in the test directory you see in the OT.
(except of set, it’s a bug but I can live with it right now)

path/test/extentions.txt:
pptx
txt
bmp
pub
set
lnk
docx
xlsx
zip

I will try to apply the code above and will post if it works.

I really don’t get the Point with the scope of variables in PS. So I reduced any unnecessary code from the example above just to make sure there is no other mistake and I really don’t understand it.

function doSomething{
param([string]$dest, [string]$source, [string[]] $sub_directories)
write-host "directories: $sub_directories"
write-host "source: $source"
write-host "destination: $dest"
}


#mirror the directory system
$source = "C:\Users\u9189\Desktop\test"
$dest = "C:\Users\u9189\Desktop\test_mirror"


#create array of directory paths
$sub_directories =@()
dir -Path $source  -directory -name -recurse > directories.txt
foreach($line in Get-Content "directories.txt"){
	$sub_directories += $line
	
}
write-host "source before calling function: $source"
write-host "sub_directories before calling function: $sub_directories"
write-host "Enter function:"
doSomething($dest, $source, $sub_directories)
write-host "left function:"
write-host "source after calling function: $source"
write-host "sub_directories after calling function: $sub_directories"

Read-Host "Press Enter to exit..."
exit
source before calling function: C:\Users\amit\Desktop\test sub_directories before calling function: 12 3da 12\45gg 3da\32.45 3da\3543 Enter function: directories: source: destination: C:\Users\amit\Desktop\test_mirror C:\Users\amit\Desktop\test System.Object[] left function: source after calling function: C:\Users\amit\Desktop\test sub_directories after calling function: 12 3da 12\45gg 3da\32.45 3da\3543 Press Enter to exit...:

If the syntax is wrong then why is it wrong for $source but not for $destination?

Sorry if I am not understanding the question and my code isn’t understandable. I’m just trying to help. To mirror folders including subdirectories I suggest using robocopy and not powershell. You just run “robocopy /s /e /xf .” This could also be called from powershell if you prefer.

To get a unique extensions.txt file in every non-empty folder in every subdirectory run the code below. Here is an overview of what it’s doing.

#Recursively grabbing all files and folders from the $source path.
# Get-Childitem -path $source -recurse

# Select only directories,(PSIsContainer) and directories that have files,(Getfiles)
# where {$_.PSIsContainer -and $_.GetFiles().Count -gt 0 } 

# Get the items,(files) in the folder list from the above filtered get-childitem command
#Get-ChildItem -path $_.FullName

# Exclude the extensions.txt file
# ? { $_.Name -notmatch "extensions.txt"}

# select only the unqiue extensions in that folder
# select Extension -Unique 

# Export the results to extensions.txt file in the current folder.
# Out-File -FilePath "$($_.FullName)\Extensions.txt"

Get-ChildItem -Path $source -Recurse | where {$_.PSIsContainer -and $_.GetFiles().Count -gt 0 } | % {
Get-ChildItem -path $_.FullName | ? { $_.Name -notmatch "extensions.txt"} | select Extension -Unique | Out-File -FilePath "$($_.FullName)\Extensions.txt"
}

Sorry if I am double posting but I don’t see my post in this list even though it says I posted last.
Sorry if I am not understanding the question and my code isn’t understandable. I’m just trying to help. To mirror folders including subdirectories I suggest using robocopy and not powershell. You just run “robocopy /s /e /xf .” This could also be called from powershell if you prefer.

To get a unique extensions.txt file in every non-empty folder in every subdirectory run the code below. Here is an overview of what it’s doing.

#Recursively grabbing all files and folders from the $source path.
# Get-Childitem -path $source -recurse

# Select only directories,(PSIsContainer) and directories that have files,(Getfiles)
# where {$_.PSIsContainer -and $_.GetFiles().Count -gt 0 } 

# Get the items,(files) in the folder list from the above filtered get-childitem command
# Get-ChildItem -path $_.FullName

# Exclude the extensions.txt file
# ? { $_.Name -notmatch "extensions.txt"}

# select only the unqiue extensions in that folder
# select Extension -Unique 

# Export the results to extensions.txt file in the current folder.
# Out-File -FilePath "$($_.FullName)\Extensions.txt"

Get-ChildItem -Path $source -Recurse | where {$_.PSIsContainer -and $_.GetFiles().Count -gt 0 } | % {
Get-ChildItem -path $_.FullName | ? { $_.Name -notmatch "extensions.txt"} | select Extension -Unique | Out-File -FilePath "$($_.FullName)\Extensions.txt"
}

If you wanted the powershell script to create the same directory structure it scanned from $source you could add this line before the 2nd Get-Childitem. It will create the same directory structure in $dest as found in $source without any files.

new-item -ItemType Directory "$dest$(Split-path $_.FullName -Resolve -NoQualifier)" -force

I couldn’t reach Powershell.org the last days. I kind of found a worksaround. This piece of code actually works fine except for paths that are longer then 260chars.
Here’s the complete script:

function write_extentions_to_txt{
	$source = "C:\Users\...\test"
	$dest = "C:\Users\...\test_mirror"
	robocopy $source $dest /e /xf *.*
	dir -Path $source  -directory -name -recurse > $dest\directories.txt

	$sub_directories =@()
	$sub_directories += ""
	foreach($line in Get-Content "$dest\directories.txt"){
		$sub_directories += $line
	}
	
	foreach($directory in $sub_directories){
		$sub_directory= -join $directory
		$old_extention_file = "$dest\$sub_directory\extentions.txt"	
		#remove extentions.txt and create write dir.txt
		if (Test-Path $old_extention_file) {
				Remove-Item $old_extention_file
		}
		
		
		dir -name -file -path "$source\$sub_directory" > "$dest\$sub_directory\dir.txt"
		$extentions = @()
		$helping_arr =@()
		$ext =@()
		#read dir.txt and write extentions to array
		foreach($line in Get-Content "$dest\$sub_directory\dir.txt") {
			$helping_arr = $line -split ""
			[array]::Reverse($helping_arr)
			$file_name_reversed= -join $helping_arr
			$ext = @($file_name_reversed.split('.'))
			$ext_str_reversed= -join $ext[0]
			$helping_arr = $ext_str_reversed -split ""
			[array]::Reverse($helping_arr)
			$ext_str= -join $helping_arr
			$extentions += $ext_str
		}
		#write extentions from array to extentions.txt
		$extentions = $extentions | select -uniq
		foreach($i in $extentions){$i >> "$dest\$sub_directory\extentions.txt"}
		#delete dir.txt
	    Remove-Item -Path "$dest\$sub_directory\dir.txt"	
	}
}

write_extentions_to_txt

Read-Host "Press Enter to exit..."
exit

Still would like to understand your way so I am Playing around with your code. I understand it now in general but I don’t really get how to fit it. Guess that you have to know what kind of functions and attributes already exists in PS.
Btw what do you mean with robocopy is not Powershell?