Skip to content

Commit ab0d599

Browse files
committed
Release 1.118.2025
1 parent f25ee6c commit ab0d599

26 files changed

Lines changed: 2639 additions & 1425 deletions

Functions/GenXdev.FileSystem/Expand-Path.ps1

Lines changed: 145 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,59 @@ function Expand-Path {
5555
Mandatory = $false,
5656
HelpMessage = "Will delete the file if it already exists"
5757
)]
58-
[switch] $DeleteExistingFile
58+
[switch] $DeleteExistingFile,
59+
########################################################################
60+
[Parameter(
61+
Mandatory = $false,
62+
HelpMessage = "Will force the use of a specific drive"
63+
)]
64+
[char] $ForceDrive = '*',
65+
########################################################################
66+
[Parameter(
67+
Mandatory = $false,
68+
HelpMessage = "Will throw if file does not exist"
69+
)]
70+
[switch] $FileMustExist,
71+
########################################################################
72+
[Parameter(
73+
Mandatory = $false,
74+
HelpMessage = "Will throw if directory does not exist"
75+
)]
76+
[switch] $DirectoryMustExist
5977
########################################################################
6078
)
6179

6280
begin {
6381

6482
# normalize path separators and remove double separators
65-
$normalizedPath = $FilePath.Trim().Replace("\", [IO.Path]::DirectorySeparatorChar).
66-
Replace("/", [IO.Path]::DirectorySeparatorChar)
83+
[string] $normalizedPath = $FilePath.Trim().Replace("\", [IO.Path]::DirectorySeparatorChar).
84+
Replace("/", [IO.Path]::DirectorySeparatorChar);
85+
86+
if ($normalizedPath.StartsWith([IO.Path]::DirectorySeparatorChar + [IO.Path]::DirectorySeparatorChar)) {
87+
88+
$normalizedPath = [IO.Path]::DirectorySeparatorChar + [IO.Path]::DirectorySeparatorChar +
89+
$normalizedPath.Substring(2).Replace(
90+
[IO.Path]::DirectorySeparatorChar + [IO.Path]::DirectorySeparatorChar,
91+
[IO.Path]::DirectorySeparatorChar
92+
)
93+
94+
if (($ForceDrive -ne '*') -and
95+
("ABCDEFGHIJKLMNOPQRSTUVWXYZ".IndexOf(($ForceDrive -as [string]).ToUpperInvariant()) -ge 0)) {
96+
97+
$i = $normalizedPath.IndexOf([IO.Path]::DirectorySeparatorChar, 2);
98+
$normalizedPath = $ForceDrive + ":" + (
99+
100+
$i -lt 0 ? ([IO.Path]::DirectorySeparatorChar) : $normalizedPath.Substring($i)
101+
)
102+
}
103+
}
104+
else {
105+
106+
$normalizedPath = $normalizedPath.Replace(
107+
[IO.Path]::DirectorySeparatorChar + [IO.Path]::DirectorySeparatorChar,
108+
[IO.Path]::DirectorySeparatorChar
109+
)
110+
}
67111

68112
# check if path ends with a directory separator
69113
$hasTrailingSeparator = $normalizedPath.EndsWith(
@@ -75,14 +119,49 @@ function Expand-Path {
75119

76120
# expand home directory if path starts with ~
77121
if ($normalizedPath.StartsWith("~")) {
78-
$normalizedPath = Join-Path (Resolve-Path ~).Path `
79-
$normalizedPath.Substring(1)
122+
123+
if (($ForceDrive -ne '*') -and
124+
("ABCDEFGHIJKLMNOPQRSTUVWXYZ".IndexOf(($ForceDrive -as [string]).ToUpperInvariant()) -ge 0)) {
125+
126+
$i = $normalizedPath.IndexOf([IO.Path]::DirectorySeparatorChar, 1);
127+
$normalizedPath = $ForceDrive + ":" + (
128+
129+
$i -lt 0 ? [IO.Path]::DirectorySeparatorChar + "**" + [IO.Path]::DirectorySeparatorChar : ("\**" + $normalizedPath.Substring($i))
130+
)
131+
}
132+
else {
133+
134+
$normalizedPath = Join-Path (Convert-Path ~) `
135+
$normalizedPath.Substring(1)
136+
}
80137
}
81138

82-
# handle absolute paths (drive letter or UNC)
83139
if ((($normalizedPath.Length -gt 1) -and
84-
($normalizedPath.Substring(1, 1) -eq ":")) -or
85-
$normalizedPath.StartsWith("\\")) {
140+
($normalizedPath.Substring(1, 1) -eq ":"))) {
141+
142+
if (($ForceDrive -ne '*') -and
143+
("ABCDEFGHIJKLMNOPQRSTUVWXYZ".IndexOf(($ForceDrive -as [string]).ToUpperInvariant()) -ge 0)) {
144+
$i = $normalizedPath.IndexOf([IO.Path]::DirectorySeparatorChar);
145+
$normalizedPath = $ForceDrive + ":" + [IO.Path]::DirectorySeparatorChar + (($i -eq -1 -and $normalizedPath.Length -gt 2) -or $i -eq 2 ? "**" + [IO.Path]::DirectorySeparatorChar : "") + $normalizedPath.Substring(2)
146+
}
147+
else {
148+
149+
if (($normalizedPath.Length -lt 3) -or ($normalizedPath.Substring(2, 1) -ne [System.IO.Path]::DirectorySeparatorChar)) {
150+
151+
Push-Location $normalizedPath.Substring(0, 2)
152+
try {
153+
$normalizedPath = "$(Get-Location)$([IO.Path]::DirectorySeparatorChar)$($normalizedPath.Substring(2))"
154+
$normalizedPath = [System.IO.Path]::GetFullPath($normalizedPath)
155+
}
156+
finally {
157+
Pop-Location
158+
}
159+
}
160+
}
161+
}
162+
163+
# handle absolute paths (drive letter or UNC)
164+
if ($normalizedPath.StartsWith([IO.Path]::DirectorySeparatorChar + [IO.Path]::DirectorySeparatorChar)) {
86165

87166
try {
88167
$normalizedPath = [System.IO.Path]::GetFullPath($normalizedPath)
@@ -92,6 +171,40 @@ function Expand-Path {
92171
}
93172
}
94173
else {
174+
175+
if (($ForceDrive -ne '*') -and
176+
("ABCDEFGHIJKLMNOPQRSTUVWXYZ".IndexOf(($ForceDrive -as [string]).ToUpperInvariant()) -ge 0)) {
177+
178+
if ($normalizedPath.Length -lt 2 -or $normalizedPath.Substring(1, 1) -ne ":") {
179+
180+
$newPath = $ForceDrive + ":" + [IO.Path]::DirectorySeparatorChar;
181+
182+
while ($normalizedPath.StartsWith(".")) {
183+
184+
$i = $normalizedPath.IndexOf([IO.Path]::DirectorySeparatorChar);
185+
if ($i -lt 0) {
186+
187+
$normalizedPath = ""
188+
}
189+
else {
190+
191+
$normalizedPath = $normalizedPath.Substring($i + 1)
192+
}
193+
}
194+
195+
if ($normalizedPath.StartsWith([IO.Path]::DirectorySeparatorChar)) {
196+
197+
$newPath += $normalizedPath
198+
}
199+
else {
200+
201+
$newPath += "**" + [IO.Path]::DirectorySeparatorChar + $normalizedPath
202+
}
203+
204+
$normalizedPath = $newPath
205+
}
206+
}
207+
95208
# handle relative paths
96209
try {
97210
$normalizedPath = [System.IO.Path]::GetFullPath(
@@ -102,6 +215,30 @@ function Expand-Path {
102215
}
103216
}
104217

218+
# handle directory/file creation if requested
219+
if ($DirectoryMustExist -or $FileMustExist) {
220+
221+
# get directory path accounting for trailing separator
222+
$directoryPath = if ($hasTrailingSeparator) {
223+
[IO.Path]::TrimEndingDirectorySeparator($normalizedPath)
224+
}
225+
else {
226+
[IO.Path]::TrimEndingDirectorySeparator(
227+
[System.IO.Path]::GetDirectoryName($normalizedPath))
228+
}
229+
230+
# create directory if it doesn't exist
231+
if ($DirectoryMustExist -and (-not [IO.Directory]::Exists($directoryPath))) {
232+
233+
throw "Directory does not exist: $directoryPath"
234+
}
235+
236+
if ($FileMustExist -and (-not [IO.File]::Exists($normalizedPath))) {
237+
238+
throw "File does not exist: $normalizedPath"
239+
}
240+
}
241+
105242
# handle directory/file creation if requested
106243
if ($CreateDirectory -or $CreateFile) {
107244

Functions/GenXdev.FileSystem/Find-DuplicateFiles.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Find-DuplicateFiles -Paths "C:\Photos","D:\Backup\Photos"
2828
function Find-DuplicateFiles {
2929

3030
[CmdletBinding()]
31+
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "")]
3132
[Alias("fdf")]
3233

3334
param(
@@ -59,11 +60,10 @@ function Find-DuplicateFiles {
5960
)
6061

6162
begin {
62-
6363
# convert all input paths to full filesystem paths
6464
$normalizedPaths = @()
6565
$Paths | ForEach-Object {
66-
$normalizedPaths += (Expand-Path $_)
66+
$normalizedPaths += (GenXdev.FileSystem\Expand-Path $_)
6767
}
6868

6969
# internal helper function to generate unique comparison key for each file

0 commit comments

Comments
 (0)