extract value between two square braces - powershell

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]
...

Related

Powershell. New-ScheduledTaskAction. The Count parameter recognised in one statement but not another

I'm trying to schedule a Powershell task, part of which tests for internet connectivity at regular intervals and sends the output to a file. The 'Count' parameter for the 'Test-Connection' cmdlet is being accepted as a standalone statement, but when transplanted into a New-ScheduledTaskAction and assigned to a variable the 'Count' parameter is not recognised.
This works:
Test-Connection 8.8.8.8 -Count 2 -Quiet |
ForEach-Object { "$(Get-Date -Format "[yyyy-MM-dd HH:mm:ss]") $_" } |
Out-File -Append -FilePath C:\Users\xxxxxx\Desktop\internet-log.txt
But when transferred to:
$InternetConnectionLog=New-ScheduledTaskAction
-Execute "powershell.exe"
-Argument Test-Connection 8.8.8.8 -Count 2 -Quiet |
ForEach-Object { "$(Get-Date -Format "[yyyy-MM-dd HH:mm:ss]") $_" } |
Out-File -Append -FilePath C:\Users\xxxxxx\Desktop\internet-log.txt
I'm presented with:
New-ScheduledTaskAction : A parameter cannot be found that matches parameter name 'Count'.
At line:1 char:108
+ ... ute "powershell.exe" -Argument Test-Connection 8.8.8.8 -Count 2 -Quie ...
+ ~~~~~~
+ CategoryInfo : InvalidArgument: (:) [New-ScheduledTaskAction], ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,New-ScheduledTaskAction
The resulting error message points specifically to the 'Count' parameter being the problem. What precisely is preventing the same parameter from being recognised?
The -Argument parameter of New-ScheduledTaskAction expects a string to be passed to it. Enclosing your parameter value in quotes makes PowerShell interpret it as a string.
$InternetConnectionLog = New-ScheduledTaskAction -Execute "powershell.exe" -Argument 'Test-Connection 8.8.8.8 -Count 2 -Quiet |
ForEach-Object {
"$(Get-Date -Format "[yyyy-MM-dd HH:mm:ss]") $_"
} | Out-File -Append -FilePath C:\Users\xxxxxx\Desktop\internet-log.txt'
The reason for single quotes over double quotes here is to preserve the string as is, which is called a literal string. Everything within single quotes tells PowerShell to not perform variable expansion. You can use double quotes, which will require escaping PowerShell's special characters that you want to literally pass like $ and " in your case. Another alternative is to store your argument value in a variable as a string (still using single quotes). Then just pass the variable to the -Argument parameter.
Through simpler examples, you can see the described behavior:
$a = "I am a string"
$a.gettype().name
String
$b = "What am I? $a"
$b
What am I? I am a string
$c = 'What am I? $a'
$c
What am I? $a
$d = 'I want to keep my "double quotes" and $dollars$.'
$d
I want to keep my "double quotes" and $dollars$.
See About Quoting Rules for further explanation about string expansion and using both types of quotes.
The answer from AdminOfThings eliminated the error message. I was able to to register the statement as a scheduled task.

I am looking to append windows host file get current dynamic ip and map it to a host name

I am looking to append windows host file get current dynamic ip and map it to a host name irrespective of current ip address.
i am getting below error
===============================================
Add-Content : A positional parameter cannot be found that accepts argument 'hostname1'.
At C:\Users\Opps\Desktop\power\New Text Document.ps1:6 char:3
+ {ac -Encoding UTF8 -value "$($env:windir)\system32\Drivers\etc\hosts ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Add-Content], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.AddContentCommand
================================================================================
Script :
$ip=get-WmiObject Win32_NetworkAdapterConfiguration|Where {$_.Ipaddress.length -gt 1}
$ip.ipaddress[0]
$hst = $env:COMPUTERNAME
Set-ExecutionPolicy -ExecutionPolicy Unrestricted If ((Get-Content "$($env:windir)\system32\Drivers\etc\hosts" ) -notcontains "127.0.0.2 hostname1")
{ac -Encoding UTF8 "$($env:windir)\system32\Drivers\etc\hosts" ($ip.ipaddress[0]) ($hst) }
Add-Content is expecting a string as a value hence to change the type we need to encapsulate the value in quotes. To access an objects property e.g. $ip.ipaddress[0] while in quotes, in order for the text to not be treated literally, we must wrap it in brackets with a preceding dollar sign "$(...)" officially known as a subexpression operator (see mklement0's explanaton) . To ensure we are not duplicating an entry we run a quick check for the entry with the if statement only proceeding to add-content if both conditions of the if statement are met
$ip = get-WmiObject Win32_NetworkAdapterConfiguration|Where {$_.Ipaddress.length -gt 1}
$ip.ipaddress[0]
$hst = $env:COMPUTERNAME
$hostfile = Get-Content "$($env:windir)\system32\Drivers\etc\hosts"
Set-ExecutionPolicy -ExecutionPolicy Unrestricted
if ($hostfile -notcontains "127.0.0.2 hostname1" -and
(-not($hostfile -like "$($ip.ipaddress[0]) $hst"))) {
Add-Content -Encoding UTF8 "$($env:windir)\system32\Drivers\etc\hosts" "$($ip.ipaddress[0]) $hst"
}

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}
}

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).

How to pipe all output of .exe execution in 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