Strange Get-Item behaviour - powershell

If I simply do
Get-Item "HKLM:\SOFTWARE\MozillaPlugins\#microsoft.com/GENUINE"
I get this on the console
NAME
----
#microsoft.com/GENUINE
However, this
$test = Get-Item "HKLM:\SOFTWARE\MozillaPlugins\#microsoft.com/GENUINE"
Write-Host "$($test.name)"
returns the full path, not just the name of the key.
Is this a bug? Intended behavior? Me doing something incorrectly?

Is this a bug? Intended behavior? Me doing something incorrectly?
The middle one.
PowerShell formatting files (*.format.ps1xml located in the directory defined in $pshome by default) explain the difference here. In a default output scenario PowerShell checks for known formatting definitions, which are loaded from aforementioned files, for given object types. If present, it will use those, which govern what data and how that data is outputted.
So, in your case, you have Microsoft.Win32.RegistryKey objects. The format definitions for that are stored in registry.format.ps1xml. Just going to show a truncated section of that file so you can see how your top example is created.
.... output truncated ....
<TableControl>
<TableHeaders>
<TableColumnHeader>
<Width>30</Width>
<Label>Name</Label>
</TableColumnHeader>
<TableColumnHeader>
<Label>Property</Label>
</TableColumnHeader>
</TableHeaders>
<TableRowEntries>
<TableRowEntry>
<Wrap/>
<TableColumnItems>
<TableColumnItem>
<PropertyName>PSChildName</PropertyName>
.... output truncated ....
So this defines an output table with 2 columns: name, property. The name column is actually the objects pschildname property. This is separate from the actual name property of the object. This is why you get the difference you see above.
There are other examples of this misconception in the PS world as well. The more you know.
The go-to resource on formatting files would be about_format.ps1xml. If you have issues with the default formatting and are not satisfied with simple Select-Object then you can create your own from copies.
FWIW you could have found both properties by just doing something like
Get-Item "HKLM:\SOFTWARE\MozillaPlugins\#microsoft.com/GENUINE" | Format-List *
That would have forced all properties to show and you would have seen name and pschildname.

Related

DITA: reuse the same text multiple times with different variable setting for each instance

I'm working on a document where, among other things, I need to explain two units that are very similar. I want to reuse text in both descriptions, but I want to use the name of the units in the shared text, and to configure a form of substitution/variable so the name of the units appear in each description. Note that the description of both units appear in the final document.
We're using a structure like this:
top.ditamap, which includes:
units_a_and_b.ditamap, which includes:
unit_a.dita
unit_b.dita
and then this file with text snippets:
unit_a_b_shared.dita
unit_a.dita and unit_b.dita will conref text snippets from unit_a_b_shared.dita.
So basically I want unit_a_b_shared.dita to contain something like this:
"When you configure DOODAA to ..."
and then I want DOODAA to be replaced with unit_a inside the unit_a part of the document, and with unit_b inside the unit_b part.
I've tried to use keywords for this, but so far without success. I haven't found a way to make them take on different values in the different files, even when using keyscopes as explained here:
https://blog.oxygenxml.com/keyscopes/keyscopesBlog.html
The problem seems to be that with keyscopes I need the full path, which includes which unit it is, and hence cannot be used in the text snippet which is shared. Without keyscopes the first definition of the keyword applies everywhere.
Any suggestions on how to achieve this goal (using keywords or not)?
I wrote that key scopes article on the Oxygen XML Blog and I think that key scopes seem to be the answer for your case.
So the "unit_a_b_shared.dita" file would have inside a something like:
<p id="reusablePara">some text before <ph keyref="unit"/> some text after</p>
And then in the DITA Map you would refer to ""unit_a_b_shared.dita"" in different key scopes and re-define the key "unit" in those places to bind it to a different value.
The DITA Map would need to look like this:
<map>
<title>Main</title>
<topicref href="unit_a.dita" keyscope="unitA">
<keydef href="unit_a_b_shared.dita" keys="reusables"/>
<keydef keys="unit">
<topicmeta>
<keywords>
<keyword>KM</keyword>
</keywords>
</topicmeta>
</keydef>
</topicref>
<topicref href="unit_b.dita" keyscope="unitB">
<keydef href="unit_a_b_shared.dita" keys="reusables"/>
<keydef keys="unit">
<topicmeta>
<keywords>
<keyword>KG</keyword>
</keywords>
</topicmeta>
</keydef>
</topicref>
</map>
and inside "unit_a.dita" you would conkeyref to the reusable paragraph inside the "unit_a_b_shared.dita" file:
<p conkeyref="reusables/reusableParagraph"/>
Note that I'm using "conkeyref" not "conref". Once you get to use key scopes you should avoid direct links or direct content references, use only indirect linking using keys.

How to query XAML file with Powershell's `select-xml` commandlet?

I'm trying to use the select-xml cmdlet in Powershell to query some XAML files in my .NET project. Here's what I've tried:
select-xml -content (cat $xaml_file_path) -xpath "//GroupBox"
Where $xaml_file_path is simply a string containing the file path to the XAML file of interest.
This throws an error:
Select-Xml: Cannot validate argument on parameter 'Content'. The argument is null, empty, or an element of the argument collection contains a null value. Supply a collection that does not contain any null values and then try the command again.
I know the XAML is valid since it compiles fine on Visual Studio. So I'm thinking there might be something else going on here.
Is there a way to query XAML files using Powershell? If so how?
To query an XML (XAML) document that uses namespaces, Select-Xml requires you to:[1]
declare all the namespaces that any nodes involved in your XPath query are in, via a hashtable that maps self-chosen prefixes to namespace URIs (parameter -Namespace).
Those self-chosen prefixes may, but needn't, be the same as in the input document, with the exception of the default namespace (xmlns), for which a name must be chosen too, but which can not be xmlns (the example below uses _).
use those self-chosen prefixes for all the nodes referenced in the XPath query (parameter -XPath), including those in the default (implied) namespace.
A simple example:
# Create a sample XAML file.
#"
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Test"
Title="MainWindow" Height="450" Width="500">
<Grid>
<TextBox x:Name="UserInnput" Height="140" TextWrapping="Wrap" VerticalAlignment="Top" AcceptsReturn="True" AcceptsTab="True" Padding="4" VerticalScrollBarVisibility="Auto" />
<Button x:Name="Save" Content="Save" HorizontalAlignment="Left" VerticalAlignment="Top" Width="100" IsDefault="True" Height="22" Margin="170,150,0,0" />
</Grid>
</Window>
"# | Set-Content sample.xml
# Create the hashtable that uses self-chosen names to the URIs
# of those namespaces involved in the XPath query below.
$namespaces = #{
# This is the default namespace, which the elements in the input
# document that have *no* namespace prefix are *implicitly* in.
# You must NOT use 'xmlns' as the prefix.
_ = 'http://schemas.microsoft.com/winfx/2006/xaml/presentation'
# For non-default namespaces, you may choose the same prefixes
# as in the original document.
# Note:
# * This namespace isn't actually used in the query below.
# * However, if the nodes involved in the query do fall into
# this or other namespaces, they must be declared here too,
# and their prefixes must then be used in the query.
x = 'http://schemas.microsoft.com/winfx/2006/xaml'
}
# Query the XAML file, passing the hashtable to -Namespace, and
# using its keys as namespace prefixes; here, '_:' must be used
# to refer to nodes in the default namespace.
Select-Xml -LiteralPath sample.xml -Namespace $namespaces -XPath '//_:TextBox'
If you want to avoid having to deal with namespaces:
In the context of Select-Xml (as well as the underlying System.Xml.XmlDocument.SelectNodes() .NET method), the only way to avoid having to deal with namespaces is to use the *[local-name()='...'] workaround shown in your own answer.
In the context of PowerShell's adaption of the [xml] DOM, which adds "virtual" properties to instances of [xml] (System.Xml.XmlDocument):
These properties are always named for the non-prefixed node names; that is, namespaces are effectively ignored.
This is convenient and often sufficient, but it limits you to "drilling down" with dot notation into the document, as opposed to having being able to run XPath queries against the document; for the latter, you can use the .SelectNodes() and .SelectSingleNode() methods, which, however, again necessitate namespace management.[1]
The equivalent example with dot notation, building on the same sample file:
# Parse the XML file into an [xml] instance.
($doc = [xml]::new()).Load((Convert-Path sample.xml))
# Drill down to the <TextBox> element.
# Note: If the <Grid> element had *multiple* <TextBox> children,
# they would *all* be returned, as a System.Xml.XmlElement array.
$doc.Window.Grid.TextBox
[1] The need for namespace management applies analogously to direct use of the underlying System.Xml.XmlDocument.SelectNodes() and System.Xml.XmlDocument.SelectSingleNodes() .NET API, although constructing the prefix-to-URI mapping table is a little more cumbersome there - see this answer.
I think that, similarly to xmllint there might be an issue with handling namespaces.
I've found that the following does work:
(select-xml -path $xaml_file_path -xpath "//*[local-name()='GroupBox']").Node
If anyone knows of a cleaner/better way to do this (i.e. using the -namespace flag), I would be curious to hear about it.

Extract Key from XML

I am invoking an API using "Invoke-WebRequest" and getting the below as return XML.
<?xml version="1.0" encoding="utf-8"?>
<string xmlns="http://www.sft.com/">a879-956c3452f55e3c</string>
How can I extract the "a879-956c3452f55e3c" part from this retun XML.
I tried this code
$tmpstring1 = $temp.Content -split 'com/">'
$finalString1 = $tmpstring1[1] -split "</"
$Key = $finalString1[0]
String parsing of XML text is best avoided, because it is brittle; it's always preferable to us a dedicated XML parser; fortunately, PowerShell provides easy access to .NET's System.Xml.XmlDocument type ([xml]):
$xmlText = #'
<?xml version="1.0" encoding="utf-8"?>
<string xmlns="http://www.sft.com/">a879-956c3452f55e3c</string>
'#
([xml] $xmlText).string.InnerText # .'#text' works too
Note: PowerShell conveniently adapts the XML DOM for dot notation, so that elements and attributes can be accessed as if they were regular object properties - see this answer for more information.
Normally, an XML element that only has text content (a text child node, such as the <string> element's a879-956c3452f55e3c value here) directly returns that text content when accessed with dot notation (.string).
However, because the <string> element has a namespace declaration (xmlns=...), .string actually returns a [System.Xml.XmlElement] instance whose text child node must explicitly be accessed, either via its .InnerText property or via the adapted property named for the (generic) node name of the text child element, .'#text'.

BIML: FilySystemTask is missing UseDirectoryIfExists attribute for operation Create Directory

I'm trying to create a directory if it does not exists, using BIML. The SSIS File System Task has an operation Create Directory which has an UseDirectoryIfExists attribute, that can be set to true.
I cannot find that attribute for the <FileSystem> in BIML.
How can I set that property to true?
I havent used FileSystem in BIML yet, however i can give it a shot. Though i havent tested it.
You can also read more about the FileSystemTask properties here. As i can see it doesnt directly have an UseDirectoryIfExists property. However you can try what i have written below this.
This here is how a manuel SSIS-package XML looks like when you create a FileSystemTask with UseDirectoryIFExists = true
<DTS:ObjectData> <FileSystemData TaskOperationType="CreateDirectory" TaskOverwriteDestFile="True" /> </DTS:ObjectData>
If UseDirectoryIfExists = false
Then it looks like this
<DTS:ObjectData> <FileSystemData TaskOperationType="CreateDirectory" /> </DTS:ObjectData>
So i think your BIML should look like this:
<Tasks> <FileSystem Operation="CreateDirectory" OverwriteDestination="true"> </FileSystem> </Tasks>

Nant property cannot be overwritten eventhough not marked as readonly

I am using nant 0.85 version. I have defined a property in a file and have not specified like'read only=true". But where ever I try to change the value of the property , I get the warning saying that, property cannot be overwritten.
I have tried setting readonly="false" overwrite="true".But nothing seems to work. Any help would be greatly appreciated .
use unless attribute, it works.
<property name="msbuild.path" value="CONFIGURABLE" unless="${property::exists('msbuild.path')}" />
then as usual nant -D:msbuild.path=...
Need more details, especially if you "change the value of the property" from command line.
One thing that I have seen that causes some confusion is that when the property is overridden from command line ( -D:prop=value ), and if the same property is defined in the file (<property name="prop" value="value"/> ) it will say read only property cannot be overridden because the property set from command line is read only and it cannot be overridden by the property defined in the file.
It is not the other way around, which causes some confusion and people thinking that despite having no readonly set to true etc. still saying cannot be overridden.
So try to see if the property you set is actually using the value you wanted, if you are overriding from command line.
You can totally do this in NAnt 0.85. Let's say for example you have a property with the name "myvalue" that you want to be able to be passed in from the command line. You would first define the property in your NAnt script like this:
<property name="myvalue" value="0" overwrite="false" />
When you call NAnt you just need to use the -D parameter to pass in your new value like this:
nant.exe buildfile:myfile.build -logfile:mylog.log -D:myvalue=16
And your new value of "16" will be recognized in your build script which you can test by simply echoing the value like this:
<echo message="myvalue: ${myvalue}" />
For further information you can read the documentation and look at example "iv":
http://nant.sourceforge.net/release/0.85/help/tasks/property.html