Michele
Michele

Reputation: 3881

regex can't be first element for pipeline - how get match

I have a regular expression that is run at the windows 10 command line, and get the error:

Expressions are only allowed as the first element of a pipeline.

I'm not sure how to put this together with the match part in the first element.

This is the document content (with important parts shown...there are multiple Manufacturer and S/N's but I just need the specific one):

e:\temp\details.txt:

...
  Manufacturer: SGSS
  S/N: A791D734804

...

I successfully get the above two lines of code from the document with this powershell, but I'm having trouble extracting the serial number after the semicolon.

This works to extract the Manufacturer and S/N lines (Context part gets line after pattern match too):

powershell "Get-Content e:\temp\details.txt | select-String -Pattern SGSS -Context 0,1" 

However, I'm trying to return just the serial number (), and get the error above. This is where I get the error with the match/command:

powershell "Get-Content -Raw e:\temp\details.txt | select-String -Pattern SGSS -Context 0,1 | $_ -match '(?ms)(N:\s)(([a-zA-Z0-9])+)'"

I tested that the regular expression works in a regex tester:

(N:\s)(([a-zA-Z0-9])+)

I used this example for the match , but I can't put it first in the expression (like the example) because of the other things I'm doing. What do you recommend?

Update: Example document contents:

A00232473244B

USB xController
RootHub
Port[6] USB Reader
Manufacturer: Gen 
S/N: 0A0000002071



Intel(R) 8 Series/1234 Series 
RootHub
Port[1]Standard-USB-Hub


USB x Controller
RootHub
Port[1] abc Printer
Manufacturer: guess
S/N: A2622431

Port[2] SG
Manufacturer: SGSS
S/N: A791B634801

Update2:

I tried this regex in a regex tester and it's still only finding the first S/N line but not following ones or just the SGSS one I care about:

S\/N:\s([a-zA-Z0-9]+)[\n\r\S\s]*

Update3: Some things I tried that are returning nothing:

powershell -command Get-Content e:\temp\details.txt -Raw ^| Where{$_ -match '(?ms)SGSS[\r\n\s]*S\/N: (\S+)\b'} ^| ForEach-Object { $Matches[1]}

powershell -command Get-Content e:\temp\details.txt -Raw ^| Where{$_ -match '(?ms)SGSS[\r\n\s]*S\/N: (\S+)\b'} ^| ForEach-Object { echo $Matches[1]}

powershell -command Get-Content e:\temp\details.txt -Raw ^| Where{$_ -match '(?ms)SGSS[\n\r]S\/N:\s([\w]*)'} ^| ForEach-Object { echo $Matches[1]}

powershell -command Get-Content e:\temp\details.txt -Raw ^| select-string -Pattern 'SGSS[\n\r]S\/N:\s([\w]*)' ^| ForEach-Object { echo $Matches[1]}

powershell -command Get-Content e:\temp\devices.txt -Raw ^| Where{$_ -match '(?ms)SGSS[\r\n\s]*S/N: (\S+)\b'} ^| ForEach-Object { $Matches[1]}

Update4:

I tried this and it's still returning nothing, with no errors as well. It's possible the issue is newlines/spaces. I'm not sure.

powershell -Command Get-Content 'e:\temp\details.txt' ^| Foreach-Object { if($_ -match 'SGSS') {$Found=1}; if(($_ -match 'S/N:\s([a-zA-Z0-9]+)[\n\r\S\s]*') -and ($Found -eq 1)) {$_ -replace '^.*N:\s' }}

Update5:

I'm trying testing it at the command line of our device, which is windows 10, and tried putting it in a ps1 file to run. I'm getting errors or nothing with this (ran at the command line with c:\temp>powershell ./SG_SN.ps1):

powershell "Get-Content e:\temp\details.txt -Raw | select-String -Pattern 'SGSS[\n\r]S\/N:\s([\w]*)'  | ForEach-Object {echo $Matches[1]}"

result: Nothing returned, no error

powershell "Get-Content e:\temp\usbconnecteddevices.txt -Raw ^| select-String -Pattern 'SAWGRASS[\n\r]S\/N:\s([\w]*)'  ^| ForEach-Object {echo $Matches[1]}"

result: error:

Get-Content : A positional parameter cannot be found that accepts argument '^'.
At line:1 char:1
+ Get-Content e:\temp\details.txt -Raw ^| select-String -Pa ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Note, I don't want to test it in powershell ISE or at the PS prompt because we can't run it that way ultimately.

Update6: This seems to be working in PowershellISE. I'll test it in the command line next. It's based on something from my notes for the last year or more.

Get-Content 'e:\temp\details.txt' | select-String -Pattern SGSS -Context 0,1 |
ForEach-Object -Process { $result = $_ -split ':'; echo 'here';$result[-1]}

Upvotes: 0

Views: 218

Answers (2)

Grok42
Grok42

Reputation: 206

Perhaps you could try setting a flag variable and then act on next match of your pattern. This was tested against a test file of your details.txt example above. It correctly selected your SGSS S/N#. I did alter it slightly so that on successful match of "SGSS", it just removed the "S/N: " text.

Here is the multi-line formatting. If you compose it single-line you will need the semi-colon in the ForEach-Object block, I believe.

powershell -Command Get-Content 'e:\temp\details.txt' | 
    Foreach-Object {
        if($_ -match 'SGSS') { $Found = 1 };
        if(($_ -match 'S\/N:\s([a-zA-Z0-9]+)[\n\r\S\s]*') -and ($Found -eq 1)) { $_ -replace '^.*N:\s' }
    }

And Single-Liner:

powershell -Command Get-Content 'e:\temp\details.txt' ^| Foreach-Object { if($_ -match 'SGSS') { $Found = 1 }; if(($_ -match 'S\/N:\s([a-zA-Z0-9]+)[\n\r\S\s]*') -and ($Found -eq 1)) { $_ -replace '^.*N:\s' } }

EDIT: It seems you had the most luck with your entire PowerShell command encapsulated in double quotes. It seems more an issue of getting your entire command string passed to powershell versus not having the correct powershell commands to retrieve your data. Perhaps the commands just above would run on your particular device interface if they were encapsulated like below.

powershell "Get-Content 'e:\temp\details.txt' | Foreach-Object { if($_ -match 'SGSS') { $Found = 1 }; if(($_ -match 'S\/N:\s([a-zA-Z0-9]+)[\n\r\S\s]*') -and ($Found -eq 1)) { $_ -replace '^.*N:\s' } }"

This ran successfully on cmd.exe and retrieved the S/N#, but I cannot say if it will run on your device interface.

Also, using your original command with the addition of "ForEach-Object" and an expression of the Match results ($Matches[2]), I received same results. I believe the error you originally received was a result of expressing $_ outside of a Foreach-Object block.

powershell "Get-Content e:\temp\details.txt | select-String -Pattern SGSS -Context 0,1 | ForEach-Object { $_ -match '(?ms)(N:\s)(([a-zA-Z0-9])+)'; $Matches[2] }"

Results

True
A791B634801

The "True" output can be removed by adjusting here... { $_ -match 'regex' | Out-Null; $Matches[2] }

As far as your original error, I believe you were piping to a variable ( $_ ), an expression assignment operation, as the below example shows.

"THIS IS A TEST STRING PIPED TO A VARIABLE" | $TEST

    At line:1 char:49
Expressions are only allowed as the first element of a pipeline.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : ExpressionsMustBeFirstInPipeline
    
$TEST = "THIS IS A TEST STRING PIPED TO A VARIABLE" | ForEach-Object { $_ }

No Error

Upvotes: 1

TheMadTechnician
TheMadTechnician

Reputation: 36332

You can use Where-Object and the automatic $Matches variable in a ForEach-Object scriptblock to get what you want:

powershell -command Get-Content e:\temp\details.txt -raw ^| Where{$_ -match '(?ms)SGSS[\r\n\s]*S/N: (\S+)\b'} ^| ForEach-Object {$Matches[1]}

Updated to escape the pipes and fix quoting issue.

Upvotes: 2

Related Questions