你遇到的Powershell陷阱是什么? :-)
我是:# -----------------------------------
function foo()
{
@("text")
}
# Expected 1, actually 4.
(foo).length
# -----------------------------------
if(@($null, $null))
{
Write-Host "Expected to be here, and I am here."
}
if(@($null))
{
Write-Host "Expected to be here, BUT NEVER EVER."
}
# -----------------------------------
function foo($a)
{
# I thought this is right.
#if($a -eq $null)
#{
# throw "You can't pass $null as argument."
#}
# But actually it should be:
if($null -eq $a)
{
throw "You can't pass $null as argument."
}
}
foo @($null, $null)
# -----------------------------------
# There is try/catch, but no callstack reported.
function foo()
{
bar
}
function bar()
{
throw "test"
}
# Expected:
# At bar() line:XX
# At foo() line:XX
#
# Actually some like this:
# At bar() line:XX
foo
想知道你的身边走路: - )
答案 0 :(得分:13)
我个人最喜欢的是
function foo() {
param ( $param1, $param2 = $(throw "Need a second parameter"))
...
}
foo (1,2)
对于那些不熟悉powershell行的人,因为它不是传递2个参数而是实际创建一个数组并传递一个参数。你必须按如下方式调用它
foo 1 2
答案 1 :(得分:10)
另一个有趣的。默认情况下不处理表达式会将其写入管道。当你没有意识到某个特定函数返回一个值时真的很烦人。
function example() {
param ( $p1 ) {
if ( $p1 ) {
42
}
"done"
}
PS> example $true
42
"done"
答案 2 :(得分:10)
$files = Get-ChildItem . -inc *.extdoesntexist
foreach ($file in $files) {
"$($file.Fullname.substring(2))"
}
失败:
You cannot call a method on a null-valued expression.
At line:3 char:25
+ $file.Fullname.substring <<<< (2)
修复如下:
$files = @(Get-ChildItem . -inc *.extdoesntexist)
foreach ($file in $files) {
"$($file.Fullname.substring(2))"
}
底线是foreach语句将循环标量值,即使该标量值为$ null。当第一个示例中的Get-ChildItem不返回任何内容时,$ files会被赋予$ null。如果您希望命令返回一个项目数组,但有可能只返回1项或零项,请在命令周围放置@()。然后你将总是得到一个数组 - 无论是0,1或N项。注意:如果该项已经是一个放置@()
的数组没有效果 - 它仍然是完全相同的数组(即没有额外的数组包装器)。
答案 3 :(得分:7)
# The pipeline doesn't enumerate hashtables.
$ht = @{"foo" = 1; "bar" = 2}
$ht | measure
# Workaround: call GetEnumerator
$ht.GetEnumerator() | measure
答案 4 :(得分:6)
答案 5 :(得分:3)
假设您有以下XML文件:
<Root>
<Child />
<Child />
</Root>
运行:
PS > $myDoc = [xml](Get-Content $pathToMyDoc)
PS > @($myDoc.SelectNodes("/Root/Child")).Count
2
PS > @($myDoc.Root.Child).Count
2
现在编辑XML文件,使其没有子节点,只有Root节点,然后再次运行这些语句:
PS > $myDoc = [xml](Get-Content $pathToMyDoc)
PS > @($myDoc.SelectNodes("/Root/Child")).Count
0
PS > @($myDoc.Root.Child).Count
1
当你想要使用foreach迭代一组节点时,这是令人讨厌的,当且仅当实际存在时。这就是我学会了你不能使用XML处理程序的属性(点)表示法作为一个简单的快捷方式。我相信正在发生的事情是SelectNodes返回0的集合。当@ ed时,它从XPathNodeList转换为Object [](检查GetType()),但保留了长度。动态生成的$ myDoc.Root.Child属性(基本上不存在)返回$ null。当$ null为'ed时,它变成长度为1的数组。
答案 6 :(得分:3)
$_
或$input
以及begin
,process
和end
块。我在Simple-Talk.com文章Down the Rabbit Hole- A Study in PowerShell Pipelines, Functions, and Parameters中详细讨论了这些要点,并提供了一个附带的wallchart - 这里是一个显示函数服务的各种调用语法缺陷的一瞥3个论点:
我在Simple-Talk.com文章Further Down the Rabbit Hole: PowerShell Modules and Encapsulation中阐述了这些要点。
使用相对路径对脚本内的文件进行点源是相对于当前目录的 - 不是脚本所在的目录!
要相对于脚本,请使用此函数来查找脚本目录: [PowerShell V3 +更新:只需使用内置$PSScriptRoot
变量!]
function Get-ScriptDirectory
{ Split-Path $script:MyInvocation.MyCommand.Path }
模块必须存储为...Modules\name\name.psm1
或...\Modules\any_subpath\name\name.psm1
。也就是说,您不能只使用...Modules\name.psm1
- 模块的直接父级的名称必须与模块的基本名称匹配。此图表显示违反此规则时的各种故障模式:
Simple-Talk.com刚刚发布了关于PowerShell陷阱的三篇最新文章。前两部分是测验的形式,可以帮助您欣赏一组精选的陷阱;最后一部分是一个挂图(虽然它需要一个相当高的天花板的房间),其中包含36个最常见的陷阱(一些改编自本页的答案),为大多数人提供了具体的例子和解决方法。阅读更多here。
答案 7 :(得分:3)
为构建没有使用Powershell构建的实用程序构建命令行有一些技巧:
& 7zip.exe
# Executing a string with a space.
& 'c:\path with spaces\command with spaces.exe'
# Executing a string with a space, after first saving it in a variable.
$a = 'c:\path with spaces\command with spaces.exe'
& $a
C:\Path\utility.exe '/parameter1' 'Value #1' 1234567890
$b = 'string with spaces and special characters (-/&)'
utility.exe $b
$c = @('Value #1', $Value2)
utility.exe $c
# Saving output as a string to a variable.
$output = ping.exe example.com | Out-String
# Piping the output.
ping stackoverflow.com | where { $_ -match '^reply' }
# Using Start-Process affords the most control.
Start-Process -Wait SomeExecutable.com
答案 8 :(得分:3)
这是我最近偶然发现的事情(PowerShell 2.0 CTP):
$items = "item0", "item1", "item2"
$part = ($items | select-string "item0")
$items = ($items | where {$part -notcontains $_})
你觉得$ items在剧本的最后是什么?
我期待“item1”,“item2”,但$ items的值是:“item0”,“item1”,“item2”。
答案 9 :(得分:2)
alex2k8,我想你的这个例子很好说:
# -----------------------------------
function foo($a){
# I thought this is right.
#if($a -eq $null)
#{
# throw "You can't pass $null as argument."
#}
# But actually it should be:
if($null -eq $a)
{
throw "You can't pass $null as argument."
}
}
foo @($null, $null)
PowerShell可以将一些比较器用于这样的数组:
$array -eq $value
## Returns all values in $array that equal $value
考虑到这一点,原始示例返回两个项目(数组中的两个$ null值),它们评估为$ true,因为最终得到了多个项目的集合。反转参数的顺序会停止数组比较。
在某些情况下,此功能非常方便,但您需要注意这一点(就像PowerShell中的数组处理一样)。
答案 10 :(得分:2)
函数'foo'和'bar'看起来相同。
function foo() { $null }
function bar() { }
E.g。
(foo) -eq $null
# True
(bar) -eq $null
# True
可是:
foo | %{ "foo" }
# Prints: foo
bar | %{ "bar" }
# PRINTS NOTHING
返回$ null并且返回任何内容并不等同于处理管道。
这个灵感来自 Keith Hill 示例......
function bar() {}
$list = @(foo)
$list.length
# Prints: 0
# Now let's try the same but with a temporal variable.
$tmp = foo
$list = @($tmp)
$list.length
# Prints: 1
答案 11 :(得分:1)
另一个:
$x = 2
$y = 3
$a,$b = $x,$y*5
由于运算符优先级,$ b中没有25;该命令与($ x,$ y)* 5相同 正确的版本是
$a,$b = $x,($y*5)
答案 12 :(得分:1)
逻辑和按位运算符不遵循标准优先级规则。运算符 - 并且应该具有比-or更高的优先级,但它们是从左到右严格评估的。
例如,比较PowerShell和Python(或几乎任何其他现代语言)之间的逻辑运算符:
# PowerShell
PS> $true -or $false -and $false
False
# Python
>>> True or False and False
True
...和按位运算符:
# PowerShell
PS> 1 -bor 0 -band 0
0
# Python
>>> 1 | 0 & 0
1
答案 13 :(得分:0)
这很有效。但几乎可以肯定的是,你并不认为它的运作方式。
PS> $a = 42;
PS> [scriptblock]$b = { $a }
PS> & $b
42
答案 14 :(得分:0)
# $x is not defined
[70]: $x -lt 0
True
[71]: [int]$x -eq 0
True
那么,什么是$ x ..?
答案 15 :(得分:0)
我最近碰到的另一个:接受管道输入的[string]参数在实践中没有强类型。你可以管道任何东西,PS会通过ToString()强制它。
function Foo
{
[CmdletBinding()]
param (
[parameter(Mandatory=$True, ValueFromPipeline=$True)]
[string] $param
)
process { $param }
}
get-process svchost | Foo
不幸的是没有办法把它关掉。我能想到的最好的解决方法:
function Bar
{
[CmdletBinding()]
param (
[parameter(Mandatory=$True, ValueFromPipeline=$True)]
[object] $param
)
process
{
if ($param -isnot [string]) {
throw "Pass a string you fool!"
}
# rest of function goes here
}
}
编辑 - 一个更好的解决方法我已经开始使用...
将此添加到您的自定义类型XML -
<?xml version="1.0" encoding="utf-8" ?>
<Types>
<Type>
<Name>System.String</Name>
<Members>
<ScriptProperty>
<Name>StringValue</Name>
<GetScriptBlock>
$this
</GetScriptBlock>
</ScriptProperty>
</Members>
</Type>
</Types>
然后编写如下函数:
function Bar
{
[CmdletBinding()]
param (
[parameter(Mandatory=$True, ValueFromPipelineByPropertyName=$True)]
[Alias("StringValue")]
[string] $param
)
process
{
# rest of function goes here
}
}
答案 16 :(得分:0)
之前我使用$ o.SomeProperty将它绊倒了,它应该是$($ o.SomeProperty)。
答案 17 :(得分:0)
忘记$ _被块覆盖让我在混乱中乱了几次,同样对于多个reg-ex匹配和$ matches数组。 &GT;。&LT;
答案 18 :(得分:0)
记住要将导入数据表中的pscustom对象显式地输入为数字,以便正确排序:
$CVAP_WA=foreach ($i in $C){[PSCustomObject]@{ `
County=$i.county; `
TotalVote=[INT]$i.TotalBallots; `
RegVoters=[INT]$i.regvoters; `
Turnout_PCT=($i.TotalBallots/$i.regvoters)*100; `
CVAP=[INT]($B | ? {$_.GeoName -match $i.county}).CVAP_EST }}
PS C:\ Politics&gt; $ CVAP_WA | sort -desc TotalVote | ft -auto -wrap
County TotalVote RegVoters Turnout_PCT CVAP CVAP_TV_PCT CVAP_RV_PCT
------ --------- --------- ----------- ---- ----------- -----------
King 973088 1170638 83.189 1299290 74.893 90.099
Pierce 349377 442985 78.86 554975 62.959 79.837
Snohomish 334354 415504 80.461 478440 69.832 86.81
Spokane 227007 282442 80.346 342060 66.398 82.555
Clark 193102 243155 79.453 284190 67.911 85.52
答案 19 :(得分:0)
我的都与文件复制有关......
文件名中的方括号
我曾经不得不使用Move-Item -Path C:\Source -Destination C:\Dest
移动一个非常大/复杂的文件夹结构。在该过程结束时,源目录中仍有许多文件。我注意到每个剩余的文件名都有方括号。
问题是-Path
参数将方括号视为通配符。
例如。如果要将Log001复制到Log200,可以使用方括号,如下所示:
Move-Item -Path C:\Source\Log[001-200].log
。
就我而言,为避免方括号被解释为通配符,我应该使用-LiteralPath
参数。
ErrorActionPreference
将$ErrorActionPreference
和Move-Item
参数与Copy-Item
一起使用时,-Verbose
变量将被忽略。
答案 20 :(得分:0)
将进程的ExitCode作为布尔值处理。
例如,使用以下代码:
$p = Start-Process foo.exe -NoNewWindow -Wait -PassThru
if ($p.ExitCode) {
# handle error
}
一切正常,除非说foo.exe不存在或无法启动。
在这种情况下,$p
将为$null
,而[bool]($null.ExitCode)
为False。
一个简单的解决方法是将逻辑替换为if ($p.ExitCode -ne 0) {}
,
但是,为使代码imo清晰起见,以下更好:if (($p -eq $null) -or ($p.ExitCode -ne 0)) {}