My program looks like this which is defined with hash:
where $varname and $varValue are passed dynamically from the file:Below is my program:
$appOptk= {
%hash{$varName}=>$varValue,
};
push #{$hash{metrics}->{appOptions}},$appOptk;
Output of the program looks like this:
"metrics": {
"appOptions": [{
"shell.common.report_default_significant_digits ": "4"
}, {
"time.remove_clock_reconvergence_pessimism ": "true"
"route.detail.hop_layers_to_fix_antenna ": "true "
}, {
"clock_opt.flow.optimize_layers ": "false"
}, {
"clock_opt.flow.skip_placement ": "true"
}
],
Can anyone please tell how to get below output by removing brackets inside the hash
"metrics": {
"appOptions": [{
"shell.common.report_default_significant_digits ": "4"
"time.remove_clock_reconvergence_pessimism ": "true"
"route.detail.hop_layers_to_fix_antenna ": "true "
"clock_opt.flow.optimize_layers ": "false"
"clock_opt.flow.skip_placement ": "true"
],
It would be easier to read if everything was in Perl. I think this is what you want.
$hash{metrics}{appOptions}[-1]{$varName} = $varValue;
Of course, you have to decide when a new array is added to the list.
# add new array
push #{$hash{metrics}{appOptions}}, {};
your expected output doesn't make any sense to me but you can achieve it by change push #{$hash{metrics}->{appOptions}},$appOptk; to
${#{$hash{metrics}->{appOptions}}[0]->{$varName}} = $varValue;
Related
I`m really struggling with Perl and I need to solve this topic. I Have a rest api response that I converted to json, Dumper shows something like this:
VAR1= [
{
"id":"abc",
"type":"info",
"profile":
{"name":"Adam",
"description":"Adam description"}
},
{
"id":"efg",
"type":"info",
"profile":
{"name":"Jean",
"description":"Jean description"}
},
{
"id":"hjk",
"type":"info",
"profile":
{"name":"Jack",
"description":"Jack description"}
},
]
What I need is to iterate over each "name" and check if value is Jean. I wanted to iterate over hashes inside of array, but each time it will only store first hash, not all of them.
What I`m trying and failing:
# my json gather, Dumper is shown above.
my $result_json = JSON::from_json($rest->GET( $host, $headers )->{_res}->decoded_content);
# I`ve tried many things to get all hashes, but either error, or single hash, or single value:
my $list = $result_json->[0];
my $list2 = $result_json->[0]->{'profile'};
my $list3 = #result_json->[0];
my $list4 = #result_json->[0]->{'profile'};
my $list5 = #result_json;
my $list5 = #result_json->{'profile'}; # this throws error
my $list6 = #result_json->[0]->{'profile'}->{'name'};
my $list7 = $result_json->[0]->{'profile'}->{'name'};
# and maybe more combinations... its just an example.
foreach my $i (<lists above>){
print $i;
};
Any idea how to set it up properly and iterate over each "name"?
Assuming that the call to JSON::from_json shown in the code smaple is indeed given the JSON string shown as a Dumper output,† that $result_json is an array reference so iterate over its elements (hash references)
foreach my $hr (#{ $result_json }) {
say "profile name: ", $hr->{profile}{name};
}
† That last comma in the supposed Dumper's output can't actually be there, so I removed it to use the rest as a sample JSON for testing
in Powershell 5
I do not understand, when debugging this code, why when the string " of the" is matched at the end of the main string, the method does not perform exit from the Class Method and continues to iterate.
Seems contra-intuitive to me.
Must I use the auxiliary variable and a label combined with break command?
something like here:
:JUMPASFOX foreach ($i in (1..10)) {
...{break JUMPASFOX}
I thought return $false anywhere causes exit.
I cannot reconcile that my code does not work.
Class EndFinder {
[string[]]$Exclusions_End_Text=#(" of", " of the", " of a", "for a", "for the", " in", " the")
EndFinder(){}
[boolean]Contains_Exclusion_At_The_End ([string]$ClipText) {
#$found=$false
$this.Exclusions_End_Text |foreach {
$exclusion_length=$_.Length
$clip_length=$ClipText.Length
if ($exclusion_length -lt $clip_length) {
$tailing=$ClipText.Substring($clip_length-$exclusion_length,$exclusion_length)
if ($tailing -eq $_) {
#$found=$true
return $true
}
}
}
return $false
#return $found
}
}
$kb=[EndFinder]::New()
$kb.Contains_Exclusion_At_The_End("big problem of the")
As Mathias R. Jessen points out in his comment, the return keyword when used in the scope of a cmdlet such as ForEach-Object will go to the next iteration, similar to what continue would do if it was a loop (for, foreach, while, etc).
Goes to next iteration when the condition is met:
0..4 | ForEach-Object {
if($_ % 2) {
return 'Odd'
}
'Even'
}
Even
Odd
Even
Odd
Even
# Same behavior with script block:
0..4 | & {
process
{
if($_ % 2) {
return 'Odd'
}
'Even'
}
}
Stops the iteration:
foreach($i in 0..4) {
if($i % 2) {
return 'Odd'
}
'Even'
}
Even
Odd
I'm assuming you're trying to see if the string provided to your method ends with any of the $Exclusions_End_Text strings from your class property. If that's the case, this should do it:
Class EndFinder {
hidden [string[]]$Exclusions_End_Text = #(
" of", " of the", " of a", "for a", "for the", " in", " the"
)
# EndFinder(){} => Constructor is not needed here, PS does this by default.
[boolean] Contains_Exclusion_At_The_End ([string]$ClipText)
{
foreach($i in $this.Exclusions_End_Text)
{
if($ClipText.EndsWith($i))
{
return $true
}
}
return $false
}
}
$kb = [EndFinder]::New()
$kb.Contains_Exclusion_At_The_End('big problem of the') # => True
$kb.Contains_Exclusion_At_The_End('not ending with any $Exclusions_End_Text') # => False
I would personally use a static method in this case, I believe it's more appropriate for what you're trying to accomplish:
Class EndFinder {
static [boolean] Contains_Exclusion_At_The_End ([string]$ClipText)
{
$Exclusions_End_Text = #(
" of", " of the", " of a", "for a", "for the", " in", " the"
)
foreach($i in $Exclusions_End_Text)
{
if($ClipText.EndsWith($i))
{
return $true
}
}
return $false
}
}
[EndFinder]::Contains_Exclusion_At_The_End('big problem of the')
[EndFinder]::Contains_Exclusion_At_The_End('not ending with any $Exclusions_End_Text')
We need all active records using "tag" which matches search string in createQueryBuilder.
For Ex: Search for "NEW YORK" which internally search for "NEW" or "YORK" in Tags "new, ab, other"
My CODE:
$query = $this->createQueryBuilder();
$query->field('status')->equals("active");
if (isset($params["search"]) && !empty($params["search"])) {
$searchArr = explode(" ", $params["search"]);
foreach ($searchArr as $searchVal) {
$query->addOr(
$query->expr()->addOr($query->expr()->field('tags')->equals(new \MongoRegex('/.*' . $searchVal . '.*/')))
);
}
}
It will give result active OR (tag1 Or tag2). Expected result active AND (tag1 Or tag2)
Any suggestions?
Replace this
foreach ($searchArr as $searchVal) {
$query->addOr(
by this
foreach ($searchArr as $searchVal) {
$query->addAnd(
I know this question has been asked before but I can't get any of the answers I have looked at to work. I have a JSON file which has thousands of lines and want to simply extract the text between two strings every time they appear (which is a lot).
As a simple example my JSON would look like this:
"customfield_11300": null,
"customfield_11301": [
{
"self": "xxxxxxxx",
"value": "xxxxxxxxx",
"id": "10467"
}
],
"customfield_10730": null,
"customfield_11302": null,
"customfield_10720": 0.0,
"customfield_11300": null,
"customfield_11301": [
{
"self": "zzzzzzzzzzzzz",
"value": "zzzzzzzzzzz",
"id": "10467"
}
],
"customfield_10730": null,
"customfield_11302": null,
"customfield_10720": 0.0,
So I want to output everything between "customfield_11301" and "customfield_10730":
{
"self": "xxxxxxxx",
"value": "xxxxxxxxx",
"id": "10467"
}
],
{
"self": "zzzzzzzzzzzzz",
"value": "zzzzzzzzzzz",
"id": "10467"
}
],
I'm trying to keep it as simple as possible - so don't care about brackets being displayed in the output.
This is what I have (which outputs way more than what I want):
$importPath = "todays_changes.txt"
$pattern = "customfield_11301(.*)customfield_10730"
$string = Get-Content $importPath
$result = [regex]::match($string, $pattern).Groups[1].Value
$result
Here is a PowerShell function which will find a string between two strings.
function GetStringBetweenTwoStrings($firstString, $secondString, $importPath){
#Get content from file
$file = Get-Content $importPath
#Regex pattern to compare two strings
$pattern = "$firstString(.*?)$secondString"
#Perform the opperation
$result = [regex]::Match($file,$pattern).Groups[1].Value
#Return result
return $result
}
You can then run the function like this:
GetStringBetweenTwoStrings -firstString "Lorem" -secondString "is" -importPath "C:\Temp\test.txt"
My test.txt file has the following text within it:
Lorem Ipsum is simply dummy text of the printing and typesetting industry.
So my result:
Ipsum
The quick answer is - change your greedy capture (.*) to non greedy - (.*?). That should do it.
customfield_11301(.*?)customfield_10730
Otherwise the capture will eat as much as it can, resulting in it continuing 'til the last customfield_10730.
Regards
You need to make your RegEx Lazy:
customfield_11301(.*?)customfield_10730
Live Demo on Regex101
Your Regex was Greedy. This means it will find customfield_11301, and then carry until it finds the very last customfield_10730.
Here is a simpler example of Greedy vs Lazy Regex:
# Regex (Greedy): [(.*)]
# Input: [foo]and[bar]
# Output: foo]and[bar
# Regex (Lazy): [(.*?)]
# Input: [foo]and[bar]
# Output: "foo" and "bar" separately
Your Regex was very similar to the first one, it captured too much, whereas this new one captures the least amount of data possible, and will therefore work as you intended
First issue is Get-Content pipe will give you line by line not the entire content at once. You can pipe Get-Content with Out-String to get entire content as a single string and do the Regex on the content.
A working solution for your problem is:
Get-Content .\todays_changes.txt | Out-String | % {[Regex]::Matches($_, "(?<=customfield_11301)((.|\n)*?)(?=customfield_10730)")} | % {$_.Value}
And the output will be:
": [
{
"self": "xxxxxxxx",
"value": "xxxxxxxxx",
"id": "10467"
}
],
"
": [
{
"self": "zzzzzzzzzzzzz",
"value": "zzzzzzzzzzz",
"id": "10467"
}
],
"
As an aside: Since your input appears to be JSON, you're normally better off parsing it into an object graph with ConvertFrom-Json, which you can easily query; however, your JSON appears to be nonstandard in that it contains duplicate property names.
There's good information in the existing answers, but let me try to cover all aspects in a single answer:
tl;dr
# * .Matches() (plural) is used to get *all* matches
# * Get-Content -Raw reads the file *as a wole*, into a single, multiline string
# * Inline regex option (?s) makes "." match newlines too, to match *across lines*
# * (.*?) rather than (.*) makes the matching *non-greedy*.
# * Look-around assertions - (?<=...) and (?=...) - to avoid the need for capture groups.
[regex]::Matches(
(Get-Content -Raw todays_changes.txt),
'(?s)(?<="customfield_11301":).*?(?="customfield_10730")'
).Value
Output with your sample input:
[
{
"self": "xxxxxxxx",
"value": "xxxxxxxxx",
"id": "10467"
}
],
[
{
"self": "zzzzzzzzzzzzz",
"value": "zzzzzzzzzzz",
"id": "10467"
}
],
For an explanation of the regex and the ability to experiment with it, see this regex101.com page
As for what you tried:
$pattern = "customfield_11301(.*)customfield_10730"
As has been noted, the primary problem with this regex is that (.*) is greedy, and will keep matching until the last occurrence of customfield_10730 has been found; making it non-greedy - (.*?) solves that problem.
Additionally, this regex will not match across multiple lines, because . by default does not match newline characters (\n). The easiest way to change that is to place inline regex option (?s) at the start of the pattern, as shown above.
It was only a lucky accident that still caused cross-line matching in your attempt, as explained next:
$string = Get-Content $importPath
This stores an array of strings in $string, with each element representing a line from the input file.
To read a file's content as a whole into a single, multiline string, use Get-Content's -Raw switch: $string = Get-Content -Raw $importPath
$result = [regex]::match($string, $pattern).Groups[1].Value
Since your $string variable contained an array of strings, PowerShell implicitly stringified it when passing it to the [string] typed input parameter of the [regex]::Match() method, which effectively created a single-line representation, because the array elements are joined with spaces (by default; you can specify a different separator with $OFS, but that is rarely done in practice).
For instance, the following two calls are - surprisingly - equivalent:
[regex]::Match('one two'), 'e t').Value # -> 'e t'
# !! Ditto, because array #('one', 'two') stringifies to 'one two'
[regex]::Match(#('one', 'two'), 'e t').Value # -> 'e t'
I'm supposed to tag this as homework, but I'm not sure how. Not asking for an answer, just to be pointed in the right direction of what I'm doing wrong. Basically, this is supposed to make a complete sentence out of ANY user input, with the exception of -AllCaps, which is functioning properly. My problem is the other part.
param ($title, [switch]$AllCaps)
$ex="to|a|the|at|in|of|with|and|but|or"
function Proper($ts)
{
$nt=foreach($word in $ts)
{
$word=$word.ToLower()
if($word -match $ex)
{
if($word -eq $ts[0])
{
$letters=$word -csplit("")
$letters[1]=$letters[1].ToUpper()
$word=$letters -join("")
}
else
{
$word=$word
}
}
else
{
$letters=$word -csplit("")
$letters[1]=$letters[1].ToUpper()
$word=$letters -join("")
}
"$word"
}
$nt=$nt -join(" ")
Write-host $nt
}
if($AllCaps)
{
$title=$title.ToUpper()
"$title"
}
else
{
$ts=$title -split(" ")
$newtitle=Proper $ts
}
So, when I execute the script passing in "the waffle of kings", expected output is "The Waffle of Kings" - instead, the code seems to be completely ignoring the "else" in the first "if else" statement, and the output I'm getting is "The waffle of kings". Since "waffle" doesn't match anything in $ex, it should be moving to the else part, capitalizing the first letter.
Less of note is that it doesn't write to the console without including "$word" within the foreach loop, though the way I have it written, the Write-Host should be doing that for me. If I take out the write-host nothing changes, but if I take out the "$word" in the loop, I get no output at all.
Using the above mentioned
$ex='^(to|a|the|at|in|of|with|and|but|or)$'
I ended up with a good one-liner, but have expanded it out here:
Function Proper($words)
{
$ex='^(to|a|the|at|in|of|with|and|but|or)$'
$nt = $(
foreach ($word in $words)
{
$word = $word.ToLower();
if ($word -match $ex)
{
$word
} else
{
$word = ([String]$word[0]).ToUpper()+$word.Substring(1);
$word
}
}
) -join " "
return $nt
}
I hope that helps.
Thanks, Chris.