Powershell dot-sourcing variables inside variables not expanding - powershell

I've got the following problem:
Say I have 2 powershell scripts, named A.ps1 and conf.ps1.
conf.ps1 contents are just a few vars that will be dot-sourced by A.ps1, like this:
$dateLogs = Get-Date -UFormat '%Y%m%d'
$installDir = 'C:\Gcloud\'
$logDir = '$installDir\GcloudLogs'
$logFile = '$logDir\$dateLogs\logFile.txt'
When imported, funny thing is that $dateLogs is expanded and in the debugger I can see "..\20190805\logFile.txt" but $installDir, for some reason, won't expand.
So instead of having $logFile = "C:\Gcloud\GcloudLogs\20190805\logFile.txt" I end up having "$installDir\GcloudLogs\20190805\logFile.txt"and that $installDir won't ever expand to its real value.
Am I missing something?
Any lead would be much appreciatd since I've been struggling for a long time with this. I tried several things like:
- ${$installDir}\GcloudLogs
- $($installDir\GcloudLogs)
- $(${installDir}\GcloudLogs)
With single quotes, double quotes and no quotes at all... None of that worked out.
Thank you all beforehand.

In order to not confuse single or double quotes and to save you from getting paths with double backslashes, it is always safer to use the Join-Path cmdlet.
$dateLogs = '{0:yyyyMMdd}' -f (Get-Date) # a more 'PowerShelly' way of formatting a date
$installDir = 'C:\Gcloud'
$logDir = Join-Path -Path $installDir -ChildPath 'GcloudLogs'
$logFile = Join-Path -Path $logDir -ChildPath "$dateLogs\logFile.txt" # use double-quotes here
It is also possible to use the .NET [System.IO.Path]::Combine() function
$dateLogs = '{0:yyyyMMdd}' -f (Get-Date)
$installDir = 'C:\Gcloud'
$logDir = [System.IO.Path]::Combine($installDir, "GcloudLogs")
$logFile = [System.IO.Path]::Combine($logDir, $dateLogs, "logFile.txt")
Both methods will create these paths:
$logDir --> C:\Gcloud\GcloudLogs
$logFile --> C:\Gcloud\GcloudLogs\20190805\logFile.txt

There's a difference between single and double quotes in string constants in Powershell. Try using double quotes instead.

Related

How to set Get-Date format as variable but get up to date time in function

I'm trying to store a get-date format as a variable so it's at the top of the file but be able to have it get an up to date get-date in functions using the variable name. Example:
$LogDir = "c:\somefolders"
$LogSubDir = $(Get-Date -f MM-yyyy)
function MakeLogSubDirs{
$Path = "$LogDir\$LogSubDir" # rather than "$LogDir\$(Get-Date -f MM-yyyy)"
If(!(test-path $path)) {
New-Item -ItemType Directory -Path $path
}
But I don't want it to set the time in the variable, I want the variable in the variable, to get gotten in the function. I tried single quotes $LogSubDir = '$(Get-Date -f MM-yyyy)', but that didn't work. "$LogDir\$(Get-Date -f MM-yyyy)" works to make the folder. Any suggestions?
here is a demo of the save the pattern into a $Var & use that later idea ... [grin]
$EU_Backwards = 'dd-MM-yyyy'
$US_InsideOut = 'MM-dd-yyyy'
$SaneSortable = 'yyyy-MM-dd'
Get-Date -Format $EU_Backwards
output = 15-05-2022
as an aside, try to use the sane, sortable largest 1st layout whenever you can. [grin]

Multiple variables in path

I am planning on using the following script by looping through a text file to set variables for the location of the source PDF, and designate the path to create a new folder (with week number) to move the source PDF to.
$pdfSource = 'C:\path\in\text\file'
$newFolder = 'C:\path\to\newfolder\in\text\file'
Get-ChildItem $pdfSource '*.pdf' -Recurse | foreach {
$x = $_.LastWriteTime.ToShortDateString()
$new_folder_name = Get-Date $x -UFormat %V
$des_path = "C:\path\to\newfolder\$new_folder_name"
if (Test-Path $des_path) {
Move-Item $_.FullName $des_path
} else {
New-Item -ItemType Directory -Path $des_path
Move-Item $_.FullName $des_path
}
}
I can't seem to figure out the syntax for the line below to include the $newFolder path variable along with the existing $new_folder_name I'm creating.
$des_path = "C:\path\to\newfolder\$new_folder_name"
Option-1:
$des_path = "${newFolder}\${new_folder_name}"
Option-2:
$des_path = "${0}\${1}" -f $newFolder, $new_folder_name
Option-3:
$des_path = $newFolder + $new_folder_name
Option-4:
$des_path = Join-Path -Path $newFolder -ChildPath $new_folder_name
There's nothing wrong with your approach to string expansion (interpolation):
$new_folder_name = 'foo' # sample value
$des_path = "C:\path\to\newfolder\$new_folder_name" # use string expansion
yields string literal C:\path\to\newfolder\foo, as expected.
Adam's answer shows you alternatives to constructing file paths, with Join-Path being the most robust and PowerShell-idiomatic, albeit slow.
Another option is to use [IO.Path]::Combine():
[IO.Path]::Combine('C:\path\to\newfolder', $new_folder_name)
The way you calculate the value for $new_folder_name should be problematic if your current culture is not en-US (US-English), but due to a bug actually isn't[1]; either way, it should be simplified:
Instead of:
$x = $_.LastWriteTime.ToShortDateString()
$new_folder_name = Get-Date $x -uformat %V
use:
$new_folder_name = Get-Date $_.LastWriteTime -uformat %V
That is, pass $_.LastWriteTime directly to Get-Date, as a [datetime] instance - no detour via a string representation needed.
[1] .ToShortDateString() returns a culture-sensitive string representation, whereas PowerShell typically uses the invariant culture to ensure cross-culture consistency; therefore, if you pass a string to a parameter that accepts a [datetime] instance, it is the invariant culture's formats that should (only) be recognized, not the current culture's. While that is true for functions written in PowerShell, in compiled cmdlets (typically C#-based), the current culture is unexpectedly applied; while this is a bug, a decision was made not to fix it for the sake of backward compatibility - see this GitHub issue

Add-Content String Quotes with Variable

I am tring to add a line to a text file I created however it needs to contain text quotes around a variable.
An example I am working with:
$wksname="wks12345"
$path = c:\scripts\file.txt
Add-Content $path '"DefaultUsername" =' "$wksname"
I get an error stating that a positional parameter is not found.
What I would like the output to look like in the text file is:
"DefaultUsername" = "wks12345"
Any ideas?
Thanks!
Use two consecutive " as an escape sequence:
Add-Content $path """DefaultUsername"" = ""$wksname"""
Use the escape char `
$wksname = "TEST123"
write-host "`"DefaultUsername`" = `"$wksname`""
Would wrote out "DefaultUsername" = "TEST123"
As for the command it should look like
$wksname="wks12345"
$path = "c:\scripts\file.txt"
Add-Content -Path $path -Value "`"DefaultUsername`" = `"$wksname`""
Your string quoting is wrong. Here's your command:
Add-Content $path '"DefaultUsername" =' "$wksname"
You've got three parameters:
$path
'"DefaultUsername" ='
"$wksname"
And Add-Content only has 2 positional parameters (-Path and -Value). PowerShell can't find a third parameter to use.
If you need to use double quotes in a string and you also want to expand variables in your string, you need to escape your double quotes. There's two ways to do it.
Like this:
Add-Content -Path $path -Value """DefaultUsername"" = ""$wksname"""
Or this:
Add-Content -Path $path -Value "`"DefaultUsername`" = `"$wksname`""

Combine path, file strings and literals for path

Trying to combine a path, filename, and add some text along with a variable for Out-File log.
I've tried many alternatives unsuccessfully and need assistance;
FormattedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$OldVersion = C:\Temp\TestFile.txt
$OldPath = (Get-Item $OldVersion).DirectoryName
$OldBaseName = (Get-Item $OldVersion).BaseName
ErrFile = Join-Path $OldPath OldBaseName
Out-File -FilePath "$ErrFile_$FormattedDate Error.txt"
Out-File -FilePath "$($OldPath)$($OldBaseName)_$($FormattedDate)_Error.txt"
...just two examples.
I've tried many other combinations and driving me crazy.
Basically I want it to be.
C:\Temp\TestFile_2017-08-24 16:51:36_Error.txt
Update:
I've tried both
$filename = '{0}_{1:s}_Error{2}' -f $basename, (Get-Date), $extension
I get _2017-08-25T13:02:17_Error.txt but no basename (TestFile).
$newpath = "${dirname}\${basename}_${date}_Error${extension}"
I get
A drive with the name '_2017-08-25 13' does not exists.
Can you also explain or provide a resource of what '{0}_{1:s}_Error{2}' and/or '{0}_{1:yyyy-MM-dd HH:mm:ss}_Error{2}' does?
Use the format operator (-f) for constructing the filename and Join-Path for building the path.
$oldpath = 'C:\Temp\TestFile.txt'
$basename = [IO.Path]::GetFileNameWithoutExtension($oldpath)
$extension = [IO.Path]::GetExtension($oldpath)
$filename = '{0}_{1:yyyy-MM-dd HH:mm:ss}_Error{2}' -f $basename, (Get-Date), $extension
$newpath = Join-Path ([IO.Path]::GetDirectoryName($oldpath)) $filename
Unless you must have the space in the date format you could simplify the format string by using the standard sortable format specifier (s) that will produce date strings like 2017-08-24T23:58:25 instead of 2017-08-24 23:58:25.
$filename = '{0}_{1:s}_Error{2}' -f $basename, (Get-Date), $extension
If you want to construct the path as a string with inline variables you need to make sure that the underscores in your file name are kept separate from the variable name. Because underscores are valid name components for variable names $var_ is the variable var_, not the variable var followed by a literal underscore. Use curly braces to ensure that variables and literal underscores don't get mixed up.
$oldpath = 'C:\Temp\TestFile.txt'
$date = (Get-Date).ToString('yyyy-MM-dd HH:mm:ss')
$dirname = [IO.Path]::GetDirectoryName($oldpath)
$basename = [IO.Path]::GetFileNameWithoutExtension($oldpath)
$extension = [IO.Path]::GetExtension($oldpath)
$newpath = "${dirname}\${basename}_${date}_Error${extension}"
Addendum: Your file names should not contain colons. Colons in Windows paths either terminate a drive name or separate the file name from the name of an alternate data stream. Your date format string should rather be something like yyyy-MM-dd HH-mm-ss or yyyy-MM-dd_HH-mm-ss to avoid this pitfall.

Robocopy didn't recognize options when launched from PowerShell

I'm trying to use the PowerShell cmdlet Invoke-Expression to launch RoboCopy.
In the script below, RoboCopy worked fine when the option was simply '.' but as soon as the option '/MIR' was added I got this "Invalid Parameter #3" error.
It seems that RoboCopy is having problems parsing '/MIR' and has choked on the forward slash in the option. I've tried using all sort of escaping characters to no avail!
# Source & Destination paths
#
[string]$srcPath = 'C:\folderSrc'
[string]$desPath = 'C:\folderDes'
# Example 1
# ----------
# This works - note how $option1 contains only '*.*'
#
[string]$option1 = '*.*'
[string]$line = 'RoboCopy $srcPath $desPath $option1'
Invoke-Expression "$line"
# Example 2:
# ----------
# This doesn't work - after '/MIR' is added to the option, RoboCopy seems to choke on the forward slash in '/MIR'
#
[string]$option2 = '*.* /MIR'
[string]$line = 'RoboCopy $srcPath $desPath $option2'
Invoke-Expression "$line"
I found that this (using double InvokeExpression) worked:
[string]$srcPath = 'C:\folderSrc'
[string]$desPath = 'C:\folderDes'
[string]$option = '*.* /MIR'
[string]$line = 'Invoke-Expression "RoboCopy $srcPath $desPath $option"'
Invoke-Expression "$line"
But couldn't explain why this (using single Invoke-Expression) also works:
[string]$srcPath = 'C:\folderSrc'
[string]$desPath = 'C:\folderDes'
[string]$option = '*.*'
[string]$line = 'RoboCopy $srcPath $desPath $option'
Invoke-Expression "$line"
Note that the sole difference in the 2 scenarios is the $option variable:
'*.*' vs. '*.* /MIR'
Inconsistency like this is utterly demoralizing...
Powershell doesn't expand $variables when using single quotes.
Use double quotes here:
[string]$line = "RoboCopy $srcPath $desPath $option1"
And it might make better sense to not use Invoke-Expression
RoboCopy.exe $srcPath $desPath *.* /MIR
Should work