Вызов-выражение PowerShell для имени переменной - PullRequest
0 голосов
/ 25 апреля 2018

Я выполняю рефакторинг ОГРОМНОГО скрипта, который мне нужен на работе, и я хочу использовать сплаттинг в нескольких служебных функциях, которые должны загружать различные файлы CSV. Моя проблема заключается в получении цели для файла данных. Ранее я использовал параметры REF, но когда я это делаю, это требует больше накладных расходов, потому что я не могу использовать вложенный объект внутри хеш-таблицы в качестве REF, поэтому он должен быть заключен в функцию, и это просто грязно при работе с таким количеством файлов. Я мог бы использовать свойство, перечисляющее имя конечного элемента, а затем жестко закодировать начало переменной ... но это не похоже на элегантное решение.

$m.DataFiles.('LDAP').HashArray = ...

Вот супер сокращенный фрагмент моего сценария:

################################################################################
#############################   SCRIPT SETTINGS    #############################
################################################################################
Set-StrictMode -Version latest;
$Global:ErrorActionPreference = 'Stop'; # Options: 'SilentlyContinue','Continue','Stop','Inquire'
$m = @{
    DataFiles   = @{
        LDAP        = @{
            FilePath        = 'C:\Temp\_PPtoO365_LDAP.csv';     ## Full path and file name: 'C:\Temp\log.csv';
            Label           = 'LdapFile';                       ## If present, then I/O functions are written to log;
            Append          = $False;                           ## Should append vs re-write the file;
            Fatal           = $True;                            ## Should a read/write failure cause immediate script exit;
            Dirty           = $False;                           ## Used internally as a save flag;
            Target          = "`$m.DataFiles.LDAP.HashArray";   ## Name of variable for Load-HashArray();
            DelimeterCSV    = ',';                              ## CSV [column] delimeter;
            DelimeterArray  = ';';                              ## Array delimeter (within a [column]);
            HashArray       = @();                              ## Array of HashTable(s);
            Template        = [Ordered]@{                       ## HashTable template for loading/saving;
                SamAccountName              = '';
                MailNickname                = '';
                Mail                        = '';
                Primaries                   = @();
                Aliases                     = @();
            };
        };
    };
    Settings    = @{
        SaveExamples    = $False;
        SaveDebug       = $False;
    };
};    

################################################################################
##############################        MAIN        ##############################
################################################################################

Function Do-Main() {
    Write-Output ([string]::Format("[{0}] 'LDAP' records; Before.",$m.DataFiles.LDAP.HashArray.Length.ToString('#,##0')));
    $params = $m.DataFiles.LDAP;
    Load-HashArray1 @params;
    Write-Output ([string]::Format("[{0}] 'LDAP' records; After Load-HashArray1().",$m.DataFiles.LDAP.HashArray.Length.ToString('#,##0')));
    $m.DataFiles.LDAP.HashArray = @(); ## Reset
    Write-Output ([string]::Format("[{0}] 'LDAP' records; After Reset.",$m.DataFiles.LDAP.HashArray.Length.ToString('#,##0')));
    Load-HashArray2 @params;
    Write-Output ([string]::Format("[{0}] 'LDAP' records; After Load-HashArray2().",$m.DataFiles.LDAP.HashArray.Length.ToString('#,##0')));
    Set-Close;
    Return;
}

Function Set-Close() {
    ## Script Cleanup - reduce HEAP ##
    $m.Clear();
    Exit(0);
}

################################################################################
##############################     Functions      ##############################
################################################################################


################################################################################
############################## Utility Functions  ##############################
################################################################################

##### Function: Loads a CSV file #####
## Will return one of four types of collections:
##  1. If Target is @(), and no Template: Object[PSCustomObject] (default)
##  2. If Target is @(), and    Template: Object[HashTable] 
##  3. If Target is @{}, and no Template: HashTable [Ordered]@{index=PSCustomObject}
##  4. If Target is @{}, and    Template: HashTable [Ordered]@{index=HashTable}
Function Load-HashArray1() {
    Param (
        [parameter(Mandatory=$True)][String]                                            $FilePath,                  ## Full path and file name: 'C:\Temp\log.csv';
        [parameter(Mandatory=$True)][String]                                            $Label,                     ## If present, then I/O functions are written to log;
        [parameter(Mandatory=$False)][Switch]                                           $Append         = $False,   ## Should append vs re-write the file;
        [parameter(Mandatory=$False)][Switch]                                           $Fatal          = $False,   ## Should a read/write failure cause immediate script exit;
        [parameter(Mandatory=$False)][Switch]                                           $Dirty          = $False,   ## Used internally as a save flag;
        [parameter(Mandatory=$True)][String]                                            $Target,                    ## Name of variable to hold the HashArray;
        [parameter(Mandatory=$False)][String]                                           $DelimeterCSV   = ',',      ## CSV [column] delimeter;
        [parameter(Mandatory=$False)][String]                                           $DelimeterArray = ';',      ## Array delimeter (within a [column]);
        [parameter(Mandatory=$False)][Object[]]                                         $HashArray      = @(),      ## Not used in this function;
        [parameter(Mandatory=$False)][System.Collections.Specialized.OrderedDictionary] $Template       = @{}       ## HashTable template for loading/saving;
    )
Write-Output "<<Debug1>> `$Target [$Target]... Expecting [`$m.DataFiles.LDAP.HashArray]";
    $f = @{
        Data        = @($Template,$Template,$Template);
        Target      = $m.DataFiles.LDAP.HashArray;
        TargetType  = '';
    };
Write-Output "<<Debug2>> `$f.Target [$($f.Target)]... Expecting []";
    $f.TargetType   = $f.Target.GetType().Name;
Write-Output "<<Debug3>> `$f.TargetType [$($f.TargetType)]... Expecting [Object[]]";
    $Null = ($m.DataFiles.LDAP.HashArray = $f.Data);
    Return;
}

Function Load-HashArray2() {
    Param (
        [parameter(Mandatory=$True)][String]                                            $FilePath,                  ## Full path and file name: 'C:\Temp\log.csv';
        [parameter(Mandatory=$True)][String]                                            $Label,                     ## If present, then I/O functions are written to log;
        [parameter(Mandatory=$False)][Switch]                                           $Append         = $False,   ## Should append vs re-write the file;
        [parameter(Mandatory=$False)][Switch]                                           $Fatal          = $False,   ## Should a read/write failure cause immediate script exit;
        [parameter(Mandatory=$False)][Switch]                                           $Dirty          = $False,   ## Used internally as a save flag;
        [parameter(Mandatory=$True)][String]                                            $Target,                    ## Name of variable to hold the HashArray;
        [parameter(Mandatory=$False)][String]                                           $DelimeterCSV   = ',',      ## CSV [column] delimeter;
        [parameter(Mandatory=$False)][String]                                           $DelimeterArray = ';',      ## Array delimeter (within a [column]);
        [parameter(Mandatory=$False)][Object[]]                                         $HashArray      = @(),      ## Not used in this function;
        [parameter(Mandatory=$False)][System.Collections.Specialized.OrderedDictionary] $Template       = @{}       ## HashTable template for loading/saving;
    )
Write-Output "<<Debug4>> `$Target [$Target]... Expecting [`$m.DataFiles.LDAP.HashArray]";
    $f = @{
        Data        = @($Template,$Template,$Template);
        Target      = Invoke-Expression ($Target);
        TargetType  = '';
    };
Write-Output "<<Debug5>> `$f.Target [$($f.Target)]... Expecting []";
    $f.TargetType   = $f.Target.GetType().Name;
Write-Output "<<Debug6>> `$f.TargetType [$($f.TargetType)]... Expecting [Object[] or HashTable]";
    $Null = Invoke-Expression ($Target = $f.Data);
    #$f.Target = $f.Data;
    Return;
}

################################################################################
##############################    Script Entry    ##############################
################################################################################
Do-Main;

При выполнении возвращается:

[0] 'LDAP' records; Before.
<<Debug1>> $Target [$m.DataFiles.LDAP.HashArray]... Expecting [$m.DataFiles.LDAP.HashArray]
<<Debug2>> $f.Target []... Expecting []
<<Debug3>> $f.TargetType [Object[]]... Expecting [Object[]]
[3] 'LDAP' records; After Load-HashArray1().
[0] 'LDAP' records; After Reset.
<<Debug4>> $Target [$m.DataFiles.LDAP.HashArray]... Expecting [$m.DataFiles.LDAP.HashArray]
<<Debug5>> $f.Target []... Expecting []
Load-HashArray2 : You cannot call a method on a null-valued expression.
At C:\Temp\__Test.ps1:44 char:9
+         Load-HashArray2 @params;
+         ~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [Load-HashArray2], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull,Load-HashArray2

Когда я закомментирую строку сразу после «Debug5», я получаю эту ошибку:

<<Debug5>> $f.Target []... Expecting []
<<Debug6>> $f.TargetType []... Expecting [Object[] or HashTable]
Invoke-Expression : The term 'System.Collections.Specialized.OrderedDictionary' is not recognized as the name of a
cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify
that the path is correct and try again.
At C:\Temp\__Test.ps1:119 char:17
+         $Null = Invoke-Expression ($Target = $f.Data);
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (System.Collecti...deredDictionary:String) [Invoke-Expression], CommandN
   otFoundException
    + FullyQualifiedErrorId : CommandNotFoundException,Microsoft.PowerShell.Commands.InvokeExpressionCommand

Я использую этот блог для вдохновения (шаг 11): Powershell: динамическое создание имен переменных; Вложенные Hashtables

1 Ответ

0 голосов
/ 25 апреля 2018

Единственное, что я хотел бы предложить, это избавиться от Invoke-Expression и использовать вместо этого блоки сценариев с оператором вызова &.Таким образом, это

Target          = "`$m.DataFiles.LDAP.HashArray"

становится этим

Target          = { $m.DataFiles.LDAP.HashArray } # no quoting issues

, а при вызове

Target      = Invoke-Expression ($Target)

просто становится

Target  = & $Target

причиной, по которой вы 'Здесь появляется ошибка

$Null = Invoke-Expression ($Target = $f.Data)

, потому что $f.Data - упорядоченный словарь.Invoke-Expression оценивает ToString() своих аргументов, а ToString() коллекции является именем типа коллекций.Избавление от Invoke-Expression должно облегчить отладку вашего кода.(ПРИМЕЧАНИЕ: в общем, использование Invoke-Expression почти всегда неправильно и имеет возможные последствия для безопасности; см. Выражение вызова считается вредным )

Плюс некоторые незначительные комментарии:

Во-первых, это

$Null = ($m.DataFiles.LDAP.HashArray = $f.Data);

должно быть просто

$m.DataFiles.LDAP.HashArray = $f.Data

, поскольку присваивания в виде операторов не возвращают значений.

Во-вторых, вы не должны использовать круглые скобки при вызове команд, как в

Target      = Invoke-Expression ($Target);

, потому что это может заставить людей думать, что им нужно сделать Copy-Item("from", "to"), что неправильно вместо Copy-Item from to что правильно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...