A positional parameter cannot be found that accepts argument - sql-server

I wrote a dynamic SQL to build the below query and execute it using xp_cmdshell in SQL Server
POWERSHELL -command "$d = Get-Date Get-childItem "c:\ServerManagement\Logs\SQL Server Agent" -recurse -include *.log | Where {($_.lastwritetime -le $d.Addhours(-120))} | Remove-Item -Force"
but when I run it on the command prompt, I get this error:
Get-ChildItem : A positional parameter cannot be found that accepts argument 'Agent'.
At line:1 char:19
+ ... = Get-Date; Get-childItem c:\ServerManagement\Logs\SQL Server Agent - ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-ChildItem], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
My intention is to run the above code as a SQL Server Agent job with the job type as "CMDEXEC"
I am a beginner at PowerShell and couldn't figure out how to troubleshoot the above error.
Executing the PowerShell code from the Powershell ISE window works just fine. -
Any help is appreciated.

Through the double quotes in double quotes your command was split in three parts
"$d = Get-Date; Get-childItem "
c:\ServerManagement\Logs\SQL Server Agent
" -recurse -include *.log | Where {($_.lastwritetime -le
$d.Addhours(-120))} | Remove-Item -Force"
Powershell tried to translate this using positional parameter which translates to:
Get-ChildItem -Path C:\ServerManagement\Logs\SQL -Filter Server Agent
There are multiple ways to avoid this:
Using different quotes like you did
"'String'"
Escaping the inner double quotes
"\"String\""
Using a Scriptblock instead of quotes to encapsulate the Powershell part
powershell.exe -command {"String"}

Your whole argument is wrapped in double quotes. Then you cannot wrap parts of it in double quotes, you need single quotes. Or you can escape the double quote using the back-tick `:
POWERSHELL -command "$d = Get-Date; Get-childItem `"c:\ServerManagement\Logs\SQL Server Agent`" -recurse -include *.log | Where {($_.lastwritetime -le $d.Addhours(-120))} | Remove-Item -Force"

I have figured it out through trial and error. I replaced the double quotes with single quotes and the code started to work, I don't know why though.
POWERSHELL -command "$d = Get-Date; Get-childItem 'c:\ServerManagement\Logs\SQL Server Agent' -recurse -include *.log | Where {($_.lastwritetime -le $d.Addhours(-120))} | Remove-Item -Force"

Related

insert header row from bat into csv file

I have created a bat file that creates a csv file, please see below
#echo off
echo %date%,%time%,%computername%,%username% >> %random%.csv
This produces csv file which contains the following data:
29/05/2021,15:35:31.10,PC9083,fmartin
But I need the bat file to include these headers e.g Date,Time,Host,Name if possible e.g.
Date,Time,Host,Name
29/05/2021,15:35:31.10,PC9083,fmartin
Any ideas on how to accomplish this? any info would be greatly appreciated
Many Thanks
John
If you wanted to move ahead and use PowerShell, this would work. Using -UseQuotes AsNeeded requires PowerShell Core 7+. If you remove -UseQuotes AsNeeded it will work with Windows PowerShell 5.1, but every field will be quoted.
New-Object psobject -Property ([ordered]#{"Date"=(Get-Date).ToString('dd/MM/yyyy');
"Time"=(Get-Date).ToString('HH:mm:ss.ff');"Host"=$Env:COMPUTERNAME;"Name"=$Env:USERNAME}) |
Export-Csv -Path '.\so-createcsv.csv' -Encoding ascii -NoTypeInformation -UseQuotes AsNeeded
If you must keep the code in a cmd.exe batch file script, this could be used. Use pwsh.exe for PowerShell Core 6+. Use powershell.exe for Windows PowerShell 5 or earlier.
pwsh.exe -NoLogo -NoProfile -Command ^
"New-Object psobject -Property ([ordered]#{""Date""=(Get-Date).ToString('dd/MM/yyyy');""Time""=(Get-Date).ToString('HH:mm:ss.ff');""Host""=$Env:COMPUTERNAME;""Name""=$Env:USERNAME}) |" ^
"Export-Csv -Path '.\so-createcsv.csv' -Encoding ascii -NoTypeInformation -UseQuotes AsNeeded"

CMD powershell command not allowing pipe

I am trying to get SQL to execute some powershell commands using xp_cmdshell which has been working, however I'm running into an unusual problem. When I try to use a pipeline, it doesn't recognize the command after the pipeline. I tried this from the standard cmd line and can confirm that the same issue happens. This is the command I'm using:
powershell.exe -command get-eventlog -Newest 10 -LogName Application -Before "2018-04-18T22:02:23" -After "2018-04-17T22:02:23" -computername dk01sv1115 | Select Message
When I use the command without using | Select Message at the end, it works without issue. The issue is I'm not getting the full event message I've tried using Select and Format functions to try to get the full details but the pipe appears to be the issue. If you run the same command after starting powershell (IE run powershell.exe then run the command) it works without issue, however when you use SQL to run powershell.exe as a seperate line in SQL it runs indefinitely. EXAMPLE SQL:
Declare #command nvarchar(1000),#computername nvarchar(1000)
Set #computername = 'test'
Set #command = 'powershell.exe
get-eventlog -Newest 10 -LogName Application -Before "' + REPLACE(Convert(VARCHAR(255),GETDATE(),120),' ','T') +'" -After "' + REPLACE(Convert(varchar(255),DateAdd(dd,-1,GETDATE()),120),' ','T') + '" -computername ' + #computername + '
exit'
exec xp_cmdshell #command
The | Select Message part is interpreted by cmd.exe, not PowerShell, because the pipe symbol (|) is special in cmd.exe as well (with roughly the same meaning), and you haven't enclosed it in "...".
The best approach to calling PowerShell from cmd.exe is to pass the entire PowerShell command as a single, double-quoted string ("...") to the -Command parameter:
powershell.exe -command "get-eventlog -Newest 10 -LogName Application -Before 2018-04-18T22:02:23 -After 2018-04-17T22:02:23 -computername dk01sv1115 | Select Message"
Tips regarding embedded quoting:
To quote literals, you can use '...' inside the overall "..." string.
Note that the values that were quoted in your original command didn't actually need quoting (e.g., "2018-04-18T22:02:23"), so I used them unquoted in my reformulation.
If you need to embed " chars., use \" (sic - even though PowerShell-internally it is ` that serves as the escape character).
If you use the "PowerShell -command" form then place the complex command in quotes or curly braces.
Update:
Adding the ^ escape character works through Invoke-SQLCommand:
Invoke-Sqlcmd -ServerInstance ECS-DCTS05 -Database Test -Query "xp_cmdshell 'powershell -command dir c:\ ^| format-list'"
This works from various places starting cmd.exe:
powershell -noprofile -command "get-childitem | format-list" # quoted
This does NOT use the format-list:
powershell -noprofile -command get-childitem | format-list # no quotes/braces
ADDED:
Actually the following commands all work as expected on my Win7 PowerShell 5.1 machine:
Start search or WinKey+Run:
cmd /k powershell -noprofile -command "get-childitem | format-table"
OR from a cmd prompt that is open:
powershell -noprofile -command "get-childitem | format-list"
powershell -noprofile -command "get-childitem | format-table"
In each case the format command words as expected.
If you are having trouble with quoting then the CMD escape OUTSIDE of quotes is ^, so sometimes prefixing the pipe with ^ will help in Cmd.exe (e.g., in cmd.exe for /f ...in ("command here with ^| pipe") do ... statements.
So does:
powershell -noprofile -command "get-childitem | ForEach-Object FullName"
Which proves that PowerShell is running the 2nd element of the pipe.
EDITED: This also works but as a comment mentioned only from within PowerShell since CMD doesn't understand the curly braces:
powershell -noprofile -command { get-childitem | format-list } # curly braces added

Using PowerShell v5, search for specific folder(s) copy those folders/files, AND the paths to another drive

I'm new to PowerShell. Spent several days trying to get this, think I am very close, but need an expert to show me the final step.
Using PowerShell v5, Need to search for folder(s) which match specific name, then copy those folders and their files, AND the path to the Folders to another drive.
Script now:
Get-ChildItem s:\ -Filter (Read-Host -Prompt "Enter") -Recurse -ErrorAction SilentlyContinue -Force |
Select-Object FullName | ForEach-Object {Copy-Item -Path $_.FullName -Destination 'C:\Robotics\AA\ConfigFiles\Automation Anywhere Files\Automation Anywhere\My Tasks\' -recurse -Force}
This does a search for the Folder, returns results and then copies the folder and contents into the destination location.
The problem is, I actually needed the source path appended to my destination path. Source is variable number of folders deep.
Any suggestions?
To limit the Get-ChildItem to folders I inserted -Directory
Copy-Item accepts input from the pipeline, no ForEach-Object necessary.
the -replace is RegEx based and requires the backslash in the path
S:\ to be escaped by another backslash S:\\.
$DestBase = 'C:\Robotics\AA\ConfigFiles\Automation Anywhere Files\Automation Anywhere\My Tasks\'
$Search = Read-Host -Prompt "Enter folder:"
Get-ChildItem -Path S:\ -Filter $Search -Directory -Recurse -ErrorAction SilentlyContinue -Force |
Copy-Item -Destination {$($_.Fullname) -replace 'S:\\',"$DestBase"} -Recurse -Force -Whatif
If the output looks OK, remove the -WhatIf paramter in the last line.

Delete files with name starting with X older than Y days

I'm trying to delete some files where their name start's with "Archive-Security*" and older than 5 days.
I've used the following cmd but it returns "ERROR: No files found with the specified search criteria."
forfiles -m "Archive-Security-*" -d -5 -c "cmd /c del #path"
I am sure you have either solved this or it is no longer a problem, but here would be a way to accomplish the task. This can be used in a .bat file script.
powershell -NoProfile -Command ^
"Get-ChildItem -File -Filter 'Archive-Security-*' |" ^
"ForEach-Object {" ^
"if ((($(Get-Date) - $_.LastAccessTime).Days) -gt 5) {" ^
"Remove-Item -Path $_.FullName -WhatIf" ^
"}" ^
"}"
Of course, using PowerShell directly would be easier.
Get-ChildItem -File -Filter 'Archive-Security-*' |
ForEach-Object {
if ((($(Get-Date) - $_.LastAccessTime).Days) -gt 5) {
Remove-Item -Path $_.FullName -WhatIf
}
}

Renaming files in bulk in PowerShell by attaching current date

I searched Stack Overflow and most of the answers I found, are not what I am trying to do. I have multiple files in the directory, that I am trying to rename by attaching the current date and time as a suffix before the extension.
I have done it multiple times on a single file, but cannot get it working for the bulk of files. Here is the code I am using:
Get-ChildItem $Path -Filter "*.dat" -Recurse | Rename-Item -NewName {$_.Basename + '_' + $curDateTime + $_.Extension }
It does not fail, but files are not renamed.
This works for me:
$curDateTime = Get-Date -Format yyyyMMdd-HHmmss
Get-ChildItem $Path *.dat -Recurse |
Rename-Item -NewName {$_.Basename + '_' + $curDateTime + $_.Extension } -WhatIf
Another solution:
Get-ChildItem $Path -Filter "*.dat" -Recurse |
ren -New {$_.Name -replace '[.](?!.*?[.])',"_${curDateTime}."} -What

Resources