powershell - 用于变量名的 PowerShell 调用表达式
问题描述
我正在重构我在工作中需要的一个巨大的脚本,我想在一些需要加载各种 CSV 文件的实用程序函数中使用 splatting。我的问题是获取数据文件的目标。我以前使用过 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:动态创建变量名称;嵌套哈希表
解决方案
我建议的一件事是摆脱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
正确的。
推荐阅读
- python - 如何在python中使用单词激活语音识别
- c++ - 什么是相关流程和不相关流程?
- python - 如何将字符串值绑定到列表中的每个元素
- c - 在C中的单链表末尾插入一个节点
- flutter - 离线flutter sdk升级
- python - 使用正则表达式(python)从文件中提取值的简单方法
- javascript - 反应:无法读取未定义的属性“setState”
- java - 使用 Microsoft Graph API 获取其他用户的 Outlook 任务
- python - 为什么我安装 keras 后出现 ModuleNotFound 错误?
- html - 使用 CSS/HTML 移动幻灯片