Variable concatenation in wrong order - string

I have the following:
function createFolder($folderName, $curPath)
{
$dest = $curPath + $folderName
write-host "Path is : " + $dest
# code to mess around with files etc
}
When I run this it gives me the following output:
Path is : + Test_Folder C:\Users\Me
Is there something I'm missing with the + operator, or a join/concat method that is meant for this kind of function? What is the correct way to create/concat/manipulate paths in PowerShell (I've just started using this to automate some cleanup tasks on my desktop).
EDIT: In case it makes a difference, this is what I see when I run the version command:
PS C:\Users\Me> version
BladeLogic Network Shell 8.2.01.273 (Release) [May 12 2012 21:56:02]
Copyright (C) 1996-2012 BladeLogic Inc.
Also, I'm on a work computer with no administrative privileges.
I tried:
$currentPath = "C:\Users\n0223270\Downloads"
$test = "test"
createFolder($test, $currentPath)
function createFolder($folderName, $curPath)
{
$dest = join-path -path $curPath -childpath $folderName
Write-Host $dest
}
This was the following error:
Join-Path : Cannot bind argument to parameter 'Path' because it is null.
At line:4 char:28
+ $dest = join-path -path <<<< $curPath -childpath $folderName
+ CategoryInfo : InvalidData: (:) [Join-Path], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.JoinPathCommand

createFolder($test, $currentPath)
This isn't how you call a Powershell function. This is passing the first parameter as an array of two strings. The second parameter would be null because it's not specified and there's no default.
Try:
createFolder $test $currentPath

Related

Powershell excel to csv script works in ISE but not in powershell console

I have a short script that takes certain worksheets out of an excel file and saves them individually as a CSV file.
Function ExcelToCsv ($File) {
$myDir = Split-Path -Parent $($global:MyInvocation.MyCommand.Definition)
$path = "$myDir\csv_files"
If(!(test-path -PathType container $path))
{
Write-Host "Folder csv_files not found, creating..."
New-Item -ItemType Directory -Path $path
}
$excelFile = "$myDir\" + $File + ".xlsx"
$Excel = New-Object -ComObject Excel.Application
$wb = $Excel.Workbooks.Open($excelFile)
$excel.DisplayAlerts = $false;
foreach ($ws in $wb.Worksheets) {
if($ws.name -like '*sheet*') {
$ws.SaveAs("$myDir\csv_files\" + $ws.name + ".csv", 6, 0, 0, 0, 0, 0, 0, 0, $true)
Write-Host "Saved file: $myDir\csv_files\"$ws.name".csv"
} else {
Write-Host "Worksheet "$ws.name" not an correct sheet. Not saved"
}
}
$Excel.Quit()
}
$FileName = "myexcel"
ExcelToCsv -File $FileName
Write-Host "`nCSV files successfully created"
read-host "Press ENTER to exit...."
This is the entire code, not much to it and this is the correct output when run from ISE:
PS U:\excel_to_csv> U:\excel_to_csv\create_csv_files.ps1
Saved file: U:\excel_to_csv\csv_files\ 1sheet .csv
Saved file: U:\excel_to_csv\csv_files\ 2sheet .csv
Saved file: U:\excel_to_csv\csv_files\ 3sheet .csv
CSV files successfully created
Press ENTER to exit....:
But when I use a Powershell console:
Folder csv_files not found, creating...
New-Item : Cannot find drive. A drive with the name 'if((Get-ExecutionPolicy ) -ne 'AllSigned') { Set-ExecutionPolicy -
Scope Process Bypass }; & 'U' does not exist.
At U:\excel_to_csv\create_csv_files.ps1:7 char:9
+ New-Item -ItemType Directory -Path $path
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (if((Get-Executi... Bypass }; & 'U:String) [New-Item], DriveNotFoundExce
ption
+ FullyQualifiedErrorId : DriveNotFound,Microsoft.PowerShell.Commands.NewItemCommand
Ľutujeme, if((Get-ExecutionPolicy ) -ne 'AllSigned') { Set-ExecutionPolicy -Scope Process Bypass }; & 'U:\excel_to_csv\
myexcel.xlsx sa nepodarilo nájsť. Je možné, že bol premiestnený, premenovaný alebo odstránený.
At U:\excel_to_csv\create_csv_files.ps1:11 char:5
+ $wb = $Excel.Workbooks.Open($excelFile)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], COMException
+ FullyQualifiedErrorId : System.Runtime.InteropServices.COMException
CSV files successfully created
Press ENTER to exit....:
Some of it is written in Slovakian, it roughly translates to:
Sorry, if((Get-ExecutionPolicy ) -ne 'AllSigned') { Set-ExecutionPolicy -Scope Process Bypass }; & 'U:\excel_to_csv\
myexcel.xlsx could not be found. It is possible it was moved, renamed or deleted.
I have tried searching for a solution but nothing worked yet.
I tried making a bash script like this:
powershell.exe -noexit -file U:\create_csv_files.ps1
But that didn't work, not sure if I screwed it up.
As I am on a company computer I am unable to use administrator privileges, but if you believe the lack of them is the problem, I might be able to do something about it.
Thank you for any and all help!
Replace
$myDir = Split-Path -Parent $($global:MyInvocation.MyCommand.Definition)
with
$myDir = $PSScriptRoot
The automatic $PSScriptRoot variable reliably reports the full path of the directory in which the currently executing script is located, irrespective of how the script was invoked. The related $PSCommandPath variable contains the script file's own full path.
Your symptom implies that you invoked your script from outside a PowerShell session, via powershell.exe -Command (with the -Command CLI parameter potentially being positionally implied), in which case $global:MyInvocation.MyCommand.Definition contains the entire command text passed to -Command.

Excel.Application: Microsoft Excel cannot access the file '[<filename>]' There are several possible reasons:

I have a PowerShell script that works, it helps me run multiple queries against multiple servers and save each output in different CSV and then merge them together into an Excel file.
$Servers = get-content -Path "Servers.txt"
$DatabaseName ="master"
#$credential = Get-Credential #Prompt for user credentials
$secpasswd = ConvertTo-SecureString "MyPassword" -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential ("sa", $secpasswd)
$QueriesFolder = "Queries\"
$ResultFolder = "Results\"
ForEach($Server in $Servers)
{
$DateTime = (Get-Date).tostring("yyyy-MM-dd")
ForEach ($filename in get-childitem -path $QueriesFolder -filter "*.sql" | sort-object {if (($i = $_.BaseName -as [int])) {$i} else {$_}} )
{
$oresults = invoke-sqlcmd -ServerInstance $Server -Database $DatabaseName -Credential $credential -InputFile $filename.fullname
write-host "Executing $filename on $Server"
$BaseNameOnly = Get-Item $filename.fullname | Select-Object -ExpandProperty BaseName
$oresults | export-csv $ResultFolder$BaseNameOnly.csv -NoTypeInformation -Force
}
$All_CSVs = get-childitem -path $ResultFolder -filter "*.csv" | sort-object {if (($i = $_.BaseName -as [int])) {$i} else {$_}}
$Count_CSVs = $All_CSVs.Count
Write-Host "Detected the following CSV files: ($Count_CSVs)"
Write-Host " "$All_CSVs.Name"`n"
$ExcelApp = New-Object -ComObject Excel.Application
$ExcelApp.SheetsInNewWorkbook = $All_CSVs.Count
$output = "C:\Users\FrancescoM\Desktop\CSV\Results\" + $Server + " $DateTime.xlsx"
if (Test-Path $output)
{
Remove-Item $output
Write-Host Removing: $output because it exists already
}
$xlsx = $ExcelApp.Workbooks.Add()
for($i=1;$i -le $Count_CSVs;$i++)
{
$worksheet = $xlsx.Worksheets.Item($i)
$worksheet.Name = $All_CSVs[$i-1].Name
$file = (Import-Csv $All_CSVs[$i-1].FullName)
$file | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation | Clip
$worksheet.Cells.Item(1).PasteSpecial()|out-null
}
$xlsx.SaveAs($output)
Write-Host Creating: $output
$ExcelApp.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($xlsx) | Out-Null;
Write-Host "Closing all worksheet"
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($ExcelApp) | Out-Null;
Write-Host "Closing Excel"
[System.GC]::Collect();
[System.GC]::WaitForPendingFinalizers()
Remove-Item "$ResultFolder\*" -Include *.csv
Write-Host "Cleaning all *.csv"
Start-Sleep -Seconds 3
}
In order to make this script more portable I want all the paths mentioned into it to be stored into a variable and then concatenated.
But as soon as I change:
$output = "C:\Users\FrancescoM\Desktop\CSV\Results\" + $Server + " $DateTime.xlsx"
into:
$output = $ResultFolder + $Server + " $DateTime.xlsx"
things get nasty and I receive the error:
Microsoft Excel cannot access the file 'C:\Users\FrancescoM\Documents\Results\0DC80000'.
There are several possible reasons:
• The file name or path does not exist.
• The file is being used by another program.
• The workbook you are trying to save has the same name as a currently open workbook.
At C:\Users\FrancescoM\Desktop\CSV\QueryLauncher.ps1:50 char:2
+ $xlsx.SaveAs($output)
+ ~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], COMException
+ FullyQualifiedErrorId : System.Runtime.InteropServices.COMException
I don't understand, I think I'm concatenating things right.
I also followed this StackOverflow post and restarted my computer after adding "C:\Windows\SysWOW64\config\systemprofile\desktop" but the problem isn't fixed.
How can a variable path mess things up with Excel?
Because you are not defining the full path in the $ResultFolder variable, it will be expanded using the current working directory.
Just look at the path you want it to be:
"C:\Users\FrancescoM\Desktop\CSV\Results\" + $Server + " $DateTime.xlsx"
and the resulting path using the partial $ResultFolder variable:
C:\Users\FrancescoM\Documents\Results\0DC80000
Since you want the output file in a folder on your desktop, set the $output to
$output = Join-Path $([Environment]::GetFolderPath("Desktop")) "CSV\Results\$Server $DateTime.xlsx"
EDIT
From your last comment I understand that you want the output to be in a subfolder called "Results" that resides inside the folder the script itself is in.
In that case do this:
# get the folder this script is running from
$ScriptFolder = if ($PSScriptRoot) { $PSScriptRoot } else { Split-Path $MyInvocation.MyCommand.Path }
# the following paths are relative to the path this script is in
$QueriesFolder = Join-Path -Path $ScriptFolder -ChildPath 'Queries'
$ResultFolder = Join-Path -Path $ScriptFolder -ChildPath 'Results'
# make sure the 'Results' folder exists; create if not
if (!(Test-Path -Path $ResultFolder -PathType Container)) {
New-Item -Path $ResultFolder -ItemType Directory | Out-Null
}
Then, when it becomes time to save the xlsx file, create the full path and filename using:
$output = Join-Path -Path $ResultFolder -ChildPath "$Server $DateTime.xlsx"
$xlsx.SaveAs($output)
P.S. I advice to use the Join-Path cmdlet to combine file paths or to make use of [System.IO.Path]::Combine() instead of joining paths together like you do with this line: $oresults | export-csv $ResultFolder$BaseNameOnly.csv. Using the latter can lead to unforeseen pathnames if ever you forget to postfix the first path part with a backslash.
P.S.2 Excel has its own default output path set in Tools->Options->General->Default File Location and has no idea of the relative path for the script. This is why you should save using a Full path and filename.

Why does PowerShell not want to save my file as a CSV

$Path = 'D:/ETL_Data/TwitchTVData.xlsx'
$csvPath = 'D:/ETL_Data/TwitchTVData2.csv'
# Open the Excel document and pull in the 'Sheet1' worksheet
$Excel = New-Object -Com Excel.Application
$Workbook = $Excel.Workbooks.Open($Path)
$page = 'Sheet1'
$ws = $Workbook.Worksheets | Where-Object {$_.Name -eq $page}
$Excel.Visible = $true
$Excel.DisplayAlerts = $false
# Set variables for the worksheet cells, and for navigation
$cells = $ws.Cells
$row = 1
$col = 4
$formula = #"
=NOW()
"#
# Add the formula to the worksheet
$range = $ws.UsedRange
$rows = $range.Rows.Count
for ($i=0; $i -ne $rows; $i++) {
$cells.Item($row, $col) = $formula
$row++
}
$ws.Columns.Item("A:D").EntireColumn.AutoFit() | Out-Null
$ws.Columns.Range("D1:D$rows").NumberFormat = "yyyy-MM-dd hh:mm"
$Excel.ActiveWorkbook.SaveAs($csvPath)
$Excel.Quit()
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
https://www.experts-exchange.com/questions/27996530/How-to-convert-and-xlsx-spreadsheet-into-CSV.html#answer38780402-20
I was attempting to follow that, but for some reason for me the SaveAs() doesn't work. It gives me an error
cannot access the file 'D://ETL_Data/E567DF00
What do I have to do to get this to save over to CSV?
Edit:
Exact error without the fileformat parameter 6 as suggested in the comments:
Microsoft Excel cannot access the file 'D:\//ETL_Data/8011FF00'. There are
several possible reasons:
o The file name or path does not exist.
o The file is being used by another program.
o The workbook you are trying to save has the same name as a currently open
workbook.
At D:\PS_Scripts\twitchExcelAddSnapShot.ps1:32 char:1
+ $Excel.ActiveWorkbook.SaveAs($csvPath)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], COMException
+ FullyQualifiedErrorId : System.Runtime.InteropServices.COMException
Exact error with fileformat parameter 6:
The file could not be accessed. Try one of the following:
o Make sure the specified folder exists.
o Make sure the folder that contains the file is not read-only.
o Make sure the file name does not contain any of the following characters:
< > ? [ ] : | or *
o Make sure the file/path name doesn't contain more than 218 characters.
At D:\PS_Scripts\twitchExcelAddSnapShot.ps1:32 char:1
+ $Excel.ActiveWorkbook.SaveAs($csvPath,6)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], COMException
+ FullyQualifiedErrorId : System.Runtime.InteropServices.COMException
While PowerShell is pretty forgiving when it comes to path separators, COM+ servers (like Excel.Application) might not be.
Change the $csvPath variable value to use \ instead of /:
$csvPath = 'D:\ETL_Data\TwitchTVData2.csv'
To complement Mathias R. Jessen's helpful answer with background information:
It seems that Excel's application-specific behavior is the cause of your problem, unrelated to the underlying foundational subsystems or APIs used.
(Excel's automation API happens to be a COM server.)
I'm unclear on why Excel acts this way - it also does so in interactive use, though you could argue that at least in programmatic use it should allow / too.
To offer generalized advice:
On Windows, to be safe, use \, especially when dealing with application-level automation APIs, though at the level of system APIs / should work as well - see below.
In cross-platform code, use / (but watch out for the exceptions above; [System.IO.Path]::DirectorySeparatorChar reports the platform-appropriate (primary) character).
Though rarely used, Windows at the API level allows interchangeable use of \ and / (which apparently goes back to the DOS 2.0 days), when support for directories was introduced), and that is also reflected in higher-level subsystems, as the following examples demonstrate.
# PS: OK
# Should output c:\windows\win.ini
(Get-Item c:/windows/win.ini).FullName
# .NET: OK
# Should return the 1st child dir.'s path.
# Note that the child directory names will be appended with "\", not "/"
[System.IO.Directory]::GetDirectories('c:/windows/system32') | Select-Object -First 1
# COM (see Excel exception below): OK
# Should return $true
(New-Object -ComObject Scripting.FileSystemObject).FileExists('c:/windows/win.ini')
# Windows API: OK
# Should return a value such as 32.
(Add-Type -PassThru WinApiHelper -MemberDefinition '[DllImport("kernel32.dll")] public static extern uint GetFileAttributes(string lpFileName);')::GetFileAttributes('c:/windows/win.ini')
# cmd.exe: INCONSISTENTLY SUPPORTED
# Note: *quoting matters* here, so that tokens with / aren't mistaken for options.
# attrib: works
cmd /c 'attrib "c:/windows/win.ini"'
# dir: works with DIRECTORIES, but fails with FILES
cmd /c 'dir /b "c:/windows/system32"' # OK
cmd /c 'dir /b "c:/windows/win.ini"' # FAILS with 'File not found'
cmd /c 'dir /b "c:/windows\win.ini"' # Using \ for the FILE component (only) works.
Here's a minimal example that demonstrates Excel's problem with /:
# Create temporary dir.
$null = mkdir c:\tmp -force
$xl=New-Object -Com Excel.Application
$wb = $xl.Workbooks.Add()
# OK - with "\"
$wb.SaveAs('c:\tmp\t1.xlsx')
# FAILS - with "/":
# "Microsoft Excel cannot access the file 'C:\//tmp/F5D39400'"
$wb.SaveAs('c:/tmp/t2.xlsx')
$xl.Quit()
# Clean up temp. dir.

DateTime variable not parsing correctly, string works

I'm trying to pass a variable into a powershell command like so:
$Today = Get-Date
Get-SCSMClassInstance -Filter {ClosedDate -eq $Today}
But I get this error:
Get-SCSMClassInstance : ClosedDate_C529833E_0926_F082_C185_294CBC8BB9FD='$Today'
-- String was not recognized as a valid DateTime.
At line:1 char:1
+ Get-SCSMClassInstance -ComputerName $computer $IncidentClass -Filter
{ClosedDate ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (Microsoft.Syste...InstanceComman
d:GetSCClassInstanceCommand) [Get-SCClassInstance], UnknownDatabaseException
+ FullyQualifiedErrorId : ExecutionError,Microsoft.SystemCenter.Core.Commands
.GetSCClassInstanceCommand
If I pass the string directly, it works fine, regardless of format:
Get-SCSMClassInstance -Filter {ClosedDate -eq "Friday, June 5, 2015 11:42:33 AM"}
Get-SCSMClassInstance -Filter {ClosedDate -gt "2015-6-5 11:42:33Z"}
I've tried setting the variable as DateTime as well as a string, every imaginable format - I've ensured that, when the variable is parsed, it will absolutely turn into the correct string, but nothing works.
Any ideas?
The Filter parameter is a string not scriptblock. It is working with a scriptblock in those other cases because the string form of script block is the text of the block without the surrounding braces. However, in this case, you need the variable to be substituted in the filter. Try this:
Get-SCSMClassInstance -Filter "ClosedDate -eq ""$Today"""

Combination into a file done in powershell (String + Variable)

My question is related about how can I write a line that combines a string (that contains a $ symbol) and a variable into a single line that goes into text file.
I'm creating a ps1 from a function.
function pwd{
param(
[Parameter(ValueFromPipeline=$True,position=0,mandatory=$true)][String]$Temp,
[Parameter(ValueFromPipeline=$True,position=1,mandatory=$true)][String]$Path,
[Parameter(ValueFromPipeline=$True,position=2,mandatory=$true)][String]$SubscriptionName
)
$NewFilePath = "$Path\newfile.ps1"
New-Item -ItemType file $NewFilePath -Force
Add-Content $NewFilePath ' $TimeStart=Get-Date'
Add-Content $NewFilePath " $AccountName=""$($SubscriptionName)"" " #Here's my doubt
}
$ScriptPath = split-path -parent $MyInvocation.MyCommand.Definition
pwdtest -Temp "String" -Path $ScriptPath -SubscriptionName "mail#something.com"
$ScriptPath = split-path -parent $MyInvocation.MyCommand.Definition
pwdtest -Temp "String" -Path $ScriptPath -SubscriptionName "mail#something.com"
So calling the function pwd this way:
$ScriptPath = split-path -parent $MyInvocation.MyCommand.Definition
pwd -Temp "String" -Path $ScriptPath -SubscriptionName "mail#something.com"
How can I rewrite that line?
Add-Content $NewFilePath " $AccountName=""$($AutomationAccount)"" "
Because I need to write in the text file this:
$TimeStart=Get-Date
$AccountName = "mail#something.com"
And right now if I use that line I've got this in the text file:
$TimeStart=Get-Date
="mail#something.com"
How can I say PowerShell Add-Content not interpretate the $AccountName as a "NULL" value and I need to use it as a content string and not a variable.
I had this idea of combination:
Add-Content $NewFilePath ' $AccountName=' + " ""$($SubscriptionName)"" "
But It doesn't work.
Add-Content : A positional parameter cannot be found that accepts argument '+'.
At C:\Users\JoseGabriel\test.ps1:10 char:5
+ Add-Content $NewFilePath ' $AccountName=' + " ""$($SubscriptionName)"" "
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Add-Content], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.AddContentCommand
Have you tried:
Add-Content $NewFilePath " `$TimeStart=Get-Date"
Add-Content $NewFilePath " `$AccountName=""$($SubscriptionName)"""
Escape characters, Delimiters and Quotes
The PowerShell escape character is the grave-accent(`).
The escape character can be used in three ways:
When used at the end of a line, it is a continuation character - so the command will continue on the next line.
To indicate that the next character following should be passed without substitution. For example $myVariable will normally be expanded to display the variables contents but `$myVariable will just be passed as $myVariable.
When used inside double quotation marks, the escape character indicates that the following character should be interpreted as a 'special' character.
Source: http://ss64.com/ps/syntax-esc.html

Resources