I have a problem accessing json-objects of predictable structure but unknown depth in Powershell. So the json-objects contain information that can be connected by "and" and "or", but those connections can be used in several levels. As an exanple:
$ab=#"
{
"cond": "one",
"and": [
{"cond": "two"},
{"cond": "three"},
{"or": [{"cond": "four"},
{"cond": "five"}
]
}
]
}
"# | ConvertFrom-Json
I need to be able to read/test something like
$test="and.or"
$ab.$test.cond
where $test is a combination of several "and"s and "or"s like and.or.or.and .
The problem is that I can't figure out how my idea of $ab.$test.cond is to be written in Powershell to work. In theory I could test all possible combinations to a given depth by hand, but I'd prefer not to. Does anyhow have an idea how this could work? Thanks a lot!
(Powershell Version 5)
I think you should define a proper set of classes for your conditional engine/descriptors, either using PowerShell classes or using C# to create an assembly so you can use the types within PowerShell.
But for a quick and dirty PowerShell solution, you could do this:
"`$ab.$test.cond" | Invoke-Expression
# or
'$ab.{0}.cond' -f $test | Invoke-Expression
This has no error checking of course. Any other solution is likely going to be a separate recursive function if you want to get real checking and such, but it will be more fragile then using a well-defined set of objects.
Related
I've got a very simple Perl issue that I can't for the life of me figure out.
I'm consuming JSON formatted data from a REST endpoint in a perl script. The data is shaped like this:
{
"ScriptRunning": true
}
There's some other stuff, but really all I care about is the ScriptRunning tag. I'm consuming this data using JSON::Syck::Load like so:
my $running_scripts = JSON::Syck::Load($output_from_rest_call)
(in my current environment it is not possible to get other libraries for CPAN, so I'm stuck with that). All that is working correctly as far as I can tell, I used Data::Dumper to confirm the data looks good:
$VAR1 = {
'ScriptRunning' => 1 # or '' if false
}
However, I can't figure out how to actually get the value of 'ScriptRunning'. I've done print ref $running_scripts and confirmed that it is a HASH, however when I try to index into the hash I'm not getting anything. I've tried the following:
my $script_is_running = $running_scripts{'ScriptRunning'};
my $script_is_running = $running_scripts{ScriptRunning};
my $keys_in_running_scripts = keys $running_scripts; # keys_in_running_scripts is empty
my $keys_in_running_scripts = keys %running_scripts; # keys_in_running_scripts is empty
Any ideas?
You need to use strict; (and use warnings; while you are at it, maybe use diagnostics; too, when you are really stuck). As a general rule, ALWAYS use strict; and use warnings; because they prevent problematic code from running and give you some much more helpful output.
You should also read perldoc perlreftut, which helps explain what you are dealing with.
Your variable $running_scripts is not a hash, but a "hash reference", which is an important distinction. When you call ref on a real hash, it returns a false value, since it is not a reference.
What you need to do is "dereference" using the arrow operator to get the value.
To get the keys call to work, there's a separate syntax for dereferencing.
my $script_is_running = $running_scripts->{ScriptRunning};
my #keys_in_running_scripts = keys %{$running_scripts};
While working on log Queries in an arm template, I stuck with how to pass parameter values or variable values in the log Query.
parameters:
{
"resProviderName":
{
"value": "Microsoft.Storage"
}
}
For Eg:
AzureMetrics | where ResourceProvider == **parameters('resProviderName')** | where Resource == 'testacc'
Here I am facing an error like, it was taking parameters('resProviderName') as a value and it was not reading value from that particular parameter "resProviderName" and my requirement is to take the values from parameters or variables and should not hardcode like what I did in the above query as Resource=='testacc'.
Do we have any option to read the values from either parameters or variables in the log query?
If so, please help on this issue.
The answer to this would depend on what this query segment is a part of, and how your template is structured.
It appears that you're trying to reference a resource in the query. It is best to use one of the many available template resource functions if you want the details of the resource like its resourceID (within the resources section). When referencing a resource that is deployed in the same template, provide the name of the resource through a parameter. When referencing a resource that isn't deployed in the same template, fetch the resource ID.
Also, I'd recommend referring to the ARM snippet given this example to understand how queries can be constructed as custom variables as opposed to the other way round. According to ARM template best practices, variables should be used for values that you need to use more than once in a template. If a value is used only once, a hard-coded value makes your template easier to read. For more info, please take a look at this doc.
Hope this helps.
This is an old question but I bumped into this while searching for the same issue.
I'm much more familiar with Bicep templates so what I did to figure out was to create a Bicep template, construct the query by using the variables and compile it. This will generate a ARM template and you can analyze it.
I figured out you can use both concat or format functions in order to construct your query using variables.
I preferred the format one since it looks more elegant and readable (also, Bicep build generates the string using the format function).
So based on this example, the query would be something like this:
query: "[format('AzureMetrics | where ResourceProvider == {0} | where Resource == ''testacc'' ', parameters('resProviderName') )]"
Don't forget to escape the ' in the ARM template which you do by doubling the single quote.
I hope this helps some people.
André
I'm trying to pass a function to a method and then pass parameters to the method I passed when calling it, but if I pass more than one parameter then the method fails with an error:
function debugMeStuffs($someBlah, $somePoo) {
Write-Host $someBlah
Write-Host $somePoo
}
function invokeOnHosts ([scriptblock]$funcToCall, $param1, $param2, $startRange, $endRange) {
#Param($funcToCall)
$i = $startRange
for($i = [int]$startRange; $i -le $endRange; $i++) {
# HOW DO I MAKE THIS WORK WITH MULTIPLE PARAMETERS?!?!?!?
$funcToCall.Invoke('blah' 'poo')
}
}
invokeOnHosts $function:debugMeStuffs "param1" "param2" 4 7
Things I've tried:
$funcToCall("blah" "poo")
$funcToCall('blah' 'poo')
$funcToCall.Invoke("blah" "poo")
$funcToCall.Invoke('blah' 'poo')
$funcToCall 'blah' 'poo'
$funcToCall.Invoke 'blah' 'poo'
$funcToCall "blah" "poo"
$funcToCall.Invoke "blah" "poo"
None of the above seem to work. Is there something else I need to do to make this work?
.Invoke() is a .NET method, so the usual method-call syntax applies: you need
parentheses - (...) - around the list of arguments
you must separate the arguments with ,
$funcToCall.Invoke('blah', 'poo')
This contrasts with PowerShell's own syntax for calling cmdlets and functions, which is shell-like[1]:
no (...) around the argument list
arguments must be separated with spaces.
& $funcToCall blah poo # equivalent of the method call above.
A command such as the above is parsed in argument mode, which is why quoting the arguments in this simple case is optional.
Note the need for &, PowerShell's call operator, which is needed to execute the script block stored in $funcToCall; this is generally necessary for invoking a command stored in a variable, and also for command names / paths that are quoted.
Given that it's easy to get confused between PowerShell's command syntax and .NET's method syntax, it's best to stick with PowerShell-native features[2], if possible.
That said, being able to call methods on .NET types directly is a wonderful extensibility option.
To help avoid accidental use of method syntax when calling PowerShell commands, you can use Set-StrictMode -Version 2 or higher, but note that that entails additional strictness checks.
[1] PowerShell is, after all, a shell - but it is also a full-featured scripting language that offers near-unlimited access to the .NET framework.
Reconciling these two personalities is a difficult balancing act, and the shell-like command-invocation syntax is a frequent problem for newcomers with a programming background, given that the rest of the language looks like a traditional programming language and that calling methods on .NET types does use the traditional syntax.
[2] This means preferring PowerShell's cmdlets, functions, and operators to use of the underlying .NET types' methods; doing so also usually provides rewards you with operating at a higher level of abstraction.
I've been working on a very small program to grab details about Half Life 2 servers (using the protocol-srcds library). The workflow is pretty straightforward; it takes a list of servers from a file, queries each of them, and writes the output out to another file (which is read by a PHP script for display, as I'm tied to vBulletin). Would be nice if it was done in SQL or something, but seeing as I'm still just learning, that's a step too far for now!
Anyway, my question relates to serialization, namely, serializing to JSON. For now, I've written a scrappy helper function jsonify, such that:
jsonify (Just (SRCDS.GameServerInfo serverVersion
serverName
serverMap
serverMod
serverModDesc
serverAppId
serverPlayers
serverMaxPlayers
serverBots
serverType
serverOS
serverPassword
serverSecure
serverGameVersioning)) =
toJSObject [ ("serverName", serverName)
, ("serverMap", serverMap)
, ("serverPlayers", show serverPlayers)
, ("serverMaxPlayers", show serverMaxPlayers) ]
(I'm using the Text.JSON package). This is obviously not ideal. At this stage, however, I don't understand using instances to define serializers for records, and my attempts to do so met a wall of frustration in the type system.
Could someone please walk me through the "correct" way of doing this? How would I go about defining an instance that serializes the record? What functions should I use in the instance (showJSON?).
Thanks in advance for any help.
You might want to consider using Data.Aeson instead which might be regarded as the successor to Text.JSON.
With aeson you define separate instances for serialize/deserializing (with Text.JSON you have to define both directions even if you need only one, otherwise the compile will annoy you -- unless you silence the warning somehow), and it provides a few operators making defining instances a bit more compact, e.g. the example from #hammar's answer can be written a little bit less noisy as shown below with the aeson API:
instance ToJSON SRCDS.GameServerInfo where
toJSON (SRCDS.GameServerInfo {..}) = object
[ "serverName" .= serverName
, "serverMap" .= serverMap
, "serverPlayers" .= serverPlayers
, "serverMaxPlayers" .= serverMaxPlayers
]
One simple thing you can do is use record wildcards to cut down on the pattern code.
As for your type system problems, it's hard to give help without seeing error messages and what you've tried so far, however I suspect one thing that might be confusing is that the result of toJSObject will have to be wrapped in a JSObject data constructor as the return type of showJSON is supposed to be a JSValue. Similarly, the values of your object should also be of type JSValue. The easiest way to do this is to use their JSON instance and call showJSON to convert the values.
instance JSON SRCDS.GameServerInfo where
showJSON (SRCDS.GameServerInfo {..}) =
JSObject $ toJSObject [ ("serverName", showJSON serverName)
, ("serverMap", showJSON serverMap)
, ("serverPlayers", showJSON serverPlayers)
, ("serverMaxPlayers", showJSON serverMaxPlayers) ]
Package struct::record from TCLLIB provides means for emulating record types. But record instances are commands in the current namespace and not variables in the current scope. This means there is no garbage collection for record instances. Passing name of the record instance to a procedure means passing it by reference not by value, it is possible to pass string representation of the record as parameter but it requires to create another instance in the procedure, configure it and delete by hand, it's annoying. I wonder about the rationale behind this design. A simple alternative is provide a lisp-style records - a set of construction, access and modification procedures and represent records as lists.
The struct::record implementation is, from my viewpoint, an oo-style implementation. If you're searching for a data-style implementation (like lisp) where the commands are totally separate from the data, you might want to look at the dict command.
I'll note that oo-style and data-style are really not good descriptions, but they were the best I could think of offhand.
You most certainly can do it “the Lisp way”.
proc mkFooBarRecord {foo bar} {
# Keep index #0 for a "type" for easier debugging
return [list "fooBarRecord" $foo $bar]
}
proc getFoo {fooBarRecord} {
if {[lindex $fooBarRecord 0] ne "fooBarRecord"} {error "not fooBarRecord"}
return [lindex $fooBarRecord 1]
}
# Etc.
That works quite well. (Write it in C and you can make it more efficient too.) Mind you, as a generic data structure, it seems that many people prefer Tcl 8.5's dictionaries. There are many ways to use them; here's one:
proc mkFooBarRecord {foo bar} {
return [dict create "type" fooBarRecord "foo" $foo "bar" $bar]
}
proc getFoo {fooBarRecord} {
dict with fooBarRecord {
if {$type ne "fooBarRecord"} {error "not fooBarRecord"}
return $foo
}
}
As for the whole structures versus objects debate, Tcl tends to regard objects as state with operations (leading to a natural presentation as a command, a fairly heavyweight concept) whereas structures are pure values (and so lightweight). Having written a fair chunk on this, I really don't know what's best in general; I work on a case-by-case basis. If you are going with “structures”, also consider whether you should have collections that represent fields across many structures (equivalent to using column-wise storage instead of row-wise storage in a database) as that can lead to more efficient handling in some cases.
Also consider using a database; SQLite integrates extremely well with Tcl, is reasonably efficient, and supports in-memory databases if you don't want to futz around with disk files.
I will not answer your question, because I was not been using Tcl for many years and I never use this kind of struct, but I can give you the path to two possible places that are very plausible to provide a good answer for you:
The Tcl'ers Wiki http://wiki.tcl.tk
The Frenode's Tcl IRC channel
At the time I used Tcl they proved to be invaluable resources.