ptree.ps1
<#PSScriptInfo
.VERSION 1.0.0 .GUID c7dd965c-30f4-4d68-b592-7280489c3803 .AUTHOR theplantinthedesk .COMPANYNAME N/A .COPYRIGHT .TAGS dir, tree, file, directory, powershell-script, directory-tree, directories, powershell-scripts, scripts, script .LICENSEURI https://gitlab.com/treex/treex-cli/-/blob/main/LICENSE .PROJECTURI https://gitlab.com/treex/treex-cli .ICONURI https://gitlab.com/uploads/-/system/project/avatar/66207727/folder.png .EXTERNALMODULEDEPENDENCIES .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES .RELEASENOTES .PRIVATEDATA #> <# .DESCRIPTION ptree is a PowerShell script designed to generate directory tree structures in a clear and organized format. It enables users to visualize the structure of directories and files on their system, optionally excluding specific directories, printing file contents, and saving the result to a markdown file or printing it to the console. #> param ( [string]$path = ".", [string[]]$exclude = @(), [string]$outputFile = "" ) function Write-ToFile { param ( [string]$filePath, [string]$content ) $utf8WithBom = New-Object System.Text.UTF8Encoding $true $streamWriter = New-Object System.IO.StreamWriter($filePath, $false, $utf8WithBom) $streamWriter.Write($content) $streamWriter.Close() } function Should-ExcludeItem { param ( [string]$itemName, [string[]]$excludeList ) return $excludeList -contains $itemName } function Process-FileContent { param ( [string]$filePath, [ref]$contents ) try { $fileContent = Get-Content -Path $filePath -Raw $contents.Value += "`r`n`r`n--- File: $filePath ---`r`n$fileContent`r`n" } catch { $contents.Value += "`r`n`r`n--- File: $filePath ---`r`n[Error: Could not read file contents]`r`n" } } function Print-Item { param ( [string]$itemName, [int]$depth, [bool]$isLast, [bool]$isDirectory, [string]$outputFile = "", [ref]$structure ) $indent = "`t" * $depth $branchSymbol = if ($isLast) { "└── " } else { "├── " } $line = if ($isDirectory) { "$indent$branchSymbol📁$itemName" } else { "$indent └── 📄$itemName" } if ($outputFile) { $structure.Value += $line + "`r`n" } else { Write-Host $line } } function Test-DirectoryPermissions { param( [string]$directoryPath ) try { # Test read access by listing items (this will throw an exception if no read access) Get-ChildItem -Path $directoryPath -ErrorAction Stop | Out-Null return $true } catch { Write-Error "Insufficient permissions to access directory: $directoryPath" return $false } } function Get-DirectoryTree { param ( [string]$directory, [int]$depth = 0, [bool]$isLast = $true, [string[]]$exclude = @(), [string]$outputFile = "", [ref]$structureOutput = "", [ref]$contentsOutput = "" ) # Use Resolve-Path here to get the absolute path $absoluteDirectory = Resolve-Path -Path $directory $directoryName = Split-Path -Leaf $absoluteDirectory # Check if the current directory should be excluded if (Should-ExcludeItem -itemName $directoryName -excludeList $exclude) { return } Print-Item -itemName $directoryName -depth $depth -isLast $isLast -isDirectory $true -outputFile $outputFile -structure $structureOutput # Retrieve directories and files, excluding those specified $directories = Get-ChildItem -Path $absoluteDirectory -Directory | Where-Object { -not (Should-ExcludeItem -itemName $_.Name -excludeList $exclude) } $files = Get-ChildItem -Path $absoluteDirectory -File | Where-Object { -not (Should-ExcludeItem -itemName $_.Name -excludeList $exclude) } # Combine directories and files into a single array $allItems = @($directories; $files) for ($i = 0; $i -lt $allItems.Count; $i++) { $item = $allItems[$i] $isLastItem = $i -eq ($allItems.Count - 1) if ($item.PSIsContainer) { Get-DirectoryTree -directory $item.FullName -depth ($depth + 1) -isLast $isLastItem -exclude $exclude -outputFile $outputFile -structureOutput $structureOutput -contentsOutput $contentsOutput } else { Print-Item -itemName $item.Name -depth $depth -isLast $isLastItem -isDirectory $false -outputFile $outputFile -structure $structureOutput if ($outputFile) { Process-FileContent -filePath $item.FullName -contents $contentsOutput } } } } # --- Main Script --- # Validate and sanitize path input if ([string]::IsNullOrWhiteSpace($path)) { $sanitizedPath = "." # Default to the current directory if $path is empty or whitespace } else { $sanitizedPath = $path -replace "[^\w\-\:\\\.]", "" # Sanitize } # Resolve the path to an absolute path $resolvedPath = Resolve-Path -Path $sanitizedPath if (-not (Test-Path -Path $resolvedPath -PathType Container)) { Write-Error "Invalid or inaccessible directory path: $resolvedPath" exit 1 } # Check read permissions on the resolved path if (-not (Test-DirectoryPermissions -directoryPath $resolvedPath)) { exit 1 } # Sanitize exclude list (basic) $sanitizedExclude = $exclude | ForEach-Object { $_ -replace "[^\w\-\.]", "" } $structureOutput = "" $contentsOutput = "" Get-DirectoryTree -directory $resolvedPath -exclude $sanitizedExclude -outputFile $outputFile -structureOutput ([ref]$structureOutput) -contentsOutput ([ref]$contentsOutput) if (-not [string]::IsNullOrWhiteSpace($outputFile)) { Write-ToFile -filePath $outputFile -content $structureOutput Write-ToFile -filePath "project-contents.md" -content $contentsOutput } |