Powershell: How to extract part a file name - powershell

I have a file that follows this naming convention: project.customer.version-number.zip
So for example i might have a file called: project.customer.1.1.889.zip
The "version-number" will change.
"project" and "customer" will stay the same. The zip file extension will always be the same.
I long way to get it I have found is to use multiple Split paths and then concatenate together to get the full version number. So i would have:
$1 = (Split-Path -Path $filePath -Leaf).Split(".")[2];
$2 = (Split-Path -Path $filePath -Leaf).Split(".")[3];
$3 = (Split-Path -Path $filePath -Leaf).Split(".")[4];
$version = $1 + "." + $2 "." + $3
Is there a quicker / better way to extract just the version number (i.e. the 1.1.889) if i don't know what it will be every time with powershell?

You can limit the number of item that split returns. In this case you just want to split it into Project, Customer, and Version. First we'll reduce it to just the file name with Split-Path -leaf, then we remove the file extension with -replace, and then we'll split the remaining file base name 3 times.
$project,$customer,$version = ((Split-Path -Path $filePath -Leaf) -replace '\.zip$').Split(".",3)

Related

Powershell: Incrementing number while ignoring string tag

this is my first stack question so go easy on me.
Currently working on a project to create a new folder on a network drive by incrementing off of the previous folders version number.
For example:
5.2.0.0110 -> 5.2.0.0111
Here is my current powershell solution that does the trick:
$SourceFolder = "\\corpfs1\setup\ProtectionSuite\Version 5.2.0.x\5.2.0.0001"
$DestinationFolder = "\\corpfs1\setup\ProtectionSuite\Version 5.2.0.x"
$msiSourceFolder = "\\SourceMsiPath"
$exeSourceFolder = "\\SourceExePath"
if (Test-Path $SourceFolder)
{
$latest = Get-ChildItem -Path $DestinationFolder| Sort-Object Name -Descending | Select-Object -First 1
#split the latest filename, increment the number, then re-assemble new filename:
$newFolderName = $latest.BaseName.Split('.')[0] + "." + $latest.BaseName.Split('.')[1] + "."+ $latest.BaseName.Split('.')[2] + "." + ([int]$latest.BaseName.Split('.')[3] + 1).ToString().PadLeft(4,"0")
New-Item -Path $DestinationFolder"\"$newFolderName -ItemType Directory
Copy-Item $msiSourceFolder -Destination $DestinationFolder"\"$newFolderName
Copy-Item $exeSourceFolder -Destination $DestinationFolder"\"$newFolderName
}
However, one thing that this does not account for is version numbers with string at the end. This solution attempts to covert the string -> int which fails. Some of the folders have strings as they are for internal releases so there is no way to just change my naming semantics.
For example: 5.2.0.1234 (eng) -> 5.2.0.1235
I would like to ignore any text after the last four digits and increment as shown in the example above. If anyone has a suggestion I am all ears! Thank you.
You can do:
$version = ($latest.BaseName -replace '^((?:\d+\.){3}\d{4}).*', '$1').Split('.')
$version[-1] = '{0:D4} -f ([int]$version[-1] + 1)
$newFolderName = $version -join '.'
# '5.2.0.0110 (eng)' --> '5.2.0.0111'
As per your comment, you should use Join-Path for constructing the full target path:
$targetPath = Join-Path -Path $DestinationFolder -ChildPath $newFolderName
$null = New-Item -Path $targetPath -ItemType Directory -Force
Copy-Item $msiSourceFolder -Destination $targetPath
Copy-Item $exeSourceFolder -Destination $targetPath
Assuming that your folder names contain only one 4-digit sequence preceded by a ., it is simpler to match and replace only it, using the regular-expression-based -replace operator with a script block-based substitution:
Update:
A later clarification revealed that the post-version suffix in the input string should be (b) removed from the output rather than (a) just ignored for the purpose of incrementing while getting preserved in the output - see the bottom section for a solution to (b).
SOLUTION (a): If the post-version suffix should be preserved:
In PowerShell (Core) v6.1+:
# Replace the sample value '5.2.0.1234 (eng)' with the following in your code:
# $newFolderName = $latest.BaseName [-replace ...]
'5.2.0.1234 (eng)' -replace '(?<=\.)\d{4}', { '{0:0000}' -f (1 + $_.Value) }
The above yields 5.2.0.1235 (eng) - note the incremented last version-number component and the preservation of the suffix.
In Windows PowerShell (versions up to 5.1), where script block-based substitutions aren't supported, direct use of the underlying .NET API is required:
[regex]::Replace('5.2.0.0110 (eng)', '(?<=\.)\d{4}', { '{0:0000}' -f (1 + $args[0].Value) })
Explanation:
(?<=\.)\d{4} is a regex (regular expression) that matches a literal . (\.) inside a look-behind assertion ((?<=...)), followed by 4 ({4}) digits (\d). The look-behind assertion ensures that the literal . isn't included in the text captured by the match.
The script block ({ ... }) receives information about (each) match, as a System.Text.RegularExpressions.Match instance, via the automatic $_ variable in the PowerShell (Core) solution, via the automatic $args variable in the Windows PowerShell solution with the direct .NET call.
The script block's output (return value) is used to replace the matched text:
'{0:0000}' -f ... uses -f, the format operator, to format the RHS with 4-digit 0-padding.
(1 + $_.Value) / (1 + $args[0].Value) adds 1 to the 4-digit sequence captured by the match, which is implicitly converted to a number due to the LHS of the + operation being a number.
SOLUTION (b): If the post-version suffix should be removed:
In PowerShell (Core) v6.1+:
'5.2.0.1234 (eng)' -replace '\.(\d{4}).*', { '.{0:0000}' -f (1 + $_.Groups[1].Value) }
The above yields 5.2.0.1235 - note the incremented last version-number component and the absence of the suffix.
In Windows PowerShell:
[regex]::Replace('5.2.0.1234 (eng)', '\.(\d{4}).*', { '.{0:0000}' -f (1 + $args[0].Groups[1].Value) })

Get a specific folder name from path

How to get the 4th folder name and store it in a variable while looping through the files stored in a parent folder. For example, if the path is
C:\ParentFolder\Subfolder1\subfolder2\subfolder3\file.extension
C:\ParentFolder\Subfolder1\subfolder2\subfolder4\file.extension
C:\ParentFolder\Subfolder1\subfolder2\subfolder5\file.extension
then subfolder2 name should be stored in a variable. Can any one please help me on this?
get-childitem $DirectorNane -Recurse | Where-Object{!($_.PSIsContainer)} | % {
$filePath = $_.FullName
#Get file name
$fileName = Split-Path -Path $filePath -Leaf
} $FileI = Split-Path -Path $filePath -Leaf
Thanks in advance!
You can use the -split operator on the $filePath variable to get what you want.
$split = $filePath -split '\\'
$myvar = $split[3]
We use the double backslash to split with, because the first slash escapes the slash character to split the path by. Then, we can reference the part of the path we want in the array that gets generated in the "split" variable.
Additionally, you can solve this with a one liner using the following code:
$myvar = $filepath.split('\')[3]
This would ensure that you're always getting the fourth element in the array, but is a little less flexible since you can't specify what exactly you want based on conditions with additional scripting.
If you are asking how to get the parent directory of the directory containing a file, you can call Split-Path twice. Example:
$filePath = "C:\ParentFolder\Subfolder1\subfolder2\subfolder3\file.extension"
$parentOfParent = Split-Path (Split-Path $filePath)
# $parentOfParent now contains "C:\ParentFolder\Subfolder1\subfolder2"

Get file name only from a given path

I have a file which will have full path of files. For example:
servername\xyz\abc.txt
servername\pqr\ab1.txt
I need to get only file name (abc.txt and ab1.txt) using PowerShell.
Instead of using the resources getting the file you can use Split-Path on each line item. -Leaf is the switch needed to return just the file name.
PS M:\Scripts> split-path "servername\pqr\ab1.txt" -Leaf
ab1.txt
If you had this information in a file you could iterate through all the entires
$fileName = Get-Content fullfilepaths.txt | ForEach-Object{split-path $_ -Leaf}
Another
A simple one that would also work ( Not sure if the performance is different ) would be to use split and return the last element.
("servername\pqr\ab1.txt".Split("\"))[-1]
PSH$ $file = Get-ChildItem "servername\pqr\ab1.txt"
PSH$ $file.Name
PSH$ $file.Name
should return ab1.txt

Powershell Copying files with varying folders in the path

Right up front apologies for my lack of knowledge with Powershell. Very new to the language . I need to copy some files located in a certain path to another similar path. For example:
C:\TEMP\Users\<username1>\Documents\<varyingfoldername>\*
C:\TEMP\Users\<username2>\Documents\<varyingfoldername>\*
C:\TEMP\Users\<username3>\Documents\<varyingfoldername>\*
C:\TEMP\Users\<username4>\Documents\<varyingfoldername>\*
etc....
to
C:\Files\Users\<username1>\Documents\<varyingfoldername>\*
C:\Files\Users\<username2>\Documents\<varyingfoldername>\*
C:\Files\Users\<username3>\Documents\<varyingfoldername>\*
C:\Files\Users\<username4>\Documents\<varyingfoldername>\*
etc....
So basically all files and directories from path one need to be copied to the second path for each one of the different paths. The only known constant is the first part of the path like C:\TEMP\Users...... and the first part of the destination like C:\Files\Users.....
I can get all the different paths and files by using:
gci C:\TEMP\[a-z]*\Documents\[a-z]*\
but I am not sure how to then pass what's found in the wildcards so I can use them when I do the copy. Any help would be appreciated here.
This should work:
Get-ChildItem "C:\TEMP\*\Documents\*" | ForEach-Object {
$old = $_.FullName
$new = $_.FullName.Replace("C:\TEMP\Users\","C:\Files\Users\")
Move-Item $old $new
}
For additional complexity in matching folder levels, something like this should work:
Get-ChildItem "C:\TEMP\*\Documents\*" -File | ForEach-Object {
$old = $_.FullName
$pathArray = $old.Split("\") # Splits the path into an array
$new = [system.String]::Join("\", $pathArray[0..1]) # Creates a starting point, in this case C:\Temp
$new += "\" + $pathArray[4] # Appends another folder level, you can change the index to match the folder you're after
$new += "\" + $pathArray[6] # You can repeat this line to keep matching different folders
Copy-Item -Recurse -Force $old $new
}

Reversing a file name based on delimiter then truncating part

I need to rename many hundreds of files to follow a new naming convention, but I'm having awful trouble. This really needs to be scripted in powershell or VBS so we can automate the task in a regular basis.
Original File Name
Monday,England.txt
New File Name
EnglanMo
Convention Rules:
The file name is reversed around the delimiter (,) to England,Monday and then truncated to 6/2 char
Englan,Mo
The Delimiter is then removed
englanmo.txt
Say we had Wednesday,Spain.txt spain being 5 char, this is not subject to any reduction
SpainWe.txt
All the txt files can be accessed in one directory, or from a CSV, whatever is easiest.
Without having the exact details of your file paths, where it'll run, etc. you'll have to adapt this to point at the appropriate path(s).
$s= "Monday,England.txt";
#$s = "Wednesday,Spain.txt";
$nameparts = $s.split(".")[0].split(",");
if ($nameparts[1].length -gt 6) {
$newname = $nameparts[1].substring(0,6);
} else {
$newname = $nameparts[1];
}
if ($nameparts[0].length -gt 2) {
$newname += $nameparts[0].substring(0,2);
} else {
$newname += $nameparts[0];
}
$newname = $newname.toLower() + "."+ $s.split(".")[1];
$newname;
get-item $s |rename-item -NewName $newname;
I'm certain this isn't the most efficient/elegant way to do this, but it works with both of your test cases.
Use Get-ChildItem to grab the files, then on files that match your criteria, use regular expressions to capture the first two characters of the day of the week and the first six characters of the location, then use those captures to create a new filename. This is my best guess. Use -WhatIf on the Move-Item cmdlet until you get the regular expression and the destination path correct.
Get-ChildItem C:\Path\To\Files *.txt |
Where-Object { $_.BaseName -matches '^([^,]{2})[^,]*,(.{1,6})' } |
Move-Item -WhatIf -Destination {
$newFileName = '{0}{1}.txt' -f $matches[1],$matches[2]
Join-Path C:\Path\To\Files $newFileName.ToLower()
}
I think you should be able to achieve this by splitting the string into arrays in powershell and then recording the array to get your reverse.
For example:
$fileNameExtension = "Monday,England.txt";
$fileName = $fileNameExtension.split("."); // gets you an array [Monday,England][txt]
$fileparts = $fileName.split(","); // gets you an array [Monday][England]
//Create the new substring parts, notice you can now pull items from the array in any order you need,
//You will need to check the length before using substringing
$part1 = $fileparts[1].substring(0,5);
$part2 = $fileparts[0].substring(0,2);
//Now construct the new file name by rebuilding the string
$newfileName = $part1 + $part2 + “.” + $fileName[1];