Split and add text/number to the filename - powershell

I have been trying to write a script to remove end part of the filename and replace it with a version number of a build. I have tried trim and split but because of extra dots and not good with regex, im having problems.
These are file examples:
Filename.Api.sow.0.1.1856.nupkg
something.Customer.Web.0.1.1736.nupkg
I want to remove 0.1.18xx from these filenames and add a build number from variable. which would be something like 1.0.1234.1233 (major.minor.build.revision)
so the end result should be:
Filename.Api.sow.1.0.1234.1233.nupkg
something.Customer.Web.1.0.112.342.nupkg
Here is my try to split and then rename. But it doesnt work.
$files = Get-ChildItem -Recurse | where {! $_.PSIsContainer}
foreach ($file in $files)
{
$name,$version = $file.Name.Split('[0-9]',2)
Rename-Item -NewName "$name$version" "$name".$myvariableforbuild
}

You are almost there. Here a solution with regex:
$myVariableForBuild = '1.0.1234.1233'
Get-ChildItem 'c:\your_path' -Recurse |
where {! $_.PSIsContainer} |
Rename-Item -NewName { ($_.BaseName -replace '\d+\.\d+\.\d+$', $myVariableForBuild) + $_.Extension }

Probably not the most concise way to do it, but I would split the string based on ".", get the last element of the array (the file extension), then iterate through each array element. If its non-numeric append it to a new string, if numeric break the loop. Then append the new version and file extension to the new string.
$str = "something.Customer.Web.0.1.1736.nupkg"
$arr = $str.Split(".")
$extension = $arr[$arr.Count - 1]
$filename = ""
$newversion = "1.0.112.342"
for ($i = 0 - 1; $i -lt $arr.Count; $i++)
{
if ($arr[$i] -notmatch "^[\d\.]+$")
{
# item is not numeric - add to string
$filename += $arr[$i] + "."
}
else
{
# item is numeric - end loop
break
}
}
# add the new version
$filename += $newversion + "."
# add the extension
$filename += $extension
Obviously its not a complete solution to your problem, but there is enough there for you to get going.

Related

remove filename between specific character

I need Help with powershell
I need to remove the number 10 between 2nd and 3rd minus sign '-'
ABCDE-2020-10-20.txt
HIJKL-2020-10-20.txt
MNOPQ-2020-10-20.txt
RSTUV-2020-10-20.txt
so far this is what I got for renaming logic but I'm having problem getting the filename
foreach ($s in $files) {
$newString = $s-replace "-", ""
$count = $s.Length - $newString.Length
if ($count -ge 3) {
$s01=$s.indexof("-",0)
$s02=$s.indexof("-",$s.indexof("-",0)+1)
$s.substring(0,$s01) + $s.substring($s02)
}
}
$files = (Get-ChildItem -path $folder -recurse -File).FullName
.fullname this will get me full path but my folder name contain - too,
.name will get me only file name but is there anyway to rename with just the filename ?
Based on your code, its not clear if your overall goal is removing the date or just the -10, but here is a few ideas.
$string -replace '\-\d{4}\-\d{2}\-\d{2}'
ABCDE.txt
$string -replace '(\-\d\d\W)','-'
ABCDE-2020-20.txt
$string -replace '(\-\d\d\b)'
ABCDE-2020.txt

Powershell replace from array strange behaviour

Problem is solved, but I don't understand why :-)
I have a Powershell script that perform replacements inside files (language metadata):
loads a list of replacement from a txt file into an array
gets all xml files from a Start folder
performs all the replacements from the array
performs a replacement on the filename based on the array first entry
saves the resulting files in a End folder
I've been using successfully variations of the exact same script for many years, with the only thing changing being the replacement file name and content... except today when creating another variant. The only change was the content of the substitution file, and suddenly the replacement did not happen anymore in the filename.
Here is the code:
#load the replacements from file
$data = Import-Csv -Path substitutions.txt -Header "Source", "Target", "Safe", "Count" -Delimiter "|"
#load the files to be processed
$xmlfiles = -join ($Startfolder, "*.xml")
$Fileset = Get-ChildItem $xmlfiles -recurse
foreach ($File in $Fileset) {
$NewFileName = ""
$WipFile = Get-Content $File
# set safe replacement flag to nothing
$flag = ""
#perform replacements
foreach ($item in $data) {
if ($WipFile -cmatch $item.Source) {
if ($item.Safe -eq 'yes') {
$WipFile = $WipFile -creplace $item.Source, $item.Target
$item.Count = $item.Count + 1
}
else {
$WipFile = $WipFile -creplace $item.Source, $item.Target
$item.Count = $item.Count + 1
$flag = "TOCHECK "
}
}
}
#replace language code in filename, based on first entry in the substitution list
$NewFileName = -join ($Endfolder, $flag, $file.name -creplace $data.Source[0], $data.Target[0])
Write-Host $NewFileName
#save file with updated content
$WipFile | Set-Content -Encoding Unicode ($File)
#move file to End folder
Move-Item $File $NewFileName
}
The substitution file is formatted as follows:
nl-NL|nl-BE|yes
After testing more, I discovered my new variant was failing if my substitution file had only one line. Add another one, and it works. How come?

Reducing amout of lines in variable within loop in Powershell

I have a txt file containing 10000 lines. Each line is an ID.
Within every loop iteration I want to select 100 lines, put them in a special format and do something. I want to do this until the document is finished.
The txt looks like this:
406232C1331283
4062321N022075
4062321H316457
Current approach:
$liste = get-content "C:\x\input.txt"
foreach ($item in $liste) {
azcopy copy $source $target --include-pattern "*$item*" --recursive=true
}
The system will go throug the TXT file and make a copy request for every name it finds in the TXT file. Now the system is able to handle like 300 search-patterns in one request. like
azcopy copy $source $target --include-pattern "*id1*;*id2*;*id3*"
How can I extract 300 items from the document at once, separate them with semicolon and embedd them in wildcard? I tried to pipe everyting in a variable and work with -skip.
But it seems not easy to handle :(
Use the -ReadCount parameter to Get-Content to send multiple lines down the pipeline:
Get-Content "C:\x\input.txt" -ReadCount 300 | ForEach-Object {
$wildCards = ($_ | ForEach-Object { "*$_*" } -join ';'
azcopy copy $source $target --include-pattern $wildCards --recursive=true
}
Do you want 100 or 300 at a time? ;-)
I'm not sure if I really got what the endgoal is but to slice a given amount of elements in chunks of a certain size you can use a for loop like this:
$liste = Get-Content -Path 'C:\x\input.txt'
for ($i = 0; $i -lt $Liste.Count; $i += 100) {
$Liste[$i..$($i + 99)]
}
Now if I got it right you want to join these 100 elements and surround them with certain cahrachters ... this might work:
'"*' + ($Liste[$i..$($i + 99)] -join '*;*') + '*"'
Together it would be this:
$liste = Get-Content -Path 'C:\x\input.txt'
for ($i = 0; $i -lt $Liste.Count; $i += 100) {
'"*' + ($Liste[$i..$($i + 99)] -join '*;*') + '*"'
}
There's many ways, here's one of them...
First I would split array to chunks of 100 elements each, using this helper function:
Function Split-Array ($list, $count) {
$aggregateList = #()
$blocks = [Math]::Floor($list.Count / $count)
$leftOver = $list.Count % $count
for($i=0; $i -lt $blocks; $i++) {
$end = $count * ($i + 1) - 1
$aggregateList += #(,$list[$start..$end])
$start = $end + 1
}
if($leftOver -gt 0) {
$aggregateList += #(,$list[$start..($end+$leftOver)])
}
$aggregateList
}
For example to split your list into chunks of 100 do this:
$Splitted = Split-Array $liste -count 100
Then use foreach to iterate each chunk and join its elements for the pattern you need:
foreach ($chunk in $Splitted)
{
$Pattern = '"' + (($chunk | % {"*$_*"}) -join ";") + '"'
azcopy copy $source $target --include-pattern $Pattern --recursive=true
}

Bulk rename files in powershell and add a count to each filename

I am able to bulk rename files in a directory OR substitute each file name with a count, however I need both combined.
Bulk rename (keep first 2 charachters of original name):
Get-ChildItem * |
Rename-Item -NewName { $_.BaseName.Split('-')[0] + $_.Extension }
(partially hard coded solution, I know!)
Alternatively, add count:
$count = 1
Get-ChildItem * | % { Rename-Item $_ -NewName (‘{0}.xlsx’ -f $count++) }
(I am not even dreaming about trailing 0s)
I tried to combine both things, but to no avail. What am I doing wrong?
My failed attempt:
$count = 1
Get-ChildItem * |
Rename-Item -NewName { $_.BaseName.Split('-')[0] -f $count++ + $_.Extension }
You're misunderstanding how the format operator works. You need a format string with a placeholder ({0}) in order to make that operator work. I would also recommend putting grouping parentheses around that expression, even though that shouldn't be required in this case. Just to be on the safe side.
('foo {0} bar' -f $some_var) + $other_var
With that said, you apparently want to append the value of the counter variable to the fragment from the original file name rather than using that fragment as a format string. For that you can simply concatenate the counter to the fragment, just like you do with the extension.
For the counter to work correctly you also need to specify the correct scope. The way you're using it defines a new variable $counter in the local scope of the scriptblock every time a file is renamed, so the variable $counter in the parent scope is never incremented. Use the script: or global: scope modifier to get the variable you actually intend to use.
$count = 1
Get-ChildItem * |
Rename-Item -NewName { $_.BaseName.Split('-')[0] + $script:count++ + $_.Extension }
If you do want to use the format operator instead of string concatenation (+) you'd use it like this:
$count = 1
Get-ChildItem * |
Rename-Item -NewName { '{0}{1}{2}' -f $_.BaseName.Split('-')[0], $script:count++, $_.Extension }
To have the $count counted up, you have to go with the loop.
$count = 1
Get-ChildItem * | foreach-object { Rename-Item -NewName { $_.BaseName.Split('-')[0] -f
count++ + $_.Extension } }
% is the alias for the foreach-object cmdlet you used in the "add count" approach.
Just added it to you failed attempt.

File Output in Powershell without Extension

Here is what I have so far:
Get-ChildItem "C:\Folder" | Foreach-Object {$_.Name} > C:\Folder\File.txt
When you open the output from above, File.txt, you see this:
file1.txt
file2.mpg
file3.avi
file4.txt
How do I get the output so it drops the extension and only shows this:
file1
file2
file3
file4
Thanks in advance!
EDIT
Figured it out with the help of the fellows below me. I ended up using:
Get-ChildItem "C:\Folder" | Foreach-Object {$_.BaseName} > C:\Folder\File.txt
Get-ChildItem "C:\Folder" | Select BaseName > C:\Folder\File.txt
Pass the file name to the GetFileNameWithoutExtension method to remove the extension:
Get-ChildItem "C:\Folder" | `
ForEach-Object { [System.IO.Path]::GetFileNameWithoutExtension($_.Name) } `
> C:\Folder\File.txt
I wanted to comment on #MatthewMartin's answer, which splits the incoming file name on the . character and returns the first element of the resulting array. This will work for names with zero or one ., but produces incorrect results for anything else:
file.ext1.ext2 yields file
powershell.exe is good for me. Let me explain to thee..doc yields powershell
The reason is because it's returning everything before the first . when it should really be everything before the last .. To fix this, once we have the name split into segments separated by ., we take every segment except the last and join them back together separated by .. In the case where the name does not contain a . we return the entire name.
ForEach-Object {
$segments = $_.Name.Split('.')
if ($segments.Length -gt 1) {
$segmentsExceptLast = $segments | Select-Object -First ($segments.Length - 1)
return $segmentsExceptLast -join '.'
} else {
return $_.Name
}
}
A more efficient approach would be to walk backwards through the name character-by-character. If the current character is a ., return the name up to but not including the current character. If no . is found, return the entire name.
ForEach-Object {
$name = $_.Name;
for ($i = $name.Length - 1; $i -ge 0; $i--) {
if ($name[$i] -eq '.') {
return $name.Substring(0, $i)
}
}
return $name
}
The [String] class already provides a method to do this same thing, so the above can be reduced to...
ForEach-Object {
$i = $_.Name.LastIndexOf([Char] '.');
if ($i -lt 0) {
return $_.Name
} else {
return $_.Name.Substring(0, $i)
}
}
All three of these approaches will work for names with zero, one, or multiple . characters, but, of course, they're a lot more verbose than the other answers, too. In fact, LastIndexOf() is what GetFileNameWithoutExtension() uses internally, while what BaseName uses is functionally the same as calling $_.Name.Substring() except it takes advantage of the already-computed extension.
And now for a FileInfo version, since everyone else beat me to a Path.GetFileNameWithoutExtension solution.
Get-ChildItem "C:\" | `
where { ! $_.PSIsContainer } | `
Foreach-Object {([System.IO.FileInfo]($_.Name)).Name.Split('.')[0]}
(ls).BaseName > C:\Folder\File.txt
Use the BaseName property instead of the Name property:
Get-ChildItem "C:\Folder" | Select-Object BaseName > C:\Folder\File.txt