{"id":84,"date":"2020-11-01T18:47:06","date_gmt":"2020-11-01T17:47:06","guid":{"rendered":"http:\/\/theredwindows.net\/?p=84"},"modified":"2021-07-30T18:36:28","modified_gmt":"2021-07-30T16:36:28","slug":"powershell-fonctions-simples-et-avancees-aide-integree","status":"publish","type":"post","link":"https:\/\/theredwindows.net\/index.php\/2020\/11\/01\/powershell-fonctions-simples-et-avancees-aide-integree\/","title":{"rendered":"PowerShell &#8211; Fonctions, simples et avanc\u00e9es, aide int\u00e9gr\u00e9e"},"content":{"rendered":"\n<p>En programmation, lorsque du code est plusieurs fois r\u00e9utilis\u00e9, il est pr\u00e9f\u00e9rable pour gagner du temps d&#8217;\u00e9crire une fonction, qui permet d&#8217;effectuer une t\u00e2che pr\u00e9cise avec de potentiels arguments, PowerShell n&#8217;\u00e9chappe pas \u00e0 la r\u00e8gle et propose la cr\u00e9ation de ce genre d&#8217;objet.<\/p>\n\n\n\n<h3>Fonction simple<\/h3>\n\n\n\n<p>Globalement une fonction est cr\u00e9\u00e9 avec le mot r\u00e9serv\u00e9 <code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">function<\/code><\/code> il en est suivi du nom de la fonction et enfin entre accolade (et oui encore) les instructions \u00e0 ex\u00e9cuter. Une fonction est appel\u00e9e par son nom et ses diff\u00e9rents param\u00e8tres. Ces derniers sont d\u00e9finis gr\u00e2ce \u00e0 <code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">param()<\/code><\/code>; pour cr\u00e9er un param\u00e8tre il faut simplement indiquer son nom comme une variable (si un type plus qu\u2019un autre est attendu, comme pour les variables, il faut simplement ajouter entre crochet avant le type), s&#8217;ils sont plusieurs les s\u00e9parer par une virgule (si une valeur par d\u00e9faut est exig\u00e9e si le param\u00e8tre n\u2019est pas appel\u00e9, comme pour une variable apr\u00e8s un <code>=<\/code> ajouter la valeur par d\u00e9faut), les param\u00e8tres peuvent poss\u00e9der des attributs (ces derniers seront d\u00e9taill\u00e9s dans la partie Fonction avanc\u00e9e) qui permettent de mieux g\u00e9n\u00e9rer les entr\u00e9es. La variable <code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">$PSBoundParameters<\/code><\/code> est instanci\u00e9e \u00e0 chaque fois qu\u2019une fonction est appel\u00e9e. C\u2019est une variable locale de type <code>Dictionnary<\/code>. Les cl\u00e9s sont le nom des param\u00e8tres et la valeur associ\u00e9e la valeur donn\u00e9e au param\u00e8tre. Une fonction peut avoir une valeur de retour, pour cela le mot <code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">return<\/code><\/code> suivi ou non d\u2019une variable (ou une valeur directement) peut \u00eatre utilis\u00e9. Par exemple:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">function Power {\n\n\tparam (\n\n\t\t[Int32]$Number,\n\t\t[Int32]$Exponent = 2\n\t)\n\n\t$result [System.Math]::Pow($Number, $Exponent)\n\treturn $result\n}<\/pre>\n\n\n\n<p>Accessoirement pour demander un param\u00e8tre, une autre syntaxe existe:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">function foo ($param) {}<\/pre>\n\n\n\n<p>Les param\u00e8tres sont appel\u00e9s par un <code>-<\/code> pr\u00e9c\u00e9dant leur nom (ou leur alias) suivi d\u2019une \u00e9ventuelle valeur, ou alors en pla\u00e7ant la valeur sur l\u2019index de position du param\u00e8tre. Les variables d\u00e9finies \u00e0 l\u2019int\u00e9rieur d\u2019une fonction ne sont pas globales mais locales.<\/p>\n\n\n\n<h3>Fonction avanc\u00e9e<\/h3>\n\n\n\n<p>Ces fonctions sont vraiment plus int\u00e9ressantes puisqu\u2019elles imitent le comportement d\u2019une cmdlet, les param\u00e8tres ont des attributs, et la fonction poss\u00e8de les parties <code>BEGIN<\/code>, <code>PROCESS<\/code> et <code>END<\/code> (non obligatoire) exactement comme en C#. De ce fait, des arguments &#8220;par d\u00e9faut&#8221; lui sont attribu\u00e9s:<code> -Verbose<\/code>,<code> -Debug<\/code> etc. Ces param\u00e8tres permettent de modifier le comportement d\u2019une fonction avanc\u00e9e tel une cmdlet, pour plus de renseignement vous pouvez regarder <a rel=\"noreferrer noopener\" href=\"https:\/\/docs.microsoft.com\/en-us\/powershell\/module\/microsoft.powershell.core\/about\/about_commonparameters?view=powershell-7\" target=\"_blank\">cet article de Microsoft<\/a>. Une fonction avanc\u00e9e reprend la syntaxe d\u2019une fonction simple, avec quelques modifications:<\/p>\n\n\n\n<ul><li>Le nom doit \u00eatre compos\u00e9 d\u2019un verbe et d\u2019un nom, comme une cmdlet C#.<\/li><li>Avant la d\u00e9claration des param\u00e8tres, les diff\u00e9rents attributs de fonctions, dont un est n\u00e9cessaire pour avoir une fonction avanc\u00e9e <code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[CmdletBinding()]<\/code><\/code> qui peut prendre diff\u00e9rents arguments (l<a rel=\"noreferrer noopener\" href=\"https:\/\/docs.microsoft.com\/en-us\/powershell\/module\/microsoft.powershell.core\/about\/about_functions_cmdletbindingattribute?view=powershell-7\" target=\"_blank\">a documentation de Microsoft sur ce sujet<\/a>), il en existe d\u2019autre tel <code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[OutputType()]<\/code><\/code> qui prend en param\u00e8tre le type de sortie de la fonction (il est attendu le type litt\u00e9ral, comme celui pr\u00e9c\u00e9dant une variable typ\u00e9; il peut y en avoir plusieurs chaque type \u00e9tant s\u00e9par\u00e9 par une virgule).<\/li><li>Il est pr\u00e9f\u00e9rable pour les param\u00e8tres d\u2019avoir des attributs.<\/li><li>Il est pr\u00e9f\u00e9rable que les instructions de la fonction soient dans des blocs tel <code>BEGIN<\/code>, <code>PROCESS<\/code> et <code>END<\/code>.<\/li><\/ul>\n\n\n\n<p>Commen\u00e7ons par les attributs de param\u00e8tre, ces derniers sont pr\u00e9sents entre crochet sous forme d\u2019acc\u00e9l\u00e9rateur de type:<\/p>\n\n\n\n<ul><li><code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[Alias('NAME1', 'NAME2')]<\/code><\/code> un attribut qui permet d\u2019ajouter un alias non pas sur la cmdlet mais sur le nom du param\u00e8tre.<\/li><li><code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[ValidateCount(MIN,MAX)]<\/code><\/code> un attribut qui permet de n\u2019accepter que des listes dont la taille est comprise entre <code>MIN<\/code> et <code>MAX<\/code>.<\/li><li><code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[ValidateLenght(MIN,MAX)]<\/code><\/code> un attribut qui permet de n\u2019accepter qu\u2019une cha\u00eene de caract\u00e8re dont la longueur est comprise entre <code>MIN<\/code> et <code>MAX<\/code>.<\/li><li><code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[ValidatePattern('REGEX')]<\/code><\/code> un attribut qui permet de n\u2019accepter qu\u2019une cha\u00eene de caract\u00e8re qui valide une comparaison &#8220;regex&#8221; qui est d\u00e9finit par <code>REGEX<\/code>.<\/li><li><code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[ValidateRange(MIN, MAX)]<\/code><\/code> un attribut qui permet de n\u2019accepter qu\u2019un entier situer entre <code>MIN<\/code> et <code>MAX<\/code>.<\/li><li><code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[ValidateScript(SCRIPT)]<\/code><\/code> un attribut qui permet de n\u2019accepter qu\u2019un param\u00e8tre apr\u00e8s sa v\u00e9rification par un scriptblock <code>SCRIPT<\/code>, la valeur pass\u00e9 dans le param\u00e8tre est plac\u00e9 dans la variable <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">$_<\/code>.<\/li><li><code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[ValidateSet('VALUE1', 'VALUE2')]<\/code><\/code> un attribut qui permet de n\u2019accepter que si la valeur du param\u00e8tre est comprise dans le tableau <code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">@('VALUE1', 'VALUE2')<\/code><\/code>, chaque valeur est s\u00e9par\u00e9 de la prochaine par une virgule.<\/li><li><code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[ValidateNotNull()]<\/code><\/code> un attribut qui permet de ne pas accepter une valeur fourni si elle est \u00e9gal \u00e0 <code>$null<\/code>.<\/li><li><code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[ValidateDrive('DRIVE1', 'DRIVE2')]<\/code><\/code> un attribut qui permet de ne pas accepter un chemin fourni si ce dernier n\u2019est pas dans le tableau de <code>PSDrive<\/code> <code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">@('DRIVE1', 'DRIVE2')<\/code><\/code>.<\/li><li><code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[ValidateNotNullOrEmpty()]<\/code><\/code> un attribut qui permet de ne pas accepter les param\u00e8tres dont la valeur est <code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">$null<\/code><\/code> ou si la cha\u00eene de caract\u00e8re est vide.<\/li><li><code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[AllowNull()]<\/code><\/code> permet d\u2019accepter un param\u00e8tre dont la valeur est <code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">$null<\/code><\/code>.<\/li><li><code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[AllowEmptyString()]<\/code><\/code> permet d\u2019accepter une cha\u00eene de caract\u00e8re vide.<\/li><li><code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[AllowEmptyCollection()]<\/code><\/code> permet d\u2019accepter des tableaux, liste par exemple dont la longueur est 0.<\/li><li><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[switch]<\/code> dont la valeur est un <code>PSObject<\/code> \u00e0 une seul entr\u00e9e <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">.IsPresent<\/code> qui est un bool\u00e9en indiquant si l\u2019argument a \u00e9t\u00e9 appel\u00e9 ou non.<\/li><li><code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[Parameter()]<\/code><\/code> qui peut prendre en arguments plusieurs choses:<ul><li><code>Mandatory<\/code> un bool\u00e9en qui d\u00e9termine si le param\u00e8tre est n\u00e9cessaire.<\/li><li><code>Position<\/code> un entier qui d\u00e9finit la position par d\u00e9faut du param\u00e8tre.<\/li><li><code>ValueFromPipeline <\/code>un bool\u00e9en qui d\u00e9finit si la variable peut prendre ses valeurs depuis un &#8220;pipeline&#8221;.<\/li><li><code>ValueFromPipelineByPropertyName <\/code>est un bool\u00e9en qui d\u00e9termine si l\u2019objet pass\u00e9 par la pipeline doit avoir le m\u00eame nom de propri\u00e9t\u00e9 que le nom du param\u00e8tre.<\/li><li><code>ParameterSetName <\/code>une cha\u00eene de caract\u00e8res qui d\u00e9termine un groupe de param\u00e8tre auquel ce dernier appartient.<\/li><li><code>helpMessage<\/code>, est une cha\u00eene de caract\u00e8res qui appara\u00eet lors de l\u2019usage de <code>Get-Help CMDLET -Parameter ParameterName<\/code>.<\/li><\/ul><\/li><\/ul>\n\n\n\n<p>La d\u00e9claration d\u2019un param\u00e8tre exigeant des <code>PSCredential<\/code> est un peu particuli\u00e8re, il est en effet pr\u00e9f\u00e9rable de la faire ainsi (sans compter l\u2019attribut <code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">Parameter()<\/code><\/code>):<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">[Parameter(Mandatory=$false)]\n[System.Management.Automation.CredentialAttribute()]\n[System.Management.Automation.PSCredential]\n$Credential = [System.Management.Automation.PSCredential]::Empty<\/pre>\n\n\n\n<p>Il en existe quelques autres que vous pouvez retrouver <a rel=\"noreferrer noopener\" href=\"https:\/\/docs.microsoft.com\/en-us\/powershell\/module\/microsoft.powershell.core\/about\/about_functions_advanced_parameters?view=powershell-7\" data-type=\"URL\" data-id=\"https:\/\/docs.microsoft.com\/en-us\/powershell\/module\/microsoft.powershell.core\/about\/about_functions_advanced_parameters?view=powershell-7\" target=\"_blank\">ici<\/a>. PowerShell prend aussi en charge l\u2019utilisation de param\u00e8tre dynamique, ils permettent la cr\u00e9ation ou non de param\u00e8tre lors de l\u2019appel des param\u00e8tres d\u2019une fonction avanc\u00e9e (ou une cmdlet). Plus globalement ces derniers sont cr\u00e9\u00e9s dans une section nomm\u00e9e <code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">DynamicParameter<\/code><\/code> suivie d\u2019accolades dans lesquelles il y a les instructions. Si un param\u00e8tre est appel\u00e9 il ne sera pas nul, donc la v\u00e9rification de la pr\u00e9sence peut se faire avec <code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">if<\/code><\/code>. La cr\u00e9ation d\u2019un param\u00e8tre reprend les \u00e9tapes suivantes:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$paramDictionnary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary\n$attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]\n\n$attribute = New-Object System.Management.Automation.ParameterAttribute\n$attribute.Mandatory = $true\n$attribute.Position = 0\n$attribute.ValueFromPipeline = $true\n$attributeCollection.Add($attribute)\n\n$attribute = New-Object System.Management.Automation.ValidateNotNullOrEmptyAttribute\n$attributeCollection.Add($attribute)\n\n$attribute = New-Object System.Management.Automation.ValidateCountAttribute(1,2)\n$attributeCollection.Add($attribute)\n\n...\n\n$Name = 'DynamicParameter'\n\n$dynParam = New-Object System.Management.Automation.RuntimeDefinedParameter($name, [String[]]; $attributeCollection)\n$paramDictionnary.Add($Name, $dynParam)\n\n$paramDictionnary<\/pre>\n\n\n\n<p>C\u2019est plut\u00f4t complexe mais extr\u00eamement pratique. La valeur des param\u00e8tres ne sera pas retourn\u00e9e directement, mais sera ajout\u00e9 dans <code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">$PSBoundParameters<\/code><\/code>.<\/p>\n\n\n\n<h3>Aide int\u00e9gr\u00e9e<\/h3>\n\n\n\n<p>Il est possible, comme en python par exemple, d\u2019ajouter une aide int\u00e9gr\u00e9e \u00e0 nos fonctions. Elle se d\u00e9finit au d\u00e9but de la fonction, entre commentaires multi lignes. Chaque segment permet d\u2019ajouter une entr\u00e9e \u00e0 l\u2019aide, on d\u00e9compte:<\/p>\n\n\n\n<ul><li><code>.SYNOPSIS<\/code>.<\/li><li><code>.DESCRIPTION<\/code>.<\/li><li><code>.PARAMETER<\/code>.<\/li><li><code>.EXAMPLE<\/code>.<\/li><li><code>.NOTES<\/code>.<\/li><li><code>.LINK<\/code>.<\/li><li><code>.OUTPUTS<\/code>.<\/li><li><code>.INPUTS<\/code>.<\/li><\/ul>\n\n\n\n<p>Une fois un segment appel\u00e9, sa valeur doit \u00eatre ajout\u00e9e sur la ligne de dessous, et durera jusqu&#8217;\u00e0 la fin du commentaire. <code>.PARAMETER<\/code> prend un argument \u00e0 sa droite le nom du param\u00e8tre. Certains segments sont d\u00e9finis lorsque certaines options sont ajout\u00e9es: <code>helpMessage<\/code> de l\u2019attribut <code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">Parameter()<\/code><\/code> permet de d\u00e9finir <code>.PARAMETER<\/code> et l\u2019attribut <code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[OutputType()]<\/code><\/code> ajoute <code>.OUTPUTS<\/code> \u00e0 l\u2019aide. Il existe d\u2019autres mani\u00e8res de d\u00e9finir l\u2019aide d\u2019une fonction, Laurent Dardenne (Microsoft PowerShell MVP, fran\u00e7ais qui plus ai) en propose une <a rel=\"noreferrer noopener\" href=\"https:\/\/ottomatt.pagesperso-orange.fr\/Data\/Tutoriaux\/Powershell\/Creation-de-fichier-daide-XML-Powershell\/Creation-de-fichier-daide-XML-Powershell.pdf\" data-type=\"URL\" data-id=\"https:\/\/ottomatt.pagesperso-orange.fr\/Data\/Tutoriaux\/Powershell\/Creation-de-fichier-daide-XML-Powershell\/Creation-de-fichier-daide-XML-Powershell.pdf\" target=\"_blank\">ici<\/a>. Pour acc\u00e9der \u00e0 l\u2019aide d\u2019une fonction il faut utiliser la cmdlet <code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">Get-Help<\/code><\/code> qui propose nombre de param\u00e8tres:<\/p>\n\n\n\n<ul><li><code>-Name<\/code> pr\u00e9cise le nom de la cmdlet.<\/li><li><code>-Full<\/code> affiche toutes l\u2019aide.<\/li><li><code>-Parameter<\/code> suivit d\u2019un param\u00e8tre, affiche l\u2019aide d\u2019un param\u00e8tre particulier.<\/li><li><code>-Examples<\/code> affiche les exemples de la fonction.<\/li><\/ul>\n\n\n\n<p>Exemple d&#8217;aide int\u00e9gr\u00e9e:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;#\n\t.SYNOPSIS\n\t\tcette partie r\u00e9sume bri\u00e8vement la fonction\n\n\t.DESCRIPTION\n\t\tcette partie explique plus en d\u00e9tail ce que fait la fonction\n\n\t.PARAMETER Parameter\n\t\tcette partie explique le r\u00f4le, les valeurs par d\u00e9faut du param\u00e8tre Parameter\n\t\n\t.EXAMPLE\n\t\tInvoke-MyFunction -Parameter \"hello\"\n\n\t.NOTES\n\t\tcette partie exprime diff\u00e9rentes remarques \u00e0 l\u2019\u00e9gard de la fonction\n\n\t.LINK\n\t\tcette partie nous donne des \u00e9ventuelles liens pour mieux comprendre le fonctionnement, ou les r\u00e9f\u00e9rences \u00e0 cette fonction\n\n\t.OUTPUTS\n\t\tcette partie informe des types de retour de la fonction\n\n\t.INPUTS\n\t\tcette partie informe des types d\u2019entr\u00e9e de la fonction\n#><\/pre>\n\n\n\n<p>Un petit projet de fin de dossier est disponible sur le blog, n\u2019h\u00e9sitez pas \u00e0 aller le consulter, il met en pratique les diff\u00e9rentes notions abord\u00e9es durant ces derniers postes.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>En programmation, lorsque du code est plusieurs fois r\u00e9utilis\u00e9, il est pr\u00e9f\u00e9rable pour gagner du temps d&#8217;\u00e9crire une fonction, qui permet d&#8217;effectuer une t\u00e2che pr\u00e9cise avec de potentiels arguments, PowerShell n&#8217;\u00e9chappe pas \u00e0 la r\u00e8gle et propose la cr\u00e9ation de ce genre d&#8217;objet. Fonction simple Globalement une fonction est cr\u00e9\u00e9 avec le mot r\u00e9serv\u00e9 function [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[2],"tags":[],"_links":{"self":[{"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/posts\/84"}],"collection":[{"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/comments?post=84"}],"version-history":[{"count":5,"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/posts\/84\/revisions"}],"predecessor-version":[{"id":254,"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/posts\/84\/revisions\/254"}],"wp:attachment":[{"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/media?parent=84"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/categories?post=84"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/tags?post=84"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}