powershell,当有2个以上的值时,从[string[]]参数中删除前导零

zfycwa2u  于 12个月前  发布在  Shell
关注(0)|答案(1)|浏览(127)

我有一个接受电话号码列表作为参数的小程序。一些电话号码的第一个字符是0。当我用一个数字调用函数时,字符串保持不变。当我用2+数字调用函数时,前导零被修剪

function user {
    [CmdletBinding()]
    param (
        [Parameter(mandatory,position=0)]
        [string[]]
        $numbers
    )

    process {

        $numbers
        $users = @{
            '0600000001' = 'user1'
            '0600000002' = 'user2'
        }

        foreach($key in $users.keys) {
            if($numbers -contains $key) {
                write-host $users[$key]
            }
        }
    }
}

字符串
例如,在一个实施例中,

[PS] D:\> user 0600000001
0600000001
user1

[PS] D:\> user 0600000002
0600000002
user2


[PS] D:> user 0600000001,0600000002
600000001
600000002


当然,如果我在数字周围加上引号,它会像预期的那样工作

[PS] D:\> user '0600000001','0600000002'
0600000001
0600000002
user2
user1


但是这个小工具不适合我的使用,用户非常喜欢忘记在参数周围加上引号。
我在谷歌上搜索了一下,发现了很多关于PS篡改前导零字符串的帖子/文章,但没有找到如何防止它的解决方案。
你有什么线索吗

编辑

该真实的小字符串具有关于电话号码的模式验证,并且被修剪的前导零中断该验证小字符串

function user {
    [CmdletBinding()]
    param (
        [Parameter(mandatory,position=0)]
        [ValidatePattern('^(\+33|0[67])\d{8}|\+(?!33)\d+$')]
        [string[]]
        $numbers
    )
    .
    .
    .
}

ql3eal8s

ql3eal8s1#

我认为你观察到的行为是**一个 bug,它已经在GitHub issue #20717中报告过了:

  • 带 * 标量 * 参数(单个值)在参数(解析)模式下,当PowerShell的参数绑定器最初将它 * 可以 * 解析 * 的东西解析为数字文字 * 时,它会这样做,即将参数转换为 * 数字类型 *,如[int],同时 * 还保留原始字符串表示 *,以防目标命令最终 not 期待一个数字。
  • 例如06000000012.30xA1e2,甚至1l1d(数字类型后缀)
  • 但是,与表达式模式不同,以+-开头的标记 * 不会 * 被解析为数字,并且仍然是 * 字符串 *([string])。
  • 此行为通过将结果 number 示例(例如类型[int]) Package 在一个 * 不可见的[psobject] Package 器 * 中来实现,该 Package 器缓存原始字符串表示;后者可以通过调用.psobject.ToString()来检索-但 * 不是 * 使用.ToString()和 * 不是 * 通过可扩展字符串("...")中的字符串插值来检索。
  • .psobject.ToString()在以下场景中被 * 隐式 * 调用:
  • 当调用 external programs 时,因为传递 original 字符串表示很重要,因为在那个上下文中没有 types 的概念,所以将原始参数(如0xA)转换为10是不合适的。
  • 在默认的显示格式化期间。
  • 转换为[string]
  • 在一个 array 参数中,这个原始的字符串表示意外地 * 丢失 * 了它的 * 元素 *,即使没有很好的理由 * 不 * 对每个元素执行相同的 Package (这在技术上是可能的,因为PowerShell数组是[object[]]类型的,即它的元素可以存储 * 任何类型 * 的值)。
  • 请注意,在user 0600000001,0600000002调用中,0600000001,0600000002 * 不是 * 在 * 表达式(解析)模式 * 下解析的-它仍然是在 * 参数模式 * 下解析的,其中, * 也 * 支持数组构造(除了调用 * 外部程序 * 时,其中不存在数组的概念),* 元素 * 的解析方式与 * 单个 * 参数模式参数相同。
  • 例如,Write-Output foo, bar是全参数模式的等价物,

Write-Output ('foo', 'bar'),即带有 * 嵌套表达式 * 的参数模式语句。
为了简洁地演示问题

& {
  param($Value) # implicitly [object]-typed - see below for why
  foreach ($v in $Value) { 
    [pscustomobject] @{
      StringRepresentation = $v.ToString()
      OriginalStringRepresentation = $v.psobject.ToString()
      Type = $v.GetType()
      IsPSObjectWrapped = $Value -is [psobject]
    }
 }
} 042

字符串
这将输出以下内容,显示参数042被解析为值为42[int],但 Package 在具有缓存的原始字符串表示的[psobject]示例中:

StringRepresentation OriginalStringRepresentation Type         IsPSObjectWrapped
-------------------- ---------------------------- ----         -----------------
42                   042                          System.Int32              True


转换到这样的参数的 * 数组 * 表明这种 Package 是 * 丢失 *:
传递042, 043, +44, -45而不是042得到:

StringRepresentation OriginalStringRepresentation Type          IsPSObjectWrapped
-------------------- ---------------------------- ----          -----------------
42                   42                           System.Int32              False
43                   43                           System.Int32              False
+44                  +44                          System.String             False
-45                  -45                          System.String             False


注意042043的原始字符串表示是如何丢失的,而+44-45-由于它们的前缀-被解析为开始的 * 字符串 *。
注意事项:

  • 示例脚本块使用了一个 untyped,即隐式[object]类型的参数,它允许在没有显式类型转换的情况下检查行为。
  • 如果参数被声明为[object[]](又名[Array]),即使在 single-argument的情况下,原始的字符串表示也会丢失,这本身可以被认为是一个bug。
  • 对于[string[]]类型的参数,如您的问题所示,它确实适用于 * 单 * 参数情况,但在多参数情况下失败,因为在构建[string[]]数组时,原始字符串表示已经丢失,因为丢失发生在初始的幕后,必然是基于[object]的数组解析。

相关问题