Bulk renaming files to add sequential prefixes via PowerShell - powershell

I have two folders all with multiple .msg files I need to add a numerical, sequential prefix to. One folder has over 1500 individual files.
I want the end result to go from this:
"[EXTERNAL] RE%3A Auth....msg"
"Fees....msg"
"Hello There...msg"
To this:
"1[EXTERNAL] RE%3A Auth....msg"
"2Fees....msg"
"3Hello There...msg"
I have tried to combine a few results from other similar threads I have found here and have come up with the following:
$i = 1
#Get-ChildItem *.msg | %{Rename-Item $_ -NewName ("{0:000#}_$($_.directory.name).msg" -f $i++)}
When I run this script, I get the following errors:
At C:\users\me\Documents\PrependNumbers.ps1:10 char:5
+ #Get-ChildItem *.msg | %{Rename-Item $_ -NewName ("{0:000#}_$($_.dire ...
+ ~~~~~~~~~~
Unexpected token '-ChildItem' in expression or statement.
At C:\users\me\Documents\PrependNumbers.ps1:10 char:16
+ #Get-ChildItem *.msg | %{Rename-Item $_ -NewName ("{0:000#}_$($_.dire ...
+ ~~~~~
Unexpected token '*.msg' in expression or statement.
At C:\users\me\Documents\PrependNumbers.ps1:10 char:1
+ #Get-ChildItem *.msg | %{Rename-Item $_ -NewName ("{0:000#}_$($_.dire ...
+ ~~~~
The splatting operator '#' cannot be used to reference variables in an expression. '#Get' can be used only as an
argument to a command. To reference variables in an expression use '$Get'.
+ CategoryInfo : ParserError: (:) [], ParseException
+ FullyQualifiedErrorId : UnexpectedToken
I feel like I probably just have a syntax error somewhere, but am unable to tell where exactly from these errors.

Related

Remove certain characters from all folder names in a folder

I have a folder with a load of subfolders with a random number at the beginning of their folder name. they are in this format:
1254170-folder1
1212340-folder2
3245417-folder3
What can I to rename all of them to
folder1
folder2
folder3
I tried something like this because I saw something similar about filenames.
for f in *\1*;do( mv "$f" "${f//1/ }");done
but it does not work. the powershell returned
At line:1 char:4
+ for f in *\1*;do( mv "$f" "${f//1/ }");done
+ ~
Missing opening '(' after keyword 'for'.
At line:1 char:17
+ for f in *\1*;do( mv "$f" "${f//1/ }");done
+ ~
Missing statement body in do loop.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MissingOpenParenthesisAfterKeyword
Not sure what should I do. I am using Windows 10 2004.
Thanks for the help.
This should work:
Get-ChildItem -path . -directory -recurse | Where {$_.Name -match '^\d+-'} | Rename-Item -NewName {$_.Name -replace '^\d+-',''}
First command enumerates folders and all subfolders in current directory (.).
Second command filters folders which starts with digits followed by dash (^\d+- regex)
Third command renames the folders by removing ^\d+- prefix
I complet the #Renat code :
if you want exclude the directories with format without characters after the '-' (example 1254170- )
And if you want your script continue same if a directory with the new name already exist
Get-ChildItem "c:\temp\" -dir | where name -match "^\d+-.+$" | Rename-Item -NewName {$_.Name.Split('-')[1]} -ErrorAction SilentlyContinue

how to append output to a CSV file

foreach ( $newfile in $file )
{
$b = Get-CMDeploymentStatus -PackageId $newfile -StatusType Any | select PackageID
Write-Output $b | Export-Csv -Path "C:\Users\PSM-6A1A000000000000\Documents\list.csv"
}
I am giving input to this with an input file which has number of package names listed and then I want to process it in such a way that the output comes one after the other right now I am getting an error as
Export-Csv : Cannot bind argument to parameter 'InputObject' because it is null. At line:16 char:20 + Write-Output $b | Export-Csv -Path "C:\Users\PSM-6A1A000000000000\Documents\lis ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidData: (:) [Export-Csv], ParameterBindingValidationException + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.ExportCsvCommand
Your code is assuming that you will have a result coming back from $b, if it does not though, you'll get an error because you're piping $b, which is null, into Export-CSV.
$null |export-csv c:\temp\1.csv
Export-Csv : Cannot bind argument to parameter 'InputObject' because it is null.
At line:1 char:8
+ $null |export-csv c:\temp\1.csv
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Export-Csv], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.ExportCs
You should add a 'Guard Clause' before you try to export.
if ($null -ne $b){
Export-csv -Append -InputObject $b
}
At least this will continue executing. Now your next problem is to determine why $b would be empty...from my experience with CM, I bet you need to specify which property in your $file you need. Maybe that line should read:
$b = Get-CMDeploymentStatus -PackageId $newfile.PackageId -StatusType Any | select PackageID
Since you say "I am giving input to this with an input file which has number of package names listed", but your code uses PackageId..
It looks to me that this file contains a packageId, each on a single line.
Anyway, I don't see the code ever reading this file..
If my assumption about the text file is correct, try:
# read the content of the text file and loop through the lines
# collect the output from Get-CMDeploymentStatus in variable $result
$result = Get-Content -Path 'X:\TheFileWithPackageIds.txt' | ForEach-Object {
# inside the ForEach-Object, the $_ automatic variable represents a single line from the text file
Get-CMDeploymentStatus -PackageId $_ -StatusType Any | select PackageID
}
# output on screen
$result
# write to new CSV file
$result | Export-Csv -Path "C:\Users\PSM-6A1A000000000000\Documents\list.csv" -NoTypeInformation

Issue with foreach + get-childitem

I'm doing a script to organize my media. I download files in one directory to accomodate them before adding to my media center.
If for example I have a file called Breaking.Bad.S01E01.DVDRip.XviD-ORPHEUS.avi I would like the script to get the name of the show, check for season on S01 and move that file to a folder in another disk, for example e:\series\breaking bad\season 01
So far it checks if the file is call s01e01 or S01E01 or s01.e01 or S01.E01 and returns Breaking Bad\Season 01, creates the path to move to and the moving action itself
I have part of that script but I cannot make get-childitem to work with foreach.
This is what I have so far and the error I'm getting:
CODE
$FilesList = Get-ChildItem -name -recurse -include *.mkv,*.mp4,*.srt,*.avi
$FilesList
foreach ($FL_Item in $FilesList)
{
$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
$SeriesDirectory = Join-Path -Path "$SeriesName" -ChildPath "$Season"
$SeriesDirectory
#$MoverArchivo = move-item -path $FileName -destination e:\series\$SeriesDirectory
#$MoverArchivo
''
}
OUTPUT I'm getting
Breaking.Bad.S01E01.DVDRip.XviD-ORPHEUS.avi
Breaking.Bad.S01E01.DVDRip.XviD-ORPHEUS.spa.srt
Breaking.Bad.S04E01.Box.Cutter.720p.hdtv.x264-orenji.mkv
Breaking.Bad.S04E01.Box.Cutter.720p.hdtv.x264-orenji.spa.srt
Breaking.Bad.S05E15.720p.HDTV.x264-EVOLVE.mkv
Breaking.Bad.S05E15.720p.HDTV.x264-EVOLVE.spa.srt
Path Of Blood (2018) [WEBRip] [1080p] [YTS.AM]\Path.Of.Blood.2018.1080p.WEBRip.x264-[YTS.AM].mp4
They Shall Not Grow Old (2018) [BluRay] [1080p] [YTS.AM]\They.Shall.Not.Grow.Old.2018.1080p.BluRay.x264-[YTS.AM].mp4
ERROR
You cannot call a method on a null-valued expression.
At D:\shared\temp\test3.ps1:8 char:5
+ $SE_Info = $FL_Item.BaseName.Split('.')[-3] -split 'e'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Cannot index into a null array.
At D:\shared\temp\test3.ps1:10 char:5
+ $Season = $SE_Info[0] -replace 's', 'Season '
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
Join-Path : Cannot bind argument to parameter 'Path' because it is an empty string.
At D:\shared\temp\test3.ps1:17 char:37
+ $SeriesDirectory = Join-Path -Path "$SeriesName" -ChildPath "$Sea ...
+ ~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Join-Path], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,Microsoft.PowerShell.Commands.Join
PathCommand
Any ideas what could be wrong?
You are using the -name parameter of Get-ChildItem in your first line:
$FilesList = Get-ChildItem -name -recurse -include *.mkv,*.mp4,*.srt,*.avi
which means it will return just the names of the files as strings.
Later, in your loop, you access each element using the BaseName property, which is a property of FileInfo objects, not strings. So, $FL_Item.BaseName returns an empty string and you get the errors as shown.
Just remove the -name and it should work (or at least you won't get those errors).
I'd use a RegEx with (named) capture groups to grep the Series,Season and Episode number.
See the RegEx working live on regex101.com
## Q:\Test\2018\12\20\SO_53875674.ps1
$DstBase = "E:\series"
Get-ChildItem -Include *.mkv,*.mp4,*.srt,*.avi -Recurse -File|
Where-Object BaseName -match "^(?<Series>.*?)\.?S(?<Season>\d{1,2})\.?E(?<Episode>\d{2})"|
ForEach-Object {
$Destination = "{0}\{1}\Season {2:00}\" -f $DstBase,$Matches.Series.replace('.',' ').Trim(),[int]$Matches.Season
if (!(Test-Path $Destination)){MD $Destination -Force | Out-Null}
"Moving file [{0}] to [{1}]" -f $_.FullName,$Destination
$_ | Move-Item -Destination $Destination -Force
}
Sample tree (with above data) after running the script:
> tree /F
└───series
└───Breaking Bad
├───Season 01
│ Breaking.Bad.S01E01.DVDRip.XviD-ORPHEUS.avi
│ Breaking.Bad.S01E01.DVDRip.XviD-ORPHEUS.spa.srt
│
├───Season 04
│ Breaking.Bad.S04E01.Box.Cutter.720p.hdtv.x264-orenji.mkv
│ Breaking.Bad.S04E01.Box.Cutter.720p.hdtv.x264-orenji.spa.srt
│
└───Season 05
Breaking.Bad.S05E15.720p.HDTV.x264-EVOLVE.mkv
Breaking.Bad.S05E15.720p.HDTV.x264-EVOLVE.spa.srt

Search for a string in a number of files, and then rename the files with that string

I'm trying to create a PowerShell script that will search through a series of .txt files (.hl7 files to be exact, but they are just txt files) and search within those files to see if they contain a four digit number. If the file does contain that four digit number, it should then rename the file with the string added to the front of the original file name. So test.hl7 should become 8000_test.hl7 if that file includes those 4 digits within it.
After a day of ferocious googling and digging through this website, this is the best I could muster:
$AccountIDs = ("8155", "8156", "8428")
$Path = "C:\Users\ThatsMe\Downloads\messages"
$Files = (Get-ChildItem $Path -Filter "*.hl7")
for ($i = 0; $i -le $Files.Length; $i++) {
if (Get-Content $Files[$i].FullName | Select-String -Pattern $AccountIDs[$i]) {
Rename-Item $Files[$i].FullName -NewName (($AccountIDs[$i]) + "_" + $Files[$i].Name)
}
}
I am getting some interesting results. I currently have four test files in that messages folder, test, test2, test3, and skibbidybop. The very first one, test gets correctly changed to 8156_test. However, the other files aren't touched. Now, when I change the filename of test to ttest, the script completely skips over that file and then renames test2 and test3 to 8156_test2 (which is incorrect) and 8428_test3 respectively. skibbidybop is never touched.
And, of course, the error message from PowerShell:
Select-String : Cannot bind argument to parameter 'Pattern' because it is null.
At line:6 char:61
+ if (Get-Content $Files[$i].FullName | Select-String -Pattern <<<< $AccountIDs[$i]) {
+ CategoryInfo : InvalidData: (:) [Select-String], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.SelectStringCommand
Get-Content : Cannot bind argument to parameter 'Path' because it is null.
At line:6 char:16
+ if (Get-Content <<<< $Files[$i].FullName | Select-String -Pattern $AccountIDs[$i]) {
+ CategoryInfo : InvalidData: (:) [Get-Content], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.GetContentCommand
Updated Code
$Path = "C:\Users\ThatsMe\Downloads\messages"
$pattern = '\b(8155|8156|8428)\b'
Get-ChildItem $Path -Filter '*.hl7' |
Select-String -Pattern $pattern |
Group-Object Path |
ForEach-Object {
$id = $_.Group.Matches[0].Groups[0].Value
$filename = $_.Group.Filename | Select-Object -First 1
Rename-Item -Path $_.Name -NewName "${id}_${filename}" -WhatIf
}
This is the error that I receive now:
C:\> C:\Users\ThatsMe\Downloads\messages\changename.ps1
Cannot index into a null array.
At C:\Users\ThatsMe\Downloads\messages\changename.ps1:8 char:38
+ $id = $_.Group.Matches[ <<<< 0].Groups[0].Value
+ CategoryInfo : InvalidOperation: (0:Int32) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
What if: Performing operation "Rename File" on Target "Item:
C:\Users\ThatsMe\Downloads\messages\test.hl7 Destination:
C:\Users\ThatsMe\Downloads\messages\_".
Cannot index into a null array.
At C:\Users\ThatsMe\Downloads\messages\changename.ps1:8 char:38
+ $id = $_.Group.Matches[ <<<< 0].Groups[0].Value
+ CategoryInfo : InvalidOperation: (0:Int32) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
What if: Performing operation "Rename File" on Target "Item:
C:\Users\ThatsMe\Downloads\messages\test3.hl7 Destination:
C:\Users\ThatsMe\Downloads\messages\_".
The errors you get are caused by two mistakes, one of them a classic off-by-one error. PowerShell arrays are zero-based, meaning that the last index of the array is one less than the number of its elements:
[ 'a', 'b', 'c' ] → count == 3
0 1 2 → last index == 2 == 3-1
Thus your for loop may run while $i is less than $Files.Length (-lt), not less or equal (-le):
for ($i = 0; $i -lt $Files.Length; $i++) {
Also, you cannot use the same index variable for two different arrays ($Files and $AccountIDs) unless you made sure both arrays have the same length or at least that the second array ($AccountIDs) has more elements than the one used to determine the maximum index ($Files). If $AccountIDs has less elements than $Files your code will eventually attempt to access an index beyond the upper boundary of $AccountIDs. Besides, you probably want to check each file for all of the numbers from $AccountIDs anyway. Doing that requires a nested loop with a second index variable.
With that said, you're making this more complicated than it needs to be. You can simply put your IDs in a single regular expression and pipe the list of files into Select-String to check them against that regular expression:
$pattern = '\b(8155|8156|8428)\b'
Get-ChildItem $Path -Filter '*.hl7' |
Select-String -Pattern $pattern |
Group-Object Path |
ForEach-Object {
$id = $_.Group.Matches[0].Groups[0].Value
$filename = $_.Group.Filename | Select-Object -First 1
Rename-Item -Path $_.Name -NewName "${id}_${filename}" -WhatIf
}
The regular expression \b(8155|8156|8428)\b matches any of the given numbers. The \b restrict the match to word boundaries to avoid matching numbers like 81552 or 842893 as well.
The Group-Object statement ensures the uniqueness of the renamed files (so that you don't attempt to rename a file more than once if more than one match is found in it).
.Matches[0].Groups[0].Value extracts the value of the first capturing group of the first match for each file.
The Select-Object -First 1 ensures that even if multiple matches were found in a file you have just one string with the filename, not an array of them.
Remove the -WhatIf switch once you verified that the rename operation would work correctly and re-run the whole statement to actually rename the files.
Edit: For PowerShell v2 you need to adjust the group handling a little bit, because that version doesn't support member enumeration.
Get-ChildItem $Path -Filter '*.hl7' |
Select-String -Pattern $pattern |
Group-Object Path |
ForEach-Object {
$id = $_.Group | Select-Object -Expand Matches -First 1 |
ForEach-Object { $_.Groups[0].Value }
$filename = $_.Group | Select-Object -Expand Filename -First 1
Rename-Item -Path $_.Name -NewName "${id}_${filename}" -WhatIf
}

Trying to use built-in PS1 methods Get-Date, Convert-Path, rename-item getting errors

I am new to PowerShell and trying to write my first script to copy files from one directory to another and then rename the files in the $DESTINATION directory once they are copied over there.
I am executing the script below from C:\Scripts directory on my machine.
The first foreach loop works great and my files land in the $DESTINATION.
But when I to through the 2nd loop I get the errors below
Any help/direction would be appreciated. Thanks.
Here is my code:
# Define variables.
$Source = "C:\ETLFILES\WinSCP\FilesETL\*.*"
$Destination = "C:\ETLFILES\WinSCP\SFE_Archive\"
$DestinationFiles = "C:\ETLFILES\WinSCP\SFE_Archive\*.*"
# Create the $sourceFileList variable to loop through
$sourceFileList = Get-ChildItem -path $Source
# Loop through the $soureFileList and copy the items to the $Destination.
foreach ($item in $sourceFileList) {
Copy-Item -Path $Source -Destination $Destination
}
# Create the $destinationFileList variable to loop through
$destinationFileList = Get-ChildItem -path $DestinationFiles
# Loop through the $destinationFileList and rename the files with appended DateTime stamp.
foreach ($itemDest in $destinationFileList) {
$Date = (Get-Date).ToString("yyyyMMdd_HHmmss")
$newFileName = $Date + "_" + $itemDest
Rename-Item $itemDest $newFileName
}
Here are the errors I'm getting and I think that I need to change the -path as it is looking to where the script is executing from and not looking at the $DestinationFiles directory like it looked to the $Source above:
PS C:\Scripts> C:\Scripts\ArchiveSFE_files.ps1
Rename-Item : Cannot rename the specified target, because it represents a path or device name.
At C:\Scripts\ArchiveSFE_files.ps1:21 char:5
+ Rename-Item $itemDest $newFileName
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Rename-Item], PSArgumentException
+ FullyQualifiedErrorId : Argument,Microsoft.PowerShell.Commands.RenameItemCommand
Rename-Item : Cannot rename the specified target, because it represents a path or device name.
At C:\Scripts\ArchiveSFE_files.ps1:21 char:5
+ Rename-Item $itemDest $newFileName
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Rename-Item], PSArgumentException
+ FullyQualifiedErrorId : Argument,Microsoft.PowerShell.Commands.RenameItemCommand
Rename-Item : Cannot rename the specified target, because it represents a path or device name.
At C:\Scripts\ArchiveSFE_files.ps1:21 char:5
+ Rename-Item $itemDest $newFileName
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Rename-Item], PSArgumentException
+ FullyQualifiedErrorId : Argument,Microsoft.PowerShell.Commands.RenameItemCommand
Rename-Item : Cannot rename the specified target, because it represents a path or device name.
At C:\Scripts\ArchiveSFE_files.ps1:21 char:5
+ Rename-Item $itemDest $newFileName
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Rename-Item], PSArgumentException
+ FullyQualifiedErrorId : Argument,Microsoft.PowerShell.Commands.RenameItemCommand
PS C:\Scripts>
PowerShell can be a bit tricky if you're used to text-based languages and tools, because everything in PowerShell is a .NET object.
This line:
$newFileName = Get-Date + "_" + $item # .ToString("yyyyMMdd-HHmmss") + "_" + $item
will most certainly result in an error. Get-Date returns a [datetime] object.
Although [datetime] does support the + operator, it expects an argument of the type [timespan] - and the string "_" can't be converted to a timespan.
What you want is a string representing the current date. Either use the ToString() statement that you've commented out, or use the -Format parameter to produce a formatted string instead of a [datetime] object:
$newFileName = (Get-Date).ToString("yyyyMMdd-HHmmss") + "_" + $item
or
$newFileName = (Get-Date -Format "yyyyMMdd-HHmmss") + "_" + $item
I'm not a betting man, but I'll bet the error you're having is this one.
Get-Date : Cannot bind parameter 'Date'. Cannot convert value "+" to type "System.DateTime".
Error: "String was not recognized as a valid DateTime."
This is because the code you've got here is trying to add a character to a PowerShell DateTime object, and that simply does not fly. You can pick a single property though and concatenate characters onto that, however.
But, I noticed that you commented out some lines in your code, and am guessing that you'd like to get the date in this format: "yyyyMMdd-HHmmss".
If that's so, this little snippet will do that for you:
# Define logic to rename and copy .
foreach ($item in $sourceFileList) {
$newFileName = $(Get-Date -UFormat %Y%m%d-%H%m%S) + "_" + $item
"the item $($item.BaseName) would become $newFileName"
}
This will output to the screen the new name for the file, like this:
the item q1 would become 20160921-110907_q1.png
the item q2 would become 20160921-110907_q2.png
the item q3 would become 20160921-110907_q3.png
You can comment out that line once you're happy with the new name. And the best part? Just drop this into your code inplace of your current $newFileName line and it will work with your previous code.
If I'm wrong, let me know the error you're getting and I'll help you get this sorted.
Looks like you are trying to write a file with : in the time part of the Get-Date when you try to rename to $newFileName. This will kick an error out.
$Date = Get-Date.ToString("yyyyMMdd_HHmmss")
$newFileName = $Date + "_" + $item
Include the above and that should prevent that problem.
thanks for everyone's help and input. I really appreciate it. I was finally able to resolve my problem whereby in the variable assigned in the foreach loop the FULL PATH was being defined that is what was generating the errors above. Once I used the .NAME property the issue was resolved. Thanks again to everyone.