2

I'm stuck on a problem pulling text from a file. Here's what I ultimately want to do:

  1. Search through a specific file for instances of the phrase "NoRequest".
  2. ONLY take the value of NoRequest. i.e. if NoRequest = true, then it would only add "true" to the array, not the whole line.
  3. Do this for each instance of the phrase in the file.

Here's a snippet of the section I'm working on:

$FileContents = Select-String -Path "C:\Users\me\Documents\file.ini" -Pattern "NoRequest"

When I use that line, I get the following:

PS> $FileContents
Documents\file.ini:222:NoRequest = false
Documents\file.ini:281:NoRequest = false
Documents\file.ini:347:NoRequest = false
Documents\file.ini:387:NoRequest = false
Documents\file.ini:1035:NOREQUEST = true
Documents\file.ini:1047:NoRequest = true
Documents\file.ini:1160:NOREQUEST = true
Documents\file.ini:1242:NOREQUEST = true

This is what I want to get:

PS> $FileContents
false
false
false
false
true
true
true
true

I've tried piping the code into a Select-Object -Last 1 solution, but since my variable is an array it selects the last line in the array, not the last word of each line. I've also thought of just taking the array and doing a "foreach line in the array, cut everything off except the last word". I'm guessing that would work, but it seems like it would be a clumsy solution. I'm sure there's a simple and effective solution to this, but I'm just not seeing it.

Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328

4 Answers4

5

You could use -split on the string and grab the last result:

Select-String -Path "C:\Users\me\Documents\file.ini" -Pattern "NoRequest" |ForEach-Object {
    -split $_.Line |Select-Object -Last 1
}
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • I always liked the approach of `ForEach-Object { $_.Line.Trim().Split(' ')[-1] }`. I think some people get confused about embedded pipelines within loops, and lose track of what pipeline they're dealing with. – TheMadTechnician Aug 21 '18 at 20:29
  • It's not exactly obvious, but it is what `awk` does by default, and perhaps that's what PowerShell chose as a model. – mklement0 Aug 21 '18 at 22:35
3

Alternatively you could just read the file in, pass it to a Where statement to find the lines you want, and use the $Matches automatic variable.

$FileContents = Get-Content "C:\Users\me\Documents\file.ini" | 
    Where{$_ -Match 'NoRequest = (true|false)'} | 
    ForEach-Object { $Matches[1] }
TheMadTechnician
  • 34,906
  • 3
  • 42
  • 56
1

With small files such as *.ini files, using an expression rather than the pipeline is an option, which enables solutions that are both concise and performant (PSv4+ syntax):

(@(Get-content C:\Users\me\Documents\file.ini) -match 'NoRequest').ForEach({ (-split $_)[-1] })
  • @(Get-content C:\Users\me\Documents\file.ini) returns the lines of the specified file as an array (@(...) ensures that an array is returned even if there happens to be just 1 line in the file).

  • -match, with an array as the LHS, acts as a filter and returns only those elements that match the specified regex.

  • The PSv4+ .ForEach() method then acts on the elements of the resulting array, splitting each into tokens by whitespace (-split), and returning the last token (-1).

As an aside: The unary form of the -split operator (-split '...') acts like the venerable awk Unix utility does by default: tokens are extracted based on any nonempty run of whitespace as the separator, with leading and trailing whitespace being ignored.

mklement0
  • 382,024
  • 64
  • 607
  • 775
0

Change your regular expression to (?<=NoRequest.* )(\w+)$, so that it matches the last word in a line (\w+ is one or more word character, $ is the end of a line) only if it's preceded by the string "NoRequest". (?<=...) is a positive lookbehind assertion that checks for the presence of a pattern without making it part of the returned match. Then expand the value of the match.

$file = 'C:\Users\me\Documents\file.ini'
Select-String -Path $file -Pattern '(?<=NoRequest.* )(\w+)$' | ForEach-Object {
    $_.Matches.Value
}

If the last word can only be either "true" or "false" you can make the expression more specific by replacing \w+ with an alternation:

Select-String -Path $file -Pattern '(?<=NoRequest.* )(true|false)$' | ForEach-Object {
    $_.Matches.Value
}
Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328