web-dev-qa-db-fra.com

Faire une boucle dans un hachage ou utiliser un tableau dans PowerShell?

J'utilise ce morceau de code (simplifié) pour extraire un ensemble de tables à partir de SQL Server avec bcp.

$OutputDirectory = "c:\junk\"
$ServerOption =   "-SServerName"
$TargetDatabase = "Content.dbo."

$ExtractTables = @(
    "Page"
    , "ChecklistItemCategory"
    , "ChecklistItem"
    )

for ($i=0; $i -le $ExtractTables.Length – 1; $i++)  {
    $InputFullTableName = "$TargetDatabase$($ExtractTables[$i])"
    $OutputFullFileName = "$OutputDirectory$($ExtractTables[$i])"
    bcp $InputFullTableName out $OutputFullFileName -T -c $ServerOption
}

Cela fonctionne très bien, mais certaines tables doivent maintenant être extraites via des vues, d'autres non. J'ai donc besoin d'une structure de données qui ressemble à ceci:

"Page"                      "vExtractPage"
, "ChecklistItemCategory"   "ChecklistItemCategory"
, "ChecklistItem"           "vExtractChecklistItem"

Je cherchais des hachages, mais je ne trouve rien sur la façon de passer en boucle dans un hachage. Quelle serait la bonne chose à faire ici? Peut-être juste utiliser un tableau, mais avec les deux valeurs, séparées par un espace?

Ou est-ce que je manque quelque chose d'évident?

75
Sylvia

La réponse de Christian fonctionne bien et montre comment vous pouvez parcourir chaque élément de la table de hachage en utilisant la méthode GetEnumerator. Vous pouvez également effectuer une boucle à l'aide de la propriété keys. Voici un exemple comment:

$hash = @{
    a = 1
    b = 2
    c = 3
}
$hash.Keys | % { "key = $_ , value = " + $hash.Item($_) }

Sortie:

key = c , value = 3
key = a , value = 1
key = b , value = 2
89
Andy Arismendi

Un raccourci n'est pas préférable pour les scripts; c'est moins lisible. L'opérateur% {} est considéré comme un raccourci. Voici comment procéder dans un script pour en améliorer la lisibilité et la réutilisabilité:

Configuration variable

PS> $hash = @{
    a = 1
    b = 2
    c = 3
}
PS> $hash

Name                           Value
----                           -----
c                              3
b                              2
a                              1

Option 1: GetEnumerator ()

Note: préférence personnelle; la syntaxe est plus facile à lire

La méthode GetEnumerator () se ferait comme suit:

foreach ($h in $hash.GetEnumerator()) {
    Write-Host "$($h.Name): $($h.Value)"
}

Sortie:

c: 3
b: 2
a: 1

Option 2: clés

La méthode des clés se ferait comme indiqué:

foreach ($h in $hash.Keys) {
    Write-Host "${h}: $($hash.Item($h))"
}

Sortie:

c: 3
b: 2
a: 1

Information additionnelle

Faites attention à trier votre hashtable ...

Sort-Object peut le changer en tableau:

PS> $hash.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Hashtable                                System.Object


PS> $hash = $hash.GetEnumerator() | Sort-Object Name
PS> $hash.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

Ceci et d'autres boucles PowerShell sont disponibles sur mon blog.

166
VertigoRay

À propos de la boucle dans un hachage:

$Q = @{"ONE"="1";"TWO"="2";"THREE"="3"}
$Q.GETENUMERATOR() | % { $_.VALUE }
1
3
2

$Q.GETENUMERATOR() | % { $_.key }
ONE
THREE
TWO
5
CB.

Vous pouvez aussi le faire sans variable

@{
  'foo' = 222
  'bar' = 333
  'baz' = 444
  'qux' = 555
} | % getEnumerator | % {
  $_.key
  $_.value
}
4
Steven Penny

Voici un autre moyen rapide, en utilisant simplement la clé comme index dans la table de hachage pour obtenir la valeur:

$hash = @{
    'a' = 1;
    'b' = 2;
    'c' = 3
};

foreach($key in $hash.keys) {
    Write-Host ("Key = " + $key + " and Value = " + $hash[$key]);
}
4
user1161625

Si vous utilisez PowerShell v3, vous pouvez utiliser JSON au lieu d'une table de hachage et le convertir en objet avec Convert-FromJson :

@'
[
    {
        FileName = "Page";
        ObjectName = "vExtractPage";
    },
    {
        ObjectName = "ChecklistItemCategory";
    },
    {
        ObjectName = "ChecklistItem";
    },
]
'@ | 
    Convert-FromJson |
    ForEach-Object {
        $InputFullTableName = '{0}{1}' -f $TargetDatabase,$_.ObjectName

        # In strict mode, you can't reference a property that doesn't exist, 
        #so check if it has an explicit filename firest.
        $outputFileName = $_.ObjectName
        if( $_ | Get-Member FileName )
        {
            $outputFileName = $_.FileName
        }
        $OutputFullFileName = Join-Path $OutputDirectory $outputFileName

        bcp $InputFullTableName out $OutputFullFileName -T -c $ServerOption
    }
1
Aaron Jensen

Je préfère cette variante de la méthode énumérateur avec un pipeline, car vous n'avez pas à vous référer à la table de hachage dans foreach (testé dans PowerShell 5):

$hash = @{
    'a' = 3
    'b' = 2
    'c' = 1
}
$hash.getEnumerator() | foreach {
    Write-Host ("Key = " + $_.key + " and Value = " + $_.value);
}

Sortie:

Key = c and Value = 1
Key = b and Value = 2
Key = a and Value = 3

Maintenant, ceci n'a pas été délibérément trié sur la valeur, l'énumérateur renvoie simplement les objets dans l'ordre inverse.

Mais puisqu'il s'agit d'un pipeline, je peux maintenant trier les objets reçus de l'énumérateur en fonction de la valeur:

$hash.getEnumerator() | sort-object -Property value -Desc | foreach {
  Write-Host ("Key = " + $_.key + " and Value = " + $_.value);
}

Sortie:

Key = a and Value = 3
Key = b and Value = 2
Key = c and Value = 1
0
Eelco L.