CMD powershell command not allowing pipe - sql-server

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

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"

How do I have to escape the quotation marks in this batch code?

How do I have to escape the quotation marks in this batch code?
I have the following code:
echo ""powershell -Executionpolicy Bypass -Command "get-clipboard > C:\File.txt""" > C:\Helptool.cmd
This code should generate the file "Helptool.cmd". The content should be as follows:
powershell -Executionpolicy Bypass -Command "get-clipboard > C:\File.txt
Where do I have to put ^ characters in the first batch code?
When I run the above code, the contents of the newly created batch file are incorrectly as follows:
"" powershell -Executionpolicy Bypass -Command "get-clipboard> C: \ File.txt" ""
The two quotes at the beginning and the three quotes at the end are undesirable. I'll get rid of them by escaping. or? Just how do I go about this?
You do not need any carets, ^, if you doublequote the entire PowerShell -Command:
This version uses the backslash to escape the nested doublequotes:
#Echo #"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -Command "Get-Clipboard -Format Text -Raw -TextFormatType UnicodeText > \"C:\File.txt\"" > "C:\Helptool.cmd"
If you prefer to use singlequotes in PowerShell, there's nothing to escape:
#Echo #"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -Command "Get-Clipboard -Format Text -Raw -TextFormatType UnicodeText > 'C:\File.txt'" > "C:\Helptool.cmd"
Please note, that I have removed your -ExecutionPolicy parameter, as it is not needed for running -Commands, (only PowerShell -Files). The full path to, and extension for, the powershell executable, are optional, but recommended, as is the -NoProfile parameter. You should open a Windows PowerShell window, and read the output from Get-Help Get-Clipboard -Full, to find out what the -Format, Raw, and -TextFormatType parameters do.

create a file using windows CMD and fill it with a repeated line but different value

I would like to update wireshark decode-as file with all RTP range using a CMD command or a Batch script.
The file must contain values like this:
decode_as_entry: udp.port,16384,(none),RTP
decode_as_entry: udp.port,16386,(none),RTP
decode_as_entry: udp.port,16388,(none),RTP
.
.
.
decode_as_entry: udp.port,32766,(none),RTP
As you can see, it is cumbersome to add the lines for all the RTP range manually.
So the batch script must create a text file, and add these lines one by one until value 32766 is reached.
I understand the logic of the code, that it should contain a FOR loop (for iteration), and an IF statement (to break the loop until 32766 is reached), but I am not able to build the batch file successfully as I lack the knowledge.
The comment by Compo using FOR /L works. Another way to do it using PowerShell from a cmd.exe .bat file script would be:
powershell.exe -NoLogo -NoProfile -Command ^
"(16384..32766).Where({$_ %% 2 -eq 0}) |" ^
" ForEach-Object { """decode_as_entry: udp.port,$($_.ToString()),(none),RTP""" } |" ^
" Out-File -FilePath '.\test.txt' -Encoding ASCII"
Of course, it is easier if you could run a PowerShell console or .ps1 script file.
(16384..32766).Where({$_ % 2 -eq 0}) |
ForEach-Object { "decode_as_entry: udp.port,$($_.ToString()),(none),RTP" } |
Out-File -FilePath '.\test.txt' -Encoding ASCII

A positional parameter cannot be found that accepts argument

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"

unable to execute PowerShell script with windows message via batch file

I have a simple PowerShell script that using windows.forms for presenting and getting data using GUI.
It works when I run it using PowerShell , but doesn't work via CMD.
Here is the .ps1 example :
[System.Windows.MessageBox]::Show('message','Step 1','YesNoCancel','Question')
batch file for executing :
powershell "&{start-process powershell -ArgumentList ' -noprofile -file c:\temp\gui.ps1' -verb RunAs} exit $LASTEXITCODE" < NUL
Th error I get is : Unable to find type [System.Windows.MessageBox].
I understood that the CMD running with different context and the assembly need to be loaded , so I tried to load it from the CMD command , but still same error .
powershell "&{[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); start-process powershell -ArgumentList ' -noprofile -file c:\temp\gui.ps1' -verb RunAs} exit $LASTEXITCODE" < NUL
Error:
Any idea ?
You could try something like this:
PowerShell -NoProfile -Command "& {Start-Process PowerShell -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File ""C:\temp\gui.ps1""' -Verb RunAs}"; Exit $LastExitCode
this is completely untested
OK I figure it out.
The issue was with the PowerShell script,
I used a [System.Windows.MessageBox] instead a [System.Windows.Forms.MessageBox]
Before:
[System.Windows.MessageBox]::Show('message','Step 1','YesNoCancel','Question')
After:
[System.Windows.Forms.MessageBox]::Show('message','Step 1','YesNoCancel','Question')
It works without changing the batch file.
Thanks,
Try running the following in the batch file:
powershell -command [System.Windows.Forms.MessageBox]::Show('message','Step 1','YesNoCancel','Question')
I hope this is helpful, if you have any other questions, please comment below.

Resources