PS KOANS Walkthrougt

Exercice 4 « About Strings » :

Les chaines de caractères (strings) sur PowerShell se présentent sous deux formes :
– Les chaînes extensibles : utilise les double guillemets «  », elles étendent les variables.
– Les littéraux de chaîne : utilise les simples guillemets  », elles n’étendent pas les variables.

Pour illustré le concept :

PS> $var1=Le contenu de la variable var1

#En utilisant une chaîne extensible, on affiche le contenu de la variable. 
#PowerShell interprète la chaîne de façon dynamique.
PS> Ceci est $var1
Ceci est Le contenu de la variable var1

#En utilisant une chaîne littérale, on affiche le contenu strict de la chaîne de caractère.
#PowerShell n'interprète rien, il affiche directement ce qu'il y a entre les simples guillemets.
PS> Ceci est $var1
Ceci est $var1

using module PSKoans
[Koan(Position = 104)]
param()
<#
    Strings

    Strings in PowerShell come in two flavours: expandable strings, and string
    literals. Expandable strings in PowerShell are created using double quotes,
    while string literals are created with single quotes.

    Expandable strings in PowerShell can evaluate expressions or variables
    mid-string in order to dynamically insert values into a preset string, like
    a sort of impromptu template string.
#>
Describe 'Strings' {

    It 'is a simple string of text' {
        'string' | Should -Be 'string'
    }

    Context 'Literal Strings' {

        It 'assumes everything is literal' {
            $var = 'Some things you must take literally'
            'Some things you must take literally' | Should -Be $var
        }

        It 'can contain special characters' {
            # 'Special' is just a title.
            $complexVar = 'They interpret these characters literally: $ ` $()'
            'They interpret these characters literally: $ ` $()' | Should -Be $complexVar
        }

        It 'can contain quotation marks' {
            $Quotes = 'This creates only one set of ''quotation marks'' in the string.'

            # Single quotes are easier to work with in double-quoted strings.
            "This creates only one set of 'quotation marks' in the string." | Should -Be $Quotes
        }
    }

    Context 'Evaluated Strings' {

        It 'can expand variables' {
            $var = 'apple'
            'My favorite fruit is apple' | Should -Be "My favorite fruit is $var"
        }

        It 'can do a simple expansion' {
            'Your home directory is: C:\Users\Hams' | Should -Be "Your home directory is: $HOME"
        }

        It 'handles other ways of doing the same thing' {
            # Strings can handle entire subexpressions being inserted as well!
            $String = "Your home folder is: $(Get-Item -Path $HOME)"
            'Your home folder is: C:\Users\Hams' | Should -Be $String
        }

#######################################
#######Attention a ce point ###########
#######################################

#il y a un guillemet dans le mot Powershell's, il faut mettre la chaîne de caractère entre double guillemets autrement on va générer une erreur.
        It 'will expand variables that do not exist' {
            <#
                If a string contains a variable that has not been created,
                PowerShell will still try to expand the value. A variable that
                doesn't exist or has a null value will simply disappear when it
                is expanded.

                This could be the result of a typing mistake.
            #>
            $String = "PowerShell's home folder is: $SPHome"
            "PowerShell's home folder is: " | Should -Be $String
        }

        It 'can get confused about :' {
            <#
                In PowerShell, a colon (:) is used to define a scope or provider
                path for a variable. For example, the Environment provider uses
                the syntax $env:SomeVariableName, which refers to an environment
                variable named 'SomeVariableName'. Environment variables are
                initially set by the operating system and usually passed to
                child processes, offering a limited way for processes to
                communicate in a limited way.

                When : is part of a string, PowerShell will try and expand a
                variable in that scope or from that provider.
            #>

            $Number = 1
            $String = "$Number:Get shopping"
            ' shopping' | Should -Be $String   #Attention à l'espace avant shopping
        }

        It 'can use curly braces to mark the variable name' {
            <#
                Variables followed by : or adjacent to other characters normally
                included in a variable name can be referenced by enclosing the
                variable name with curly braces like so: ${varName}
            #>

            $Number = 1
            $String = "${Number}:Get shopping"
            '1:Get shopping' | Should -Be $String
        }

        It 'can escape special characters with backticks' {
            $LetterA = 'Apple'
            $String = "`$LetterA contains $LetterA."

            "`$LetterA contains Apple." | Should -Be $String
        }

        It 'can escape quotation marks' {
            $String = "This is a `"string`" value."
            $AlternateString = "This is a ""string"" value."

            # A mirror image, a familiar pattern, reflected in the glass.
            $Results = @(
                '____'
                '____'
            )
            $Results | Should -Be @($String, $AlternateString)
        }

        It 'can insert special characters with escape sequences' {
            <#
                All text strings in PowerShell are actually just a series of
                character values. Each of these values has a specific number
                assigned in the [char] type that represents that letter, number,
                or other character.

                .NET uses UTF16 encoding for [char] and [string] values.
                However, with most common letters, numbers, and symbols, the
                assigned [char] values are identical to the ASCII values.

                ASCII is an older standard encoding for text, but you can still
                use all those values as-is with [char] and get back what you'd
                expect thanks to the design of the UTF16 encoding. An extended
                ASCII code table is available at: https://www.ascii-code.com/
            #>
            $ExpectedValue = [char] 9

            <#
                If you're not sure what character you're after, consult the
                ASCII code table above.

                Get-Help about_Special_Characters will list the escape sequence
                you can use to create the right character with PowerShell's
                native string escape sequences.
            #>
            $ActualValue = "`_"

            $ActualValue | Should -Be $ExpectedValue
        }
    }

    Context 'String Concatenation' {

        It 'adds strings together' {
            # Two become one.
            $String1 = '_____'
            $String2 = 'is cool.'

            $String1 + ' ' + $String2 | Should -Be 'This string is cool.'
        }

        It 'can be done more easily' {
            # Water mixes seamlessly with itself.
            $String1 = 'This string'
            $String2 = 'is cool.'

            "$String1 $____" | Should -Be 'This string is cool.'
        }
    }

    Context 'Substrings' {

        It 'lets you select portions of a string' {
            # Few things require the entirety of the library.
            $String = 'At the very top!'

            <#
                The [string].Substring() method has a few variants, or
                "overloads." The most common overloads are:

                string Substring(int startIndex)
                string Substring(int startIndex, int length)

                In other words:
                - Both variants return a string.
                - One variant needs two index references, where to start and
                    stop in selecting the substring.
                - The other only requires a starting index, and goes until the
                    end of the original string.
            #>
            '____' | Should -Be $String.Substring(0, 6)
            '____' | Should -Be $String.Substring(7)
        }
    }

    Context 'Here-Strings' {
        <#
            Here-strings are a fairly common programming concept, but have some
            additional quirks in PowerShell that bear mention. They start with
            the sequence @' or @" and end with the matching reverse "@ or '@
            sequence.

            The terminating sequence MUST be at the start of the line, or they
            will be ignored, and the string will not terminate correctly.
        #>
        It 'can be a literal string' {
            $LiteralString = @'
            Hullo!
'@ # This terminating sequence must be at the start of the line.

            # "Empty" space, too, is a thing of substance for some.
            '            ____' | Should -Be $LiteralString
        }

        It 'can be an evaluated string' {
            # The key is in the patterns.
            $Number = __

            # Indentation sometimes gets a bit disrupted around here-strings.
            $String = @"
I am number #$Number!
"@

            '____' | Should -Be $String
        }

        It 'interprets all quotation marks literally' {
            $AllYourQuotes = @"
All things that are not 'evaluated' are "recognised" as characters.
"@
            '____' | Should -Be $AllYourQuotes
        }
    }

    Context 'Arrays and Strings' {

        It 'can be inserted into a string' {
            <#
                Arrays converted to string will display each member separated by
                a space by default.
            #>
            $array = @(
                'Hello'
                'world'
            )
            '____ ____' | Should -Be "$array"
        }

        It 'can be joined with a different string by setting the OFS variable' {
            <#
                The $OFS variable, short for "Output Field Separator," defines
                the separator used to join an array when it is converted to a
                string.

                By default, the OFS variable is unset, and a single space is
                used as the separator.
            #>

            $OFS = '... '
            $array = @(
                'Hello'
                'world'
            )
            '____' | Should -Be "$array"

            # Removing the created OFS variable, the default will be restored.
            Remove-Variable -Name OFS -Force
        }
    }
}

A retenir :

Les expressions :

Alors qu’on utilise les chaines extensibles, il faut faire attention avec ce type de chaines, car cela ne retourne pas le résultat escompté , par exemple en exécutant ce bout de code, nous aimerions avoir l’identifiant unique (Handle) de l’application Calc :

Calc
$c = Get-Process Calc
"Calc utilise $c.Handles"

Le résultat est : Calc utilise System.Diagnostics.Process (calc).Handles

PowerShell voit $c comme une variable à développer, mais .Handles est considéré comme une partie de la chaîne de caractères. Donc, il développe $c en System.Diagnostics.Process (Calc) et .Handles est simplement ajouté à la chaîne de caractères. C’est pourquoi vous obtenez Calc utilise System.Diagnostics.Process (Calc).Handles.

Pour y remédier, il faut redéfinir la partie $c.Handles en expression : $($c.Handles).

$($c.Handles) est considéré maintenant comme une expression à développer. PowerShell évalue d’abord l’expression à l’intérieur des parenthèses, $c.Handles, qui donne le nombre de handles du processus Calc, puis insère ce résultat dans la chaîne de caractères. C’est pourquoi vous obtenez quelque chose comme Calc utilise 42.

En résumé, $variable.property dans une chaîne de caractères ne sera pas développé comme vous le pensez en PowerShell. Vous devez utiliser $($variable.property) pour obtenir le résultat souhaité. C’est une caractéristique de la syntaxe de PowerShell.

Les deux-points (:) :

PS> $Number = 1
PS> $String = "$Number:Get shopping"
PS> $string
PS>  shopping

En PowerShell, un deux-points (:) est utilisé pour définir une portée ou un chemin de fournisseur pour une variable. Par exemple, le fournisseur d’environnement utilise la syntaxe $env:NomDeVariable, qui fait référence à une variable d’environnement nommée ‘NomDeVariable’. Les variables d’environnement sont initialement définies par le système d’exploitation et sont généralement transmises aux processus enfants, offrant une manière limitée pour les processus de communiquer de manière limitée.

Lorsqu’un deux-points (:) fait partie d’une chaîne de caractères, PowerShell essaiera d’étendre une variable dans cette portée ou à partir de ce fournisseur. C’est pourquoi dans l’exemple précédent, $Number: est interprété comme une variable d’environnement, ce qui conduit à une chaîne vide car il n’y a pas de variable d’environnement avec ce nom.

Pour éviter ce genre de soucis lors du scripting, vous devez ajouter des {} :

PS> $Number = 1
PS> $String = "${Number}:Get shopping"
PS> $string
PS> 1:Get shopping

Dans ce cas il nous retourne bien le résultat escompté, car il n’a pas interpréter la variable number dans le scope d’une variable d’environnement.

Ignorer un caractère avec (`):

PS> $LetterA = 'Apple'
PS> $String = "`$LetterA contains $LetterA."

Dans ce cas, la variable $LetterA au debut de la chaine de caractère de la variable string ne sera pas considéré comme une variable puisque son $ qui l’a définit en tant que variable est ignorer par `.

Il peut aussi ignorer (escape) l’interprétation des symboles, par exemple `"code`" sera traduis en "code", PowerShell transformera les «  » en chaine de caractère non interprétable, il se contentera de les afficher.

Powershell met à disposition des utilisateurs plusieurs séquences d’escape, par exemple si on veut faire un saut de ligne on utilisera n ou si on veut faire une tabulation `t

Get-Help about_Special_Characters listera les séquences d’escape et leurs détails si besoin de plus d’information.

Retour en haut