How do I run a piped powershell command from command line? - file

I am trying to run the following powershell command from the windows 7 command line:
powershell ls 'C:/path to file/' | ForEach-Object {$_.LastWriteTime=Get-Date}
I have encountered several errors. When I run the above command, I get the error:
'ForEach-Object' is not recognized as an internal or external command,
operable program or batch file.
I changed the command to:
powershell ls 'C:/My Programs/CPU Analysis/data/test/' | powershell ForEach-Object {$_.LastWriteTime=Get-Date}
Now I am getting the error:
Property 'LastWriteTime' cannot be found on this object; make sure it exists
and is settable.
At line:1 char:17
+ ForEach-Object {$_.LastWriteTime=Get-Date}
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : PropertyNotFound
How can I modify this command to work from the command line?
Update
Both solutions are basically saying the same thing, but #Trevor Sullivan has a clearer answer.

cmd.exe doesn't understand foreach object. Plus, you're trying to split execution across two separate PowerShell processes, which is not going to work in this scenario.
You'll need to run the whole command in PowerShell.
powershell "ls 'C:/My Programs/CPU Analysis/data/test/' | ForEach-Object {$_.LastWriteTime = Get-Date}"

I'm not sure what are you trying to achieve..but if you are after files and their last modified time then use this:
powershell "ls 'C:\path' | ft name,LastWriteTime"
All you have to do is enclose your command in double quotes ".

Related

Can't run a Powershell command in a SQL Server Agent Job Step

I tried with "Powershell" option and commands (both don't work):
Get-Item "*.csv" | Out-File "d:\tracking\$(get-date -f yyyyMMdd-hhmmss).txt"
"Get-Item """*.csv""" | Out-File """d:\tracking\$(get-date -f yyyyMMdd-hhmmss).txt""""
And tried with "CmdExec" option and command (it doesn't work):
powershell.exe -Command "Get-Item """*.csv""" | Out-File """d:\tracking\$(get-date -f yyyyMMdd-hhmmss).txt""""
But the last command runs ok on a separate cmd.exe window
and the following also runs ok inside the powershell:
Get-Item "*.csv" | Out-File "d:\tracking\$(get-date -f yyyyMMdd-hhmmss).txt"
They create a text file eg. "20220607-112233.txt" containing the directory listing of CSV files
However I can't get this command to work from within the Job Step
The step finishes with "Unable to run. Syntax error"
Ok, I found a solution:
I created a batch (.bat file) with the code inside block 2:
powershell.exe -Command "Get-Item """*.csv""" | Out-File """d:\tracking\$(get-date -f yyyyMMdd-HHmmss).txt""""
(I only changed the hh for HH, for 24 hours based time)
Then, I configured the Job Step to run the bat file (setting the option "cmdExec")
That's all
I don't know what was going on, directory permissions were ok, the command of code block #2 above even works if I execute it with xp_cmdshell

Issue with my powershell syntax to do an clean up

I am using below PowerShell code to delete the logs in database server path
powershell.exe -command & {
get-childitem -path "$(ESCAPE_SQUOTE(D:\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Log\))" -filter *_*_*_*.txt |
Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-30)} | remove-item -verbose
}
Its erroring out as in below
At line:1 char:25
+ powershell.exe -command & {get-childitem -path "$(ESCAPE_SQUOTE(D:\Microsoft SQL ...
+ ~
The ampersand (&) character is not allowed. The & operator is reserved for future use; wrap an ampersand in double
quotation marks ("&") to pass it as part of a string.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : AmpersandNotAllowed
I am new to PowerShell and thought adding "" to "&" would suffice but its erroring again
powershell.exe : ScriptBlock should only be specified as a value of the Command parameter.
At line:1 char:1
+ powershell.exe -command "&" {get-childitem -path "$(ESCAPE_SQUOTE(D:\Microsoft S ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [], ParameterBindingException
+ FullyQualifiedErrorId : IncorrectValueForCommandParameter
Added "" to the path but it doesn't help
powershell.exe -command & {get-childitem -path "$(ESCAPE_SQUOTE("D:\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Log\"))" -filter *_*_*_*.txt | Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-30)} | remove-item -verbose}
I need to call it from an SQL agent job under operating system command exec
Can any one help me with the syntax
Regards and Wishes
Eben
When passing commands to the PowerShell CLI's -Command / -c parameter:
If you're calling from outside PowerShell, omit & { ... } altogether, just specify ... - & { ... } is never needed and only creates overhead.
Additionally, if you're calling from cmd.exe (you're not), it's safest to double-quote ... ("..."), and to escape embedded double quotes as \" (sic).
From inside PowerShell, omit & (you do need { ... } to robustly pass the commands, but this approach only works when calling from inside PowerShell):
Your error message suggest that powershell.exe was indeed called from a PowerShell session.
However, the question is why you're calling another PowerShell instance - as a child process - given that you're already in a PowerShell session - you could just execute the statements inside { ... } directly.
Given your symptom, I'm wondering whether the exec command in your case actually already uses PowerShell rather than cmd.exe as the shell, in which case passing just the text inside { ... } from your question would be sufficient.

Powershell Applying SQL Patches Multiple Servers

I need to apply SQL Server Patches in more than 300 Servers, so, I've created code below and saved it as Apply_SQL_Patch.ps1.
I'm reading a txt file with all servers names and I'd like to connect to them, extract and apply Patch.
The issue is when I execute it, it connect to server, but it's not changing directory to D:\Software\Patch, resulting in an error on next lines:
$output = foreach ($cluster in GC "D:\Software\Patch\Servers_List.txt")
{
Enter-PSSession -ComputerName $cluster
cd D:\Software\Patch\
.\SQLServer2014-KB4037356-x64.exe /X:D:\Software\Patch
.\setup.exe /action=patch /instancename=SQL2014 /quiet /IAcceptSQLServerLicenseTerms
}
$output | Out-File -Append D:\Software\Patch\Patch_Result.txt
Error below:
.\SQLServer2014-KB4037356-x64.exe : The term
'.\SQLServer2014-KB4037356-x64.exe' is not recognized as the name of a
cmdlet, function, script file, or operable program. Check the spelling
of the name, or if a path was included, verify that the path is
correct and try again. At D:\software\patch\Apply_SQL_Patch.ps1:5
char:2
+ .\SQLServer2014-KB4037356-x64.exe /X:D:\Software\Patch
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (.\SQLServer2014-KB4037356-x64.exe:String) [],
CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
.\setup.exe : The term '.\setup.exe' is not recognized as the name of
a cmdlet, function, script file, or operable program. Check the
spelling of the name, or if a path was included, verify that the path
is correct and try again. At D:\software\patch\Apply_SQL_Patch.ps1:7
char:2
+ .\setup.exe /action=patch /instancename=SQL2014 /quiet /IAcceptSQLServerLicense ...
+ ~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (.\setup.exe:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Thanks for your help!
Enter-PSSession -ComputerName $cluster
cd D:\Software\Patch\
.\SQLServer2014-KB4037356-x64.exe /X:D:\Software\Patch
.\setup.exe /action=patch /instancename=SQL2014 /quiet /IAcceptSQLServerLicenseTerms
I don't think this is going to work like you think. You're creating a session and then executing three commands locally.
Try:
Invoke-Command -ComputerName $cluster -ScriptBlock {
cd D:\Software\Patch\
Start-Process -PSPath '.\SQLServer2014-KB4037356-x64.exe' -ArgumentList '/X:D:\Software\Patch' -Wait
.\setup.exe /action=patch /instancename=SQL2014 /quiet /IAcceptSQLServerLicenseTerms
}
I've replaced the patch extraction command with the one above because the command returns control to PowerShell immediately. You may need to do the same thing with setup.exe. I don't have an SQL 2014 instance to test on.

Powershell returns negative exit code while script results are correct

I have made the following PowerShell script:
Set-Location D:\folder1\folder2\folder3\folder4;
Get-ChildItem | Rename-Item -NewName {$_.BaseName.insert(19,'loadtime') + (Get-Date -Format HHmm) + $_.Extension};
The goal is to rename a file by adding loadtime at position 19 and a time stamp. While the results of running the scripts the following error message is returned:
Rename-Item : The input to the script block for parameter 'NewName' failed.
Exception calling "Insert" with "2" argument(s): "Specified argument was out
of the range of valid values.
Parameter name: startIndex"
At D:\folder1\folder2\folder3\folder4\folder5\RenameFile.ps1:2
char:38
+ Get-ChildItem | Rename-Item -NewName {$_.BaseName.insert(19,'loadtime') +
(Get-D ...
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (Archive:PSObject) [Rename-
Item], ParameterBindingException
+ FullyQualifiedErrorId :
ScriptBlockArgumentInvocationFailed,
Microsoft.PowerShell.Commands.RenameItemCommand
When I run the script from my SSIS package I get the following error:
[Execute Process Task] Error: In Executing "PowerShell.exe" "-F D:\folder1\folder2\folder3\folder4\folder5\exec RenameFile.ps1" at "", The process exit code was "-196608" while the expected was "0".
I have tried to search for this error message but I could not find a page talking about this specific exit code.
The problem is though that while running the script commands from a PowerShell window I would still get the error message but the filename does change as expected. However since an error is returned the Execute Process Task in SSIS will fail.
My question is, what is the cause of this error? I assume it has to do with:
{$_.BaseName.insert(19,'loadtime') + (Get-Date -Format HHmm) + $_.Extension};
Wherein $.BaseName.insert(19,'loadtime') and (Get-Date -Format HHmm) and + $.Extension are seen as separate arguments.
My knowledge of PowerShell is not too great so this is just speculation on my part.
What would help me get rid of the error?
Thanks in advance!
As per the comments, the issue was caused by trying to insert into non-existing part of a string. This will raise an exception.
As a solution, make sure the indexed location exists, or just concatenate at the end. Like so,
$_.basename + 'loadtime' + (get-date -format hhMM) + $_.extension
The weird error code -196608 is actually a result of an error code represented as decimal (base 10) integer instead of hex value (base 16). Consider this:
[int]$i = -196608
$i.ToString('x')
fffd0000
What happens here is that the real error code is, in hex format, 0xFFFD0000. Because of Two's Compliment, large enough hex values actually represent negative decimal numbers.
As for this particular error code, it pops up every here and there without proper documentation. Should I hazard a guess, it has something to do with the fact that Powershell itself works fine, but the script it was told to run didn't.

Powershell: call an exe with parameters from items in an array?

I have a set of commands that I'd like to run and do some returncode checks against the result, so i figured it would be easy to put them into an array for execution.
Let's take this one as an example:
C:\windows\system32\inetsrv\AppCmd.exe set config "Default Web Site/" /section system.webServer/webdav/authoring /enabled:true /commit:apphost
Now, when I put it into my array without any quotes it's immediately interpreted as a command, the command is executed and the result is written into the array:
$commands = #()
$commands += C:\windows\system32\inetsrv\AppCmd.exe set config "Default Web Site/" /section system.webServer/webdav/authoring /enabled:true /commit:apphost
When I use quotes to put it into the array as a string, trying to execute it doesn't work.
PS> $commands = #()
PS> $commands += "C:\windows\system32\inetsrv\AppCmd.exe set config ""Default Web Site/"" /sectio n:system.webServer/webdav/authoring /enabled:true /commit:apphost"
PS> $commands[0]
C:\windows\system32\inetsrv\AppCmd.exe set config "Default Web Site/" /section:system.webServer/webdav/authoring /enabled:true /commit:apphost
PS> & $commands[0]
The term 'C:\windows\system32\inetsrv\AppCmd.exe set config "Default Web Site/" /section:system.webServer/webdav/authoring /enabled:true /commit:apphost' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:2
+ & <<<< $commands[0]
+ CategoryInfo : ObjectNotFound: (C:\windows\syst.../commit:apphost:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
So, how do I correctly put this command into an array, queue or whatever fits best so I can execute it when the time is right?
When you use the call operator & the string following it must name only the command to be executed - not any parameters. With what you have configured you can use Invoke-Expression instead e.g.:
iex $command[0]
One warning about Invoke-Expression, be careful using this command if any input comes from the user as it can be used to inject unwanted commands e.g.:
Read-Host "Enter commit value"
Enter commit value: apphost; remove-item c:\ -r -force -ea 0 -confirm:0 #
Well, i'm a year late to a party, but you can also do this:
new-alias -name Monkey -value "$env:windir\system32\inetsrv\APPCMD.exe"
Even later: I think this answer and this other answer by user Roman Kuzmin closely address this issue, by storing executable name in a variable and all its parameters in an array.

Resources