I have a local path like this C:\test\1\2\3\x\z\6\7\8
How do I insert a y
folder in this hierarchy between x
and z
folders using PowerShell, assuming the folder names are random but the hierarchy is always the same
Tried Get-ChildItem
Split-Path
Split-Path
Join-Path
functions but didn't get the expected results...
CodePudding user response:
Use the regex-based -replace
operation:
To match by position in the hierarchy:
'C:\test\1\2\3\x\z\6\7\8' -replace '(?<=^([^\\] \\){6})', 'y\'
Note:
For an explanation of the regex and the ability to experiment with it, see this regex 101.com page; in essence, the position after the
6
\
-separated components from the start of the path is matched (via a positive look-behind assertion,(?<=...)
), and the name of the new folder, followed by\
, is inserted (that is, the position of the match is "replaced", resulting in effective insertion).Should the real folder name (represented by
y
here) happen to contain$
chars., escape them as$$
; see below and this answer for more information.
To match by folder names, anywhere inside the hierarchy:
'C:\test\1\2\3\x\z\6\7\8' -replace '\\(x)\\(z)\\', '\$1\y\$2\'
Note:
Because
\
is a metacharacter in regexes, it must be escaped as\\
- If your concrete folder names (represented by
x
andz
here) happen to contain regex metacharacters too (say.
), they too must be\
-escaped; alternatively, escape the names as a whole with[regex]::Escape()
.
- If your concrete folder names (represented by
(...)
, i.e. capture groups are used to capture the surrounding folder names, so they can be referred to via placeholders$1
and$2
in the substitution expression (which itself is not a regular expression; only$
chars. are metacharacters there; literal use requires$$
).
CodePudding user response:
Another way to do using a DirectoryInfo
instance, a do
loop to traverse it's hierarchy, Path.Combine
method and a Stack
:
$insertAfter = 'x'
$folderToInsert = 'y'
$directory = [System.IO.DirectoryInfo] 'C:\test\1\2\3\x\z\6\7\8'
$segments = [System.Collections.Stack]::new()
do {
if($directory.BaseName -eq $insertAfter) {
$segments.Push($folderToInsert)
}
$segments.Push($directory.BaseName)
$directory = $directory.Parent
} while($directory)
[IO.Path]::Combine.Invoke([string[]] $segments)
Assuming C:\test\1\2\3\x\z\6\7\8
was a real directory, then you can use Get-Item
instead:
$directory = Get-Item 'C:\test\1\2\3\x\z\6\7\8'