Reputation: 3928
I have a file having key-value data in it. I have to get the value of a specific key in that file. I have the Linux equivalent command:
File:
key1=val1
key2=val2
..
Command:
cat path/file | grep 'key1' | awk -F '=' '{print $2}'
Output:
val1
I want to achieve the same output on windows as well. I don't have any experience working in power shell but I tried with the following:
Get-Content "path/file" | Select-String -Pattern 'key1' -AllMatches
But I'm getting output like this:
key1=val1
What am i doing wrong here?
Upvotes: 2
Views: 7752
Reputation: 439692
Note:
With your specific input format (key=value
lines), Алексей Семенов's helpful answer offers the simplest solution, using ConvertFrom-StringData
; note that it ignores whitespace around =
and trailing whitespace after the value.
The answer below focuses generally on how to implement grep
and awk
-like functionality in PowerShell.
It is not the direct equivalent of your approach, but a faster and PowerShell-idiomatic solution using a switch
statement:
# Create a sample file
@'
key1=val1
key2=val2
'@ > sample.txt
# -> 'val1'
switch -Regex -File ./sample.txt { '^\s*key1=(.*)' { $Matches[1]; break } }
The -Regex
option implicitly performs a -match
operation on each line of the input file (thanks to -File
), and the results are available in the automatic $Matches
variable.
$Matches[1]
therefore returns what the first (and only) capture group ((...)
) in the regex matched; break
stops processing instantly.
A more concise, but slower option is to combine the -match
and -split
operators, but note that this will only work as intended if only one line matches:
((Get-Content ./sample.txt) -match '^\s*key1=' -split '=')[1]
Also note that this invariably involves reading the entire file, by loading all lines into an array up front via Get-Content
.
A comparatively slow version - due to using a cmdlet and thereby implicitly the pipeline - that fixes your attempt:
(Select-String -List '^\s*key1=(.*)' ./sample.txt).Matches[0].Groups[1].Value
Note:
Select-String
outputs wrapper objects of type Microsoft.PowerShell.Commands.MatchInfo
that wrap metadata around the matching strings rather than returning them directly (the way that grep
does); .Matches
is the property that contains the details of the match, which allows accessing what the capture group ((...)
) in the regex captured, but it's not exactly obvious how to access that information.
The -List
switch ensures that processing stops at the first match, but note that this only works with a direct file argument rather than with piping a file's lines individually via Get-Content
.
Note that -AllMatches
is for finding multiple matches in a single line (input object), and therefore not necessary here.
Another slow solution that uses ForEach-Object
with a script block in which each line is -split
into the key and value part, as suggested by Jeroen Mostert:
Get-Content ./sample.txt | ForEach-Object {
$key, $val = $_ -split '='
if ($key -eq 'key1') { $val }
}
Caveat: This invariably processes all lines, even after the key of interest was found.
To prevent that, you can append | Select-Object -First 1
to the command.
Unfortunately, as of PowerShell 7.1 there is no way to directly exit a pipeline on demand from a script block; see long-standing GitHub feature request #3821.
break
does not work as intended - except if you wrap your pipeline in a dummy loop statement (such as do { ... } while ($false)
) to break out of.Upvotes: 0
Reputation: 856
<# required powershell version 5.1 or later
@'
key1=val1
key2=val2
'@ | out-file d:\temp.txt
#>
(Get-Content d:\temp.txt | ConvertFrom-StringData).key1
Upvotes: 3