Move files based on name (with an extra complication) - powershell

I´ve seen a couple of topics showing how to move files based on their names. I have an extra issue with this matter.
I have a bunch of video files, based on tv series with the
name of the series + season + episode number
For example: Breaking.Bad.s01e03
And my files are organized like:
d:\series\breaking bad\season01
d:\series\breaking bad\season02
d:\series\breaking bad\season03
...
etc
What i need is a script that checks for series name+season and moves them to it's corresponding folder.
Is it possible?
thanks in advance

i got bored and decided to answer your question even tho you have ignored the "how to ask a good question" info ... [frown]
the OP needs this to be case-insensitive - and the .Replace() method is not. changed to use the -replace operator instead.
$FileName = 'Breaking.Bad.S01e03'
$Series = $FileName.Substring(0, $FileName.LastIndexOf('.')).Replace('.', '_')
# disabled the initial version since the OP now needs case-insensitive replacement
#$Season = $FileName.Split('.')[2].Split('e')[0].Replace('s', 'Season')
$Season = $FileName.Split('.')[2].Split('e')[0] -replace 's', 'Season'
$Series
$Season
output ...
Breaking_Bad
Season01
i will leave to you the process of building a path from the above AND how to move files. [grin] here's a pair of hints ...
Get-Help Join-Path
Get-Help Move-Item
the OP has changed the entire format of the files, so this is a version that works with that format. no other formats were given, so no other formats were coded for.
if there are other formats needed, and the OP is unable to code for them, please ask a new question.
# fake reading in filenames
# in real life, use Get-ChildItem
$FileList = #(
[System.IO.FileInfo]'Breaking.Bad.S01E01.DVDRip.XviD-ORPHEUS.avi'
[System.IO.FileInfo]'Breaking.Bad.s02E01.DVDRip.XviD-ORPHEUS.avi'
[System.IO.FileInfo]'Breaking.Bad.S03e01.DVDRip.XviD-ORPHEUS.avi'
[System.IO.FileInfo]'Breaking.Bad.s04e01.DVDRip.XviD-ORPHEUS.avi'
)
foreach ($FL_Item in $FileList)
{
$SeriesName = ($FL_Item.BaseName -split '\.s\d')[0].Replace('.', '_')
$SE_Info = $FL_Item.BaseName.Split('.')[-3] -split 'e'
$Season = $SE_Info[0] -replace 's', 'Season'
$Episode = 'Episode{0}' -f $SE_Info[1]
$SeriesName
$Season
$Episode
''
}
output ...
Breaking_Bad
Season01
Episode01
Breaking_Bad
Season02
Episode01
Breaking_Bad
Season03
Episode01
Breaking_Bad
Season04
Episode01
again, i will refer you to Join-Path, New-Item, and Move-Item for creating the destination paths and moving the files.

Related

Remove list of phrases if they are present in a text file using Powershell

I'm trying to use a list of phrases (over 100) which I want to be removed from a text file (products.txt) which has lines of text inside it (they are tab separated / new line each). So that the results which do not match the list of phrases will be re-written in the current file.
#cd .\Desktop\
$productlist = #(
'example',
'juicebox',
'telephone',
'keyboard',
'manymore')
foreach ($product in $productlist) {
get-childitem products.txt | Select-String -Pattern $product -NotMatch | foreach {$_.line} | Out-File -FilePath .\products.txt
}
The above code does not remove the words listed in the $productlist, it simply outputs all links in products.txt again.
The lines inside of products.txt file are these:
productcatalog
product1example
juicebox038
telephoneiphone
telephoneandroid
randomitem
logitech
coffeetable
razer
Thank you for your help.
Here's my solution. You need the parentheses otherwise the input file will be in use when trying to write to the file. Select-string accepts an array of patterns. I wish I could pipe 'path' to set-content but it doesn't work.
$productlist = 'example', 'juicebox', 'telephone', 'keyboard', 'manymore'
(Select-String $productlist products.txt -NotMatch) | % line |
set-content products.txt
here's one way to do what you want. it's somewhat more direct than what yo used. [grin] it uses the way that PoSh can act on an entire collection when it is on the LEFT side of an operator.
what it does ...
fakes reading in a text file
when ready to do this in real life, replace the whole #region/#endregion block with a call to Get-Content.
builds the exclude list
converts that into a regex OR pattern
filters out the items that match the unwanted list
shows that resulting list
the code ...
#region >>> fake reading in a text file
# when ready to do this for real, replace the whole "#region/#endregion" block with a call to Get-Content
$ProductList = #'
productcatalog
product1example
juicebox038
telephoneiphone
telephoneandroid
randomitem
logitech
coffeetable
razer
'# -split [System.Environment]::NewLine
#endregion >>> fake reading in a text file
$ExcludedProductList = #(
'example'
'juicebox'
'telephone'
'keyboard'
'manymore'
)
$EPL_Regex = $ExcludedProductList -join '|'
$RemainingProductList = $ProductList -notmatch $EPL_Regex
$RemainingProductList
output ...
productcatalog
randomitem
logitech
coffeetable
razer

Powershell Script needed to redact items in between separators in a row according to defined list

Hello and thanks for reading. It's my first post and really need some help. The hardest part is getting my question across in a way that people will understand. I will try my best.
I have some huge csv files (some in excess of 8 millions rows so Excel not an option really) where I need to modify the contents of the 3rd 'field' in each row according to sets of words defined in a reference file
So an example csv might be something like:
AB12|TEST|CAT DOG MOUSE|TEST1|TEST2|TEST3||TEST4
CD34|TEST|HORSE CART TRAIN|TEST1|TEST2|TEST3||TEST4
etc etc.
In my reference file I have a list eg:
CAT
HORSE CART
These are contained in a CSV
What I need is to modify the files so that the 3rd 'field' (everything after the 2nd'|' and before the 3rd '|' is compared to the reference list and modified to match. ie in the first line, everything after CAT would be deleted and in the second line, everything after HORSE CART would be deleted within this 3rd field. So the resultant file outputted would look like:
AB12|TEST|CAT|TEST1|TEST2|TEST3||TEST4
CD34|TEST|HORSE CART|TEST1|TEST2|TEST3||TEST4
I normally use F.A.R.T to modify large files, but this needs to be a bit more clever than FART is able to offer.
I really hope this makes sense to someone out there and appreciate any help you might offer.
So far I have been experimenting with this, but it's a long way off doing what I want:
cls
$content = ""
write-output "** Original String **"
write-output ""
$content = Get-Content "~\Desktop\Test\*.dat"
$content
$separator1 = " "
$separator2 = "|"
$parts = $content.split($separator1)
write-output ""
write-output "** Revised String **"
write-output ""
$part1 = echo $parts[0]
$part3 = $part2.split($separator2)
$part4 = $part3[1]
$revised = $part1, $part4 -join "|"
$revised
write-output ""
So in summary then: This is really a modified 'Find and Replace Text' function that concentrates on a single field in each line, looks for matching sets of words, then deletes everything in that field other than the matched words, which are defined in a separate csv file.
Ok, as comparing arrays in PowerShell doesn't support wild cards we have to do this the old fashioned (costly) way. Compare every field with every reference.
I've not provided an example of reading the file as that can be done in different ways with regards to speed or memory consumption (your choice).
Also, I've provided the reference as an array instead as a file input to keep the example to the point (and easily testable).
The output should then of course be done to a new file instead of written to the host.
$file = #"
F1|F2|F3|F4|F5|F6|F7|F8
AB12|TEST|CAT DOG MOUSE|TEST1|TEST2|TEST3||TEST4
CD34|TEST|HORSE CART TRAIN|TEST1|TEST2|TEST3||TEST4
CD34|TEST|HORSE CART|TEST1|TEST2|TEST3||TEST4
"#
$ref = #("CAT*","HORSE CART*")
$file.split("`n") | foreach {# line in file
$outline = $nul
$_.split('|') | foreach {# field in the line
$field = $_
$refhit = $false
$ref | foreach {# item in the ref array
if ($field -like $_) {# replace field with ref
$refhit = $true
$outline += $_.TrimEnd('*') + '|'
}# end match
}# end ref
if (!$refhit){#pass on the field as is
$outline += "$field|"
}
}#end field
# Output filtered line
write-host $outline.TrimEnd('|')
}#end line

Properly Trimming and Escaping a String with a Backslash Separator in PowerShell

I've got an array of directories that present as such:
C:\Parent\Child\Child\_Grandchild
Some children are deeper than others. I need to trim off the
C:\Parent\
and the
\_Grandchild
into an array consisting of
Child, Child
but am constantly having PS strip off leading characters. It seems to be mainly 1's, A's, C's, and P's, but could be others (those are the ones at the top of the list so I notice them). Here is the code I am using, I am certain I am using split incorrectly but cannot figure out how to get it to work as needed.
$a # <-- An array item in a foreach loop
$b = $a.TrimStart('C:\Parent\');
$c = $b.TrimEnd('\_Grandchild');
$c_split = $c -split '\\';
This code seems to often produce results like the following
$c_split[0] = 'hild'; # (or 'ild' in some cases, depending on the starting characters)
$c_split[1] = 'Child';
$c_split[2] = 'Child';
and so on. I figured it was something with my initial TrimStart, but viewing $b during the process looks just fine, just as you would expect. I've tried leaving the trailing \ on the first trim but that didn't seem to solve the problem either.
Without some better test data it's hard to determine what you are really going for but I might have something. If you are just wanting to remove C:\Parent and \_GrandChild or at least the last child in the directory chain, the following will work:
# Assumed test data
$Directories = #(
"C:\Parent\Child\Child\_Grandchild",
"C:\Parent\Child\Hulk\_Grandchild",
"C:\Parent\Child\DareDevil\Child\Child\_Grandchild",
"C:\Parent\Child\DoctorWho\Child\_Grandchild",
"C:\Parent\Child\DareDevil\Child\FrenchBread\_Grandchild"
)
$Directories | ForEach-Object {
$Path = $_
$Path = Split-Path -Path $Path -NoQualifier #Remove the C:
$Path = Split-Path -Path $Path # Remove the last portion
# Here you have "\Parent\..." excluding the _Grandchild portion
# Split it and then assign the first value to null to disguard
$Null, $Path = $Path.Split("\")
# Path is your array of items you want
}
This could be a one liner. Maybe a tad messy but works with your sample data.
Using IO.Path you can remove the drive root, use a regex to replace everything up to the first backslash, use io.path again to remove last folder, trimend to remove the last \ and split.
$c_split = ($a.Replace([io.path]::GetPathRoot($a),"") -replace "^[^\\]*\\","").Replace([io.path]::GetFileName($a),"").TrimEnd("\").Split("\")
Something like this maybe:
$path = "C:\Parent\Child\Child\_Grandchild"
$split_path = $path.split("\")
$modified_path = $split_path[2..($split_path.length-2)]
Going on the assumption that you always want to remove "c:\whatever" from the start and the final directory.

Renaming one file (and nothing more than ONE file) using PowerShell

The problem
I constantly find myself in need of quick-method to rename a random file here and there while I work. I need to bring these filenames down to a structure compatible with web standards and some personal needs. A few examples below:
When I find I need
---------------------------------- -----------------------------------------
Welcome to the party.JPG welcome_to_the_party.jpg
Instructions (and some other tips) instructions_and_some_other_tips
Bar Drinks – The Best Recipes bar_drinks_the_best_recipes
La mañana del águila y el ratón la_manana_del_aguila_y_el_raton
Basically I need:
all uppercase characters to become lowercase
spaces to become underscore
some other special characters and diacritics for other languages to become their closest match (á is a, é is e, ç is c, and so on...)
Symbols like ( ) [ ] { } ' ; , to completely dissapear
Perhaps some replacements (optional) as: # = no; # = at or & = and
Not the question, but just FYI and you can see the big picture
I will be using a registry entry [HKEY_CLASSES_ROOT*\shell...] so I can call a batch file and/or a PowerShell Script by right-clicking the desired file, passing the argument information (the file in question) to the script that way.
My guesses
I have been looking closely at PowerShell Scripts, but I am not very knowledgeable about this area yet and all the solutions provided so far are addressing the entire folder (Dir/Get-ChildItem) instead of a specific file.
For example, I was successful using the line below (PowerShell) to replace all spaces by underscore, but then it affects other files in the directory as well.
Dir | Rename-Item –NewName { $_.name –replace “ “,”_“ }
Again, I do not need to address this problem for the entire folder, since I already have ways of doing so using software like Total Commander.
Thanks for any help you can give me.
Ruy
may be this code can help you
function Remove-Diacritics([string]$String)
{
$objD = $String.Normalize([Text.NormalizationForm]::FormD)
$sb = New-Object Text.StringBuilder
for ($i = 0; $i -lt $objD.Length; $i++) {
$c = [Globalization.CharUnicodeInfo]::GetUnicodeCategory($objD[$i])
if($c -ne [Globalization.UnicodeCategory]::NonSpacingMark) {
[void]$sb.Append($objD[$i])
}
}
return("$sb".Normalize([Text.NormalizationForm]::FormC))
}
function Clean-String([string]$String)
{
return(Remove-Diacritics ($String.ToLower() -replace "#", "no" -replace "\#", "at" -replace "&", "and" -replace "\(|\)|\[|\]|\{|\}|'|;|\,", "" -replace " ", "_"))
}
$youfile="C:\tmp4\121948_DRILLG.tif"
$younewnamefile=Clean-String $youfile
Rename-Item -Path $youfile $younewnamefile
Place this script somewhere (let's call it WebRename.ps1):
$old = $args -join ' '
$new = $old.ToLower().Replace(' ', '_')
# add all the remaining transformations you need here
Rename-Item $old $new
In the registry use this as the command (with your own path of course):
PowerShell -c C:\WebRename.ps1 "%1"
If your looking to be able to do this quickly and always want the same changes to be made you can add the following function to a .psm1 file and then place the file in one of your module folders (C:\Program Files\WindowsPowerShell\Modules is the most common one) you'll be able to just call WebRename-File filePath any time you need to quickly rename a file, the function is set up in such a way as to work fine if you pass in a single file path or you can pipe the results of a get-childitem to it if you ever do find the need to do bulk renames.
function WebRename-File {
param(
[parameter(Mandatory=$true,ValueFromPipeline=$true)]
$filePath
)
begin{}
Process{
foreach($path in $filePath){
$newPath = $path.ToLower()
$newPath = $newPath.Replace(' ','_')
###add other operations here###
Rename-Item -Path $path -NewName $newPath
}
}
end{}
}

Change specific part of a string

I've got a .txt-File with some text in it:
Property;Value
PKG_GUID;"939de9ec-c9ac-4e03-8bef-7b7ab99bff74"
PKG_NAME;"WinBasics"
PKG_RELATED_TICKET;""
PKG_CUSTOMER_DNS_SERVERS;"12314.1231
PKG_CUSTOMER_SEARCH_DOMAINS;"ms.com"
PKG_JOIN_EXISTING_DOMAIN;"True"
PKG_DOMAINJOIN_DOMAIN;"ms.com"
PKG_DOMAINJOIN_USER;"mdoe"
PKG_DOMAINJOIN_PASSWD;"*******"
So now, is there a way to replace those *'s with e.g. numbers or sth. ?
If so, may you tell me how to do it?
Much like Rahul I would use RegEx as well. Considering the application I'd run Get-Content through a ForEach loop, and replace text as needed on a line-by-line basis.
Get-Content C:\Path\To\File.txt | ForEach{$_ -replace "(PKG_DOMAINJOIN_PASSWD;`")([^`"]+?)(`")", "`${1}12345678`$3"}
That would output:
Property;Value
PKG_GUID;"939de9ec-c9ac-4e03-8bef-7b7ab99bff74"
PKG_NAME;"WinBasics"
PKG_RELATED_TICKET;""
PKG_CUSTOMER_DNS_SERVERS;"12314.1231
PKG_CUSTOMER_SEARCH_DOMAINS;"ms.com"
PKG_JOIN_EXISTING_DOMAIN;"True"
PKG_DOMAINJOIN_DOMAIN;"ms.com"
PKG_DOMAINJOIN_USER;"mdoe"
PKG_DOMAINJOIN_PASSWD;"12345678"
On second thought, I don't know if I'd do that. I might import it as a CSV, update the property, and export the CSV again.
Import-CSV C:\Path\To\File.txt -Delimiter ";" |%{if($_.Property -eq "PKG_DOMAINJOIN_PASSWD"){$_.value = "12345678";$_}else{$_}|export-csv c:\path\to\newfile.txt -delimiter ";" -notype
If You are using Powershell V2.0 (Hopefully) you can try something like below. gc is short hand for get-content commandlet.
(gc D:\SO_Test\test.txt) -replace '\*+','12345678'
With this the resultant data would be as below (notice the last line)
Property;Value
PKG_GUID;"939de9ec-c9ac-4e03-8bef-7b7ab99bff74"
<Rest of the lines here>
PKG_DOMAINJOIN_USER;"mdoe"
PKG_DOMAINJOIN_PASSWD;"12345678" <-- Notice here; *'s changed to numbers
Rahul's answer was good, I just wanted to mention that *+ will replace all instances of a single * character or more, so it would match any other place there is at least one star. If what you posted is all you would ever expect for you sample data though this would be fine.
You could alter the regex match to make it more specific if it was needed by changing it to something like
\*{3,0}
which would match 3 or more stars, or very specific would be
(?<=")\*{3,}(?=")
which would replace 3 or more stars which are surrounded by double quotes.
Here's a function that uses regex lookahead and lookbehind zero-length assertions to replace named parameters in a string similar to your example:
function replace-x( $string, $name, $value ) {
$regex = "(?<=$([regex]::Escape($name));`").*(?=`")"
$string -replace $regex, $value
}
Its reusable for different settings in your file, e.g:
$settings = get-content $filename
$settings = replace-x $settings PKG_DOMAINJOIN_USER foo
$settings = replace-x $settings PKG_DOMAINJOIN_PASSWD bar