Steve Martin
Steve Martin

Reputation: 75

select-string use in a for-each loop

I am working on building a script that will access a large amount of files and pull a specific string out in order to assign as a variable.

The string is similar across all files which is not the issue. I am able to make this process function as an individual event (define a single source file)

$hostname_import = select-string .\test.txt -Pattern 'hostname ABC-.+'
$hostname = $hostname_import -replace '.+ ',''

The above would output the specific hostname identified in the target file (the second function is to trim the word hostname and the space) I can then use this to proceed with using the variable as needed to perform various actions.

The issue I am having is executing this function in a foreach loop so that I can grab the initial file- execute the select-string function (or similar) and then perform the data modification within the loop as expected.

For context- I am going through configuration files- and building a separate file based on those configurations to report on findings- part of the report building requires the hostname of the device.

--EDIT 1: After consulting with my rubber duck I will be attempting to import these files as CSVs in order to potentially reach a solution.

Huge help!

Upvotes: 3

Views: 5334

Answers (2)

mklement0
mklement0

Reputation: 437197

Select-String can directly process multiple files via its -Path or -LiteralPath parameter, as an array of paths and/or as wildcard expressions.

What it doesn't support is passing a directory path in order to process the files in it (let alone recursively), so for that you'll have pipe the results of a Get-ChildItem (possibly with -Recurse) to the Select-String call.

The following example uses the latter technique, looping over all *.config files in the current directory's subtree:

Get-ChildItem -File -Recurse -Filter *.config |
  Select-String -Pattern 'hostname ABC-(.+)' |
   ForEach-Object {
     $sourceFilePath = $_.Path
     $hostName = $_.Matches[0].Groups[1].Value
   }

Note the use of a capture group ((...)) inside the regex pattern, which allows extracting only the substring of interest from the overall match, via the Microsoft.PowerShell.Commands.MatchInfo instances that Select-String outputs. This obviates the need for the -replace operation; see the bottom section for details.

Note that multiple matches may be reported per file; if you know that there's only one (or are only interested in the first), add -List to the Select-String call to speed up the operation.


How to extract only the text (string) of a matching line / line part:

When you use Select-String output objects (which are of type Microsoft.PowerShell.Commands.MatchInfo) directly in string contexts such as -replace, the resulting string representation contains more than just the line text if a file argument was given, because the input file path is prepended to the line text, followed by :; e.g.:
C:\path\to\file.config:hostname ABC-foo

To extract the line text only, directly as a string, use the .Line property.

  • Note: In PowerShell (Core) 7+ you can now ask Select-String to output strings (matching lines) directly, by passing the -Raw switch.

To extract only the part of the line that the regex matched, use the .Matches property (n/a if the -SimpleMatch switch for literal substring matching was also passed), as shown above.

  • .Matches is a collection of System.Text.RegularExpressions.Match instances (there can only be multiple elements if the -AllMatches switch was passed), the .Value property of each contains the text that matched the pattern overall.

  • If the -Pattern regex contains capture groups ((...)), each Match instance's .Groups collection - itself composed of Match instances - contains what these groups captured, starting with index 1; again the .Value property contains the captured text.

Upvotes: 6

Otter
Otter

Reputation: 1141

It sounds like you need a Get-ChildItem which will fetch all files in a folder then pipe the output to a Foreach-Object loop which will reference the each file.

Note that I have added the -recurse switch in case you need to fetch subfolders files too.

Try the below:

Get-Childitem -path "C:\PathToFolder" -recurse | Foreach-Object {
    $hostname_import = select-string $_.Fullname -Pattern 'hostname ABC-.+'
    $hostname = $hostname_import -replace '.+ ',''
    
    # The rest of your logic goes here
}

Upvotes: 1

Related Questions