PowerShell Invoke-Expression用于变量名称

时间:2018-04-25 01:50:13

标签: powershell

我正在重构我工作中需要的巨大脚本,并且我想在一些需要加载各种CSV文件的实用程序函数中使用splatting。我的问题是获取数据文件的目标。我之前使用过REF参数,但是当我这样做时需要更多的开销,因为我不能在哈希表中使用嵌套对象作为REF,所以它必须包含在一个函数中,而且当它处理时很麻烦处理这么多文件。我可以使用列出最终元素名称的属性然后硬编码变量的开头......但这似乎不是一个优雅的解决方案。

self.charset

这是我脚本的超缩略摘录:

$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;

当我在&#34; Debug5&#34;之后注释掉该行时,我收到此错误:

[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

我使用此博客作为灵感(步骤11):Powershell: Dynamically Creating Variable Names; Nested Hashtables

1 个答案:

答案 0 :(得分:2)

我建议的一件事就是摆脱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几乎总是错误的做法,并可能带来安全隐患;请参阅Invoke-Expression Considered Harmful

加上一些小评论:

首先,这个

$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这是正确的。