MRNAP.psm1
|
#Requires -Version 5.1 <# .SYNOPSIS Generates a report file name and path with various customizable options. .DESCRIPTION Generates a timestamped report file name and full path. Accepts pipeline input by value (a bare string becomes ReportName) and by property name (pipe any object whose properties match parameter names). Supports custom directory, extension, UTC or local time, date-only, time-only, no-separator mode, and automatic archival of existing files to an 'old' subdirectory. Works on Windows, Linux, and macOS with PowerShell 5.1 and 7+. .PARAMETER ReportName The base name of the report file. Accepts pipeline input by value and by property name (RN). If omitted, the calling script's filename (without extension) is used; falls back to a random word when running interactively. .PARAMETER DirectoryName Destination directory for the report file. Accepts pipeline input by property name (DN). Defaults to ~/Reports ($HOME/Reports). If a relative path is supplied, a leading path separator is prepended to root it. .PARAMETER Extension File extension for the report. Accepts pipeline input by property name (EXT, E). Default: csv. A leading dot is added automatically if omitted. .PARAMETER NoDateTimeSeconds Omit the timestamp entirely — only the report name and extension are used. Accepts pipeline input by property name (NODTS, N). .PARAMETER UTC Use UTC instead of local time for the timestamp. Also forces the full datetime format (yyyy_MM_ddTHHmmss-). Accepts pipeline input by property name. .PARAMETER NoSeparators Remove underscores and dashes from the timestamp portion of the filename. Accepts pipeline input by property name (NoSep, NX). .PARAMETER NoSeconds Include time in the timestamp but omit seconds (HHmm instead of HHmmss). Accepts pipeline input by property name (NoSec, NS). .PARAMETER AddTime Include full date and time (yyyy_MM_ddTHHmmss-) in the timestamp. Accepts pipeline input by property name (AT). .PARAMETER NoDate Use only the time component (HHmmss-) — no date in the filename. Accepts pipeline input by property name (ND). .PARAMETER JustDate Use only the date (yyyy_MM_dd) with no report name. Accepts pipeline input by property name (JD). .PARAMETER Move Before returning the path, move any file in the destination directory that exactly matches <ReportName>.<Extension> to an 'old' subdirectory. Creates the destination and 'old' directories if they do not exist. Accepts pipeline input by property name (M). .PARAMETER FlatName Return just the file name with no directory path and no timestamp. By default the extension is included (e.g. "Tom.csv"). Combine with -NoExtension to get a bare name (e.g. "Tom"). Accepts pipeline input by property name (FL). .PARAMETER NoExtension Omit the file extension from the output. Most useful with -FlatName to produce a bare name like "Tom". Accepts pipeline input by property name (NE). .LINK https://github.com/dcazman/MRNAP .EXAMPLE MRNAP -ReportName "SalesReport" -DirectoryName "/tmp/Apple" -Extension "txt" -UTC -Move Generates a report file name and path with the specified options and moves existing files to an 'old' directory. Result: /tmp/Apple/2025_01_15T213022-SalesReport.txt .EXAMPLE MRNAP -ReportName "MonthlyReport" -UTC -NoSeparators Generates a file path with name "MonthlyReport" using UTC time and without separators. Result: ~/Reports/20250115T213022MonthlyReport.csv .EXAMPLE MRNAP -NoDateTimeSeconds Generates a filename and file path with no timestamp. Result: ~/Reports/<ScriptName>.csv or Result: ~/Reports/<RandomWord>.csv .EXAMPLE 'DailyReport' | MRNAP -DirectoryName '~/Reports' Pipes a string directly as the report name. Result: ~/Reports/2025_01_15-DailyReport.csv .EXAMPLE [PSCustomObject]@{ ReportName = "Sales"; DirectoryName = "/tmp/reports" } | MRNAP Pipes an object with named properties to generate a report path. Result: /tmp/reports/2025_01_15-Sales.csv .NOTES Author: Dan Casmas Version: 10.0 Date: 2/2026 Designed to work on Windows, Linux, and macOS. Tested with PowerShell 5.1 and 7+. #> function MRNAP { [Alias("MoldReportNameAndPath")] [CmdletBinding()] [OutputType([string])] param ( [parameter(Position = 0, Mandatory = $False, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True, HelpMessage = "The name of the report. If not specified, a random word or script name will be used.")] [Alias("RN")][string]$ReportName, [parameter(Position = 1, Mandatory = $False, ValueFromPipelineByPropertyName = $True, HelpMessage = "The destination directory where the report file will be stored. Default is the user's home directory.")] [Alias("DN")][string]$DirectoryName, [parameter(Position = 2, Mandatory = $False, ValueFromPipelineByPropertyName = $True, HelpMessage = "The file extension for the report file. The default value is 'csv'.")] [Alias("EXT", "E")][string]$Extension = "csv", [parameter(Mandatory = $False, ValueFromPipelineByPropertyName = $True, HelpMessage = "Exclude the timestamp in the file name.")] [Alias("NODTS", "N")][switch]$NoDateTimeSeconds, [parameter(Mandatory = $False, ValueFromPipelineByPropertyName = $True, HelpMessage = "Use Coordinated Universal Time (UTC) for the timestamp in the file name.")] [switch]$UTC, [parameter(Mandatory = $False, ValueFromPipelineByPropertyName = $True, HelpMessage = "Do not use separators (underscores and dashes).")] [Alias("NoSep", "NX")][switch]$NoSeparators, [parameter(Mandatory = $False, ValueFromPipelineByPropertyName = $True, HelpMessage = "Exclude seconds from the timestamp.")] [Alias("NoSec", "NS")][switch]$NoSeconds, [parameter(Mandatory = $False, ValueFromPipelineByPropertyName = $True, HelpMessage = "Include time in the timestamp.")] [Alias("AT")][switch]$AddTime, [parameter(Mandatory = $False, ValueFromPipelineByPropertyName = $True, HelpMessage = "Exclude the date in the file name.")] [Alias("ND")][switch]$NoDate, [parameter(Mandatory = $False, ValueFromPipelineByPropertyName = $True, HelpMessage = "Only the date in file name.")] [Alias("JD")][switch]$JustDate, [parameter(Mandatory = $False, ValueFromPipelineByPropertyName = $True, HelpMessage = "Move similar files to an 'old' directory if similar files exist.")] [Alias("M")][switch]$Move, [parameter(Mandatory = $False, ValueFromPipelineByPropertyName = $True, HelpMessage = "Return just the file name with no path or timestamp.")] [Alias("FL")][switch]$FlatName, [parameter(Mandatory = $False, ValueFromPipelineByPropertyName = $True, HelpMessage = "Omit the file extension from the output.")] [Alias("NE")][switch]$NoExtension ) begin { # Helper functions are defined once here so they are not redefined on every pipeline input. function GetScriptName { Try { $callStack = Get-PSCallStack $modulePath = $callStack[0].ScriptName # path to MRNAP.psm1 foreach ($frame in $callStack) { if ([string]::IsNullOrWhiteSpace($frame.ScriptName)) { continue } if ($frame.ScriptName -eq $modulePath) { continue } return [IO.Path]::GetFileNameWithoutExtension((Split-Path $frame.ScriptName -Leaf)) } return $null # No script frame found — running interactively } catch { return $null } } function GetRandomWord { $Words = @( 'Alpha', 'Ace', 'Bravo', 'Cat', 'Dan', 'Delta', 'Echo', 'Ethan', 'Foxtrot', 'Golf', 'Hotel', 'India', 'Juliet', 'Kathie', 'Kilo', 'Lima', 'Mat', 'November', 'Oscar', 'Phil', 'Quebec', 'Romeo', 'Sierra', 'Tango', 'Uniform', 'Victor', 'Whiskey', 'X-ray', 'Yoyo', 'Zachary', 'Zulu' ) return Get-Random -InputObject $Words } } process { $timestamp = "" # Set default report name if not provided if ([string]::IsNullOrWhiteSpace($ReportName) -and (-not $JustDate -or ($JustDate -and $NoDateTimeSeconds))) { $ScriptName = GetScriptName if ([string]::IsNullOrWhiteSpace($ScriptName)) { $ReportName = GetRandomWord } else { $ReportName = $ScriptName } } # Set default directory name if not provided if ([string]::IsNullOrWhiteSpace($DirectoryName)) { $DirectoryName = [IO.Path]::Combine($HOME, "Reports") } elseif (-not [IO.Path]::IsPathRooted($DirectoryName)) { # Relative path supplied — root it with the platform separator (\ on Windows, / on Linux/macOS) $DirectoryName = [IO.Path]::DirectorySeparatorChar + $DirectoryName } # Ensure the extension starts with a dot if (-not $NoExtension -and -not $Extension.StartsWith(".")) { $Extension = ".$Extension" } # Build the basic report name with extension $ReportNameExt = if ($NoExtension) { $ReportName } else { "$ReportName$Extension" } # Short-circuit for flat name: return just the filename with no path or timestamp if ($FlatName) { return [string]$ReportNameExt } # Format the timestamp based on the specified options if (-not $NoDateTimeSeconds) { $timestampFormat = "yyyy_MM_dd-" if ($AddTime) { $timestampFormat = "yyyy_MM_ddTHHmmss-" } elseif ($JustDate) { $timestampFormat = "yyyy_MM_dd" } elseif ($NoSeconds) { $timestampFormat = "yyyy_MM_ddTHHmm-" } if ($UTC) { # Handle UTC formatting for NoDate case if ($JustDate) { $timestamp = (Get-Date).ToUniversalTime().ToString($timestampFormat) # Only date in UTC } elseif ($NoDate) { $fmt = if ($NoSeconds) { "HHmm-" } else { "HHmmss-" } $timestamp = (Get-Date).ToUniversalTime().ToString($fmt) # Only time in UTC, no date } else { $timestampFormat = "yyyy_MM_ddTHHmmss-" if ($NoSeconds) { $timestampFormat = "yyyy_MM_ddTHHmm-" } $timestamp = (Get-Date).ToUniversalTime().ToString($timestampFormat) # Full date and time in UTC } } else { # Handle local time formatting if ($NoDate) { $fmt = if ($NoSeconds) { "HHmm-" } else { "HHmmss-" } $timestamp = (Get-Date).ToString($fmt) # Local time with no date } elseif ($JustDate) { $timestamp = (Get-Date).ToString($timestampFormat) } else { $timestamp = Get-Date -Format $timestampFormat # Full date and time in local time } } } # Handle separators if needed if ($NoSeparators) { $timestamp = $timestamp -replace "_", "" -replace "-", "" } # Build the full file path $FullPath = Join-Path $DirectoryName "$timestamp$ReportNameExt" # Move files to "old" directory if specified if ($Move) { if (-not (Test-Path $DirectoryName)) { try { New-Item -ItemType Directory -Path $DirectoryName -Force -ErrorAction Stop | Out-Null } catch { Write-Warning "Unable to create directory $DirectoryName" } } Else { $items = @() try { $items = Get-ChildItem -Path $DirectoryName -Filter $ReportNameExt -File -ErrorAction Stop -Force } catch { Write-Warning "Unable to list files in $DirectoryName." } if ($items.count -gt 0) { $oldDirectory = Join-Path $DirectoryName "old" if (-not (Test-Path $oldDirectory)) { try { New-Item -ItemType Directory -Path $oldDirectory -Force -ErrorAction Stop | Out-Null } catch { Write-Warning "Unable to create directory and move any related files to $oldDirectory" } } try { $items | Move-Item -Destination $oldDirectory -ErrorAction Stop -Force | Out-Null } catch { Write-Warning "Problem moving files to $oldDirectory." } } } } return [string]$FullPath } } |