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
yhere) 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
xandzhere) 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$1and$2in 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'
