Home > OS >  PowerShell insert a string into a filename, just before the extension
PowerShell insert a string into a filename, just before the extension

Time:04-15

A general thing that I need to do a lot with files is to create/reference a backup version of a file. e.g. I might want to have a date/time stamp version of a file so that I can reference both the original report and the backup version throughout a script:

$now = $(Get-Date -format "yyyy-MM-dd__HH-mm")
$ReportName = "MyReport-en.csv"
$Backups = "D:\Backups\Reports"

I found that using -join or always inserted a space before the date/time stamp:

$ReportBackup = "$Backups\$($ReportName -split ".csv")"   "_$now.csv"
$ReportBackup = "$Backups\$($ReportName -split ".csv")" -join "_$now.csv"

I found a way to do this, but it looks feels inefficient with the triple $ and duplication of the .csv

$ReportBackup = "$Backups\$($($ReportName -split ".csv")[0])$_now.csv"

which results in:

$ReportBackup => D:\Backups\Reports\MyReport-en_2022-04-15__07-55.csv

Can you think of simpler/cleaner way to achieve the generic goal of inserting a piece of text before the extension, without the triple $ or duplication of the extension? ("Use a $name = "MyReport-en"" is not so useful because often I am reading a file object and get the name complete with extension.

CodePudding user response:

$now = Get-Date -Format "yyyy-MM-dd__HH-mm"
$reportName = "MyReport-en.csv"
$backups = "D:\Backups\Reports"

$reportBackup = Join-Path $backups $reportName.Replace(".csv","_$now.csv")
$reportBackup

CodePudding user response:

If you have obtained the report file as FileInfo object by perhaps using Get-Item or Get-ChildItem, you'll find that object has convenient properties you can use to create a new filename with the date included:

# assume $ReportName is a FileInfo object
$Backups = "D:\Backups\Reports"
# I'm creating a new filename using the '-f' Format operator
$NewName = '{0}_{1:yyyy-MM-dd__HH-mm}{2}' -f $ReportName.BaseName, (Get-Date), $ReportName.Extension
$ReportBackup = Join-Path -Path $Backups -ChildPath $NewName

If however $ReportName is just a string that only holds the filename, you can do:

$ReportName = "MyReport-en.csv"
$Backups    = "D:\Backups\Reports"
$baseName   = [System.IO.Path]::GetFileNameWithoutExtension($ReportName)
$extension  = [System.IO.Path]::GetExtension($ReportName)
$NewName    = '{0}_{1:yyyy-MM-dd__HH-mm}{2}' -f $baseName, (Get-Date), $extension
$ReportBackup = Join-Path -Path $Backups -ChildPath $NewName

P.S. It is always risky to simply use .Replace() on a filename because that doesn't allow you to anchor the substring to replace, which is needed, because that substring may very well also be part of the name itself.
Also, the string .Replace() method works case-sensitive.

This means that

'My.csvReport-en.csv'.Replace(".csv", "_$(Get-Date -Format 'yyyy-MM-dd__HH-mm').csv")

would fail (returns My_2022-04-15__13-36.csvReport-en_2022-04-15__13-36.csv)
and

'MyReport-en.CSV'.Replace(".csv", "$(Get-Date -Format 'yyyy-MM-dd__HH-mm').csv")

would simply not replace anything because it cannot find the uppercase .CSV ..

If you really want to do this by replacing the extension into a date extension, go for a more complex case-insensitive regex -replace like:

$ReportName -replace '^(. )(\.[^.] )$', "`$1_$(Get-Date -Format 'yyyy-MM-dd__HH-mm')`$2" 

Regex details:

^              Assert position at the beginning of the string
(              Match the regular expression below and capture its match into backreference number 1
   .           Match any single character that is not a line break character
               Between one and unlimited times, as many times as possible, giving back as needed (greedy)
)             
(              Match the regular expression below and capture its match into backreference number 2
   \.          Match the character “.” literally
   [^.]        Match any character that is NOT a “.”
               Between one and unlimited times, as many times as possible, giving back as needed (greedy)
)             
$              Assert position at the end of the string (or before the line break at the end of the string, if any)
  • Related