How to pipe all output of .exe execution in Powershell? - powershell

In Powershell I am running psftp.exe which is PuTTy's homepage. I am doing this:
$cmd = "psftp.exe"
$args = '"username#ssh"#ftp.domain.com -b psftp.txt';
$output = & $cmd $args
This works; and I am printing out $output. But it only catches some output in that variable (like "Remote working directory is [...]") and is throwing other output to an error type like this:
psftp.exe : Using username "username#ssh".
At C:\full_script.ps1:37 char:20
+ $output = & <<<< $cmd $args
+ CategoryInfo : NotSpecified: (Using username "username#ssh".:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
This "Using username ..." etc looks like a normal FTP message. How can I make sure all output gets put into $output?

The problem is some output is being sent to STDERR and redirection works differently in PowerShell than in CMD.EXE.
How to redirect output of console program to a file in PowerShell has a good description of the problem and a clever workaround.
Basically, call CMD with your executable as a parameter. Like this:
UPDATE
I fixed my code so it would actually work. :)
$args = '"username#ssh"#ftp.domain.com -b psftp.txt';
$output = cmd /c psftp.exe $args 2`>`&1

Give this a try
$output = [string] (& psftp.exe 'username#ssh#ftp.domain.com' -b psftp.txt 2>&1)
There is a PowerShell bug about 2>&1 making error records. The [string] cast works around it.

& "my.exe" | Out-Null #go nowhere
& "my.exe" | Out-Default # go to default destination (e.g. console)
& "my.exe" | Out-String # return a string
the piping will return it in real-time
& "my.exe" | %{
if ($_ -match 'OK')
{ Write-Host $_ -f Green }
else if ($_ -match 'FAIL|ERROR')
{ Write-Host $_ -f Red }
else
{ Write-Host $_ }
}
Note:
If the executed program returns anything other than a 0 exitcode, the piping will not work. You can force it to pipe with redirection operators such as 2>&1
& "my.exe" 2>&1 | Out-String
sources:
https://stackoverflow.com/a/7272390/254276
https://social.technet.microsoft.com/forums/windowsserver/en-US/b6691fba-0e92-4e9d-aec2-47f3d5a17419/start-process-and-redirect-output-to-powershell-window

Related

extract value between two square braces

I need to extract the value between two square braces from a specific line in a command output. Here's the output of the command.
C:\Informatica\PowerCenter\isp\bin> .\infacmd.bat ping -dn Infadomain -nn Node01
[INFACMD_10052] Node [Node01] Domain [Infadomain] Host:Port [infadev:6005] was successfully pinged.
[INFACMD_10470] Kerberos authentication is [disabled] and secure communication is [disabled] in the Informatica domain [Infadomain].
Command ran successfully.
From the above output, I need to extract the value 'infadev' from the above command result. I tried the regex functions to extract the value but somehow the code does not work.
$cmd = Invoke-Command -ScriptBlock {& cmd.exe /c "infacmd.bat ping" -dn "Infadomain" -nn "Node01"} | Where-Object {$_ -ne 'Command ran successfully.'}
$result = $cmd |(\[(?:\[??[^\[]*?\]))
write-host $result
At line:2 char:17 + $result = $cmd |([(?:[??[^[]*?])) + ~~~~~~~~~~~~~~~~~~~~~ Expressions are only allowed as the first element of a pipeline. + CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException + FullyQualifiedErrorId : ExpressionsMustBeFirstInPipeline
invoke-command -ScriptBlock {& cmd.exe /c "infacmd.bat ping" -dn "InfaDomain" -nn "Node01"} | Where-Object {$_ -ne 'Command ran successfully.'}
this is another take on the idea. [grin] it uses a named capture group to grab the 1st set of bracketed data after the Host:Port text.
$Cmd = #'
[INFACMD_10052] Node [Node01] Domain [Infadomain] Host:Port [infadev:6005] was successfully pinged.
[INFACMD_10470] Kerberos authentication is [disabled] and secure communication is [disabled] in the Informatica domain [Infadomain].
Command ran successfully.
'# -split [environment]::NewLine
$Null = $Cmd.ForEach({$_ -match 'Host:Port \[(?<HostPortInfo>.+)\]'})
$Matches.HostPortInfo
output ...
infadev:6005
You can use Where -match to get array of results, than use $Matches[i].
$cmd = Invoke-Command -ScriptBlock {& cmd.exe /c "infacmd.bat ping" -dn "Infadomain" -nn "Node01"} | Where-Object {$_ -ne 'Command ran successfully.'}
$cmd | Where {$_ -match "(\[(?:\[??[^\[]*?\]))" }
$result0 = $Matches[0]
$result1 = $Matches[1]
...

Capture cmd error to file in CURRENT directory?

I have a PowerShell script that restarts servers listed in a file.
foreach ($server in $servers) {
try {
cmd /c "shutdown.exe /r /f /m \\$server /t 0 /d p:0:0 /c 'PlannedRestart'"
"$server`tRestarted"
"$server`tRestarted" | Out-File -FilePath .\RestartServers_LOG.txt -Append
} catch {
"$server`t" + cmd /c ">&1"
"$server`t" + cmd /c "dir > .\RestartServers_LOG.txt 2>&1"
}
}
I am trying to catch any errors that may occur from the CMD to output on the PowerShell session as well as to a file in the CURRENT directory, so in PowerShell that would be .\ but I don't know what the current directory specification is for CMD. Unless its the same thing?
Essentially, I am hoping to accomplish something similar to how we can capture the exception message of PowerShell like this:
"$server`t" + $_.Exception.Message.ToString().Split(":")[1].Replace("`n","") |
Out-File -FilePath .\RestartServers_LOG.txt -Append
but CMD doesn't deal with exceptions like PowerShell, and instead STDERR.
Note: I am using the command shutdown.exe because PowerShell doesn't have ability to restart "planned" or "unplanned" unfortunately.
Just do it the PoSh way:
$params = '/r', '/f',
'/t', '0',
'/d', 'p:0:0',
'/c', 'PlannedRestart'
$servers | ForEach-Object {
$output = & shutdown.exe /m "\\${_}" #params 2>&1
if ($LastExitCode -eq 0) {
"{0}`tRestarted" -f $_
} else {
"{0}`tRestart failed:`t{1}" -f $_, $output
}
} | Set-Content '.\RestartServers_LOG.txt'
External commands don't throw exceptions in the first place, so your try..catch wouldn't do anything anyway.

Desired State Configuration can't get hash table from Script resource Get block

I have tested this using the built in Script resource from 1.1 as well as xScript 5.1.0.0 and get the same results. My Set and Test blocks work fine. I'm using several other script resources that are very similar and they work fine as well for the get block.
I've tried a lot of variations in syntax but it always comes back the same. I know the block is running because I commented out the line where the file which gets created gets removed and I see the file. I also ran this as a function in powershell and piped the output to Get-Member and can see it is indeed a hastable that is returned.
On a side note, I really don't like the method I'm using here to manage this setting through DSC. I'm open to other ideas as long as it is still within DSC.
Script StorePasswordsUsingReversibleEncyption
{
SetScript = {
secedit /export /cfg c:\temp\secpol.cfg
(gc C:\temp\secpol.cfg).replace("ClearTextPassword = 1", "ClearTextPassword = 0") | Out-File C:\temp\secpol.cfg
secedit /configure /db c:\windows\security\local.sdb /cfg c:\temp\secpol.cfg /areas SECURITYPOLICY /quiet
rm -force c:\temp\secpol.cfg -confirm:$false
}
TestScript = {
secedit /export /cfg c:\temp\secpol.cfg
$str = (Get-Content 'c:\temp\secpol.cfg' | select-String 'ClearTextPassword' -SimpleMatch).ToString()
rm -force c:\temp\secpol.cfg -confirm:$false
if ($str -eq 'ClearTextPassword = 0') {return $true}
else {return $false}
}
# Not working yet
GetScript = {
secedit /export /cfg c:\temp\secpol.cfg
$str = (Get-Content 'c:\temp\secpol.cfg' | select-String 'ClearTextPassword' -SimpleMatch).ToString()
rm -force c:\temp\secpol.cfg -confirm:$false
return #{Result = $str}
}
}
After I run Get-DSCConfiguration, the output returned in console is this:
Get-DscConfiguration : PowerShell DSC resource MSFT_ScriptResource failed to execute Get-TargetResource functionality
with error message: Failure to get the results from the script in a hash table format.
At line:1 char:1
+ Get-DscConfiguration
+ ~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (MSFT_DSCLocalConfigurationManager:root/Microsoft/...gurationManager)
[Get-DscConfiguration], CimException
+ FullyQualifiedErrorId : ProviderOperationExecutionFailure,Get-DscConfiguration
Try this:
GetScript = {
$null = secedit /export /cfg c:\temp\secpol.cfg
$str = (Get-Content 'c:\temp\secpol.cfg' | select-String 'ClearTextPassword' -SimpleMatch).ToString()
rm -force c:\temp\secpol.cfg -confirm:$false
return #{Result = $str}
}
The problem is that when you call an external command (like secedit), everything it writes to stdout is returned as output if this command (and that's pretty natural). But if don't catch it into a variable, it will be passed further to the output of your script block. The return statement is also a little misleading - it doesn't mean "return this thing only", but "write this thing to output stream, then return".
This means that your original GetScript doesn't return a single hashtable, but rather an arraythat looks like this:
#(
"some-output-from-secedit",
#{ Result = $str }
)
Assigning the output from external commands to a variable (I used $null in this case to denote I want to discard it) will prevent it from cluttering your script block's output.
Another way would be to redirect command's output to Write-Verbose (if you're interested in reading it) or to $null (if you don't care):
secedit /export /cfg c:\temp\secpol.cfg | write-verbose
can you try modifying your getscript block like this:
GetScript = {
start-process secedit -ArgumentList '/export /cfg c:\temp\secpol.cfg' -Wait
$str = (Get-Content 'c:\temp\secpol.cfg' | select-String 'ClearTextPassword' -SimpleMatch).ToString()
rm -force c:\temp\secpol.cfg -confirm:$false
return #{Result = $str}
}

Calling bteq from Powershell

I currently generate a bat file that contains multiple BTEQ calls to execute a .btq script against a Teradata box, below is an example CMD call:
bteq <Bteq\"File_1.BTQ" >>bteq_output.txt 2>&1
The syntax as far as I understand it is:
> specifies the input file & >> specifies the output file
I am currently trying to convert the bat implementation to a PowerShell version however I get stuck with the following issue:
PS C:\...\Deploy.ps1:21 char:81
+ ... -Object { bteq < (Join-Path $deployDir $_) >> bteq_log.txt }
+ ~
The '<' operator is reserved for future use.
+ CategoryInfo : ParserError: (:) [],ParentContainsErrorRecordException
+ FullyQualifiedErrorId : RedirectionNotSupported
Which is the result of this call in my PowerShell script:
(Get-Content -Path $configFile) | Select-Object -Skip 1 | ForEach-Object { bteq.exe < (Join-Path $deployDir $_) >> bteq_output.txt }
It seems that the BTEQ command line syntax directly conflicts with the < operator in PowerShell.
EDIT
If I try to escape the < with ` then instead I am presented with the following error, as though the parameter is not picked up:
bteq.exe : *** Warning: You must log on before sending SQL requests.
At C:\...\Deploy.ps1:20 char:76
+ (Get-Content -Path $configFile) | Select-Object -Skip 1 | ForEach-Object { bteq. ...
+ ~~~~~
+ CategoryInfo : NotSpecified: ( *** Warning: Y... SQL requests. :String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
Anyone able to shed any light as to how one would call a bteq command in PowerShell when specifying the file to be used?
in powershell
try cat <your-bteq-file> | bteq to call your bteq file
You should be able to wrap the portion in your foreach into a script block and PowerShell will treat it as one command. You would just need to begin with the & symbol.
foreach { & {bteq.exe < (Join-Path $deployDir $_) >> bteq_output.txt} }
Not being at a machine to test this you might have to dynamically build the script block as a string so your variable is resolved correctly from the loop.
Prior to seeing the answer posted by Shailesh, I managed to get it working via a call to cmd as follows:
(Get-Content -Path $configFile)[1..$fileCount] | ForEach-Object {
cmd.exe /C ([String]::Concat(" bteq <", $deployDir, "`\""" , $_ , """ >>output.txt 2>&1"))
if($LASTEXITCODE -ne 0){
Write-Host "Error Code" $LASTEXITCODE
break
} else {
Write-Host "Completed" $_ ": Return code" $LASTEXITCODE
}
};
Here this set of statement iterates over the text read from the $configFile variable, and then makes a call to bteq for each one breaking if an error code is not equal to 0.

How can I pipe from Get-Content -Wait?

I would like to pipe the output of Get-Content $file -Wait to a custom PowerShell script. The script looks like this.
$lines = ($input | Out-String) -replace "`r", "" -split "`n"
foreach ($line in $lines) {
#Process $line
Write-Host $line
}
Basically the idea is to take the input, format it nicely and then process the output before it gets printed to the console.
The problem is nothing is getting sent to my script when I call it like cat $file -Wait | MyScript. If I do cat $file -Wait or cat $file | MyScript, everything works as expected. But combining the pipe and the wait parameter doesn't work.
Is there some syntax I need to use to allow processing the -Wait parameter? I tried using Out-String -Stream, but that doesn't work either.
The problem is with $input.
If you do this :
Get-Content $file -Wait | Get-Member -InputObject $input
Or
Get-Content $file -Wait | Get-Member -InputObject $_
You will get :
Get-Member : The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeline input.
If Get-Member is unable to read the object going through the pipeline, you know that something is very wrong with the object (or the pipelining).
Let's try piping $input to Out-String, like you are doing in your script :
Get-Content $file -Wait | Out-String $input
You will get :
Out-String : A positional parameter cannot be found that accepts argument 'System.Collections.ArrayList+ArrayListEnumeratorSimple'.
At line:1 char:52
+ get-content '.\netstat anob.txt' -wait | Out-String <<<< $input
+ CategoryInfo : InvalidArgument: (:) [Out-String], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.OutStringCommand
So, indeed, "Get-Content" -Wait gives you a weird kind of object : a System.Collections.ArrayList+ArrayListEnumeratorSimple .
It looks like it's the result of the GetEnumerator() method from a System.Collections.ArrayList object, or something like that.
Given the fact that Get-Member or even "Get-Member -Force" is unable to read this kind of "Object", from Powershell's point of view, it's not a object.
The workaround would be to drop the -Wait parameter from Get-Content and find another way of achieving what you want, possibly by running Get-Content and then, running "Get-Content -Tail 1" several times in a loop.
This is possible if your script accepts pipeline input. You can see it as you have mentioned when you pipe to other cmdlets like Select-String. For example defining script.ps1 as:
process { Write-Host "line: $input" }
Then running
1..200 | foreach { add-content -Path test.txt -Value "$_"; start-sleep 1 }
in one PowerShell session and
gc test.txt -wait | .\script.ps1
in another, you can see that each line is piped to the script.
I don't see any way to do what you are asking. -Wait initiates a loop that never ends, the only way to stop is to manually kill it. Since it will always be stuck inside the loop anything you try to do after initiating the loop is never going to process.
The problem is in this line:
Write-Host $line
You should use Write-Output instead. Write-Output sends objects to pipeline, Write-Host directly to host (console).