Windows 命令提示符/ powershell 中的通配模式

jag*_*ghy 2 windows terminal powershell glob command-prompt

我想知道是否有任何方法可以在Windows上实现这种行为,例如:

/b?n/ca? /etc/pa??wd -> 执行 'cat /etc/passwd'

mkl*_*nt0 6

  • 除了 PowerShell 中的有限例外之外,Windows 上不支持shell通配符 -目标命令本身必须执行通配符模式解析以匹配文件名;如果不这样做,则必须预先手动执行通配符,并将结果作为文字路径传递;请参阅底部部分了解背景信息。

  • 电源外壳

    • 也许令人惊讶的是,您可以通过通配符模式调用可执行文件,正如zett42指出的那样,尽管这种行为是有问题的(请参见底部部分):

      # Surprisingly DOES find C:\Windows\System32\attrib.exe
      # and invokes it.
      C:\Windows\System3?\attr*.exe /?
      
      Run Code Online (Sandbox Code Playgroud)
      • 通常,您可以通过 cmdlet 发现命令,包括外部程序Get-Command
    • PowerShell 中的许多文件处理 cmdlet确实执行自己的通配符(例如,Get-ChildItemRemove-Item);如果您正在调用不支持的命令,特别是不支持的外部程序,则必须预先手动执行通配符,除非在类Unix平台上调用 _external 程序时,PowerShell执行自动通配符(请参阅底部部分):

      • 用于Convert-Path获取匹配文件或目录的完整文件系统本机路径。

        • 虽然Resolve-Path也可以工作,但它返回您需要访问其属性以获得相同信息的对象(将这些对象字符串化,就像您将它们传递给外部程序时隐式发生的那样,产生它们的属性,这可能基于仅 PowerShell驱动器程序和 .NET API 一无所知。).ProviderPath.Path
      • 为了更好地控制匹配的内容,请根据需要使用Get-ChildItem和访问结果对象.Name.FullName属性;例如,Get-ChildItem允许您限制仅匹配文件( -File) 或目录( -Directory)。

      • PowerShell 可以轻松地以编程方式使用手动执行的通配符的结果;以下示例将*.txt当前目录中所有文件的完整路径作为单独的参数传递给cmd.exe's命令;echo如果需要, PowerShell 会自动将路径用空格括起来"..."

         cmd /c echo (Get-ChildItem -Filter *.txt).FullName
        
        Run Code Online (Sandbox Code Playgroud)
      • 一般来说,请注意,PowerShell 的通配符模式比主机平台的文件系统 API 的通配符模式更强大,特别是包括对字符集(例如[ab])和范围(例如[0-9])的支持;另一个重要的区别是精确?匹配一个字符,而 Windows 上的本机文件系统 API不匹配任何字符或匹配一个.

        • 但是,当使用-Filter文件处理 cmdlet 的参数(例如 )时Get-ChildItem,将使用主机平台的模式,这在限制功能的同时提高了性能;需要注意的是,在类 Unix 平台上,?其行为看起来就像在 Windows 上一样,即导致它不匹配任何字符或匹配一个字符。
  • cmd.exe(命令提示符,旧 shell)

    • cmd.exe支持通过通配符模式 调用可执行文件;一些cmd.exe内部命令(例如,dirdel)和一些标准外部程序(例如,attrib.exe确实执行自己的通配;否则,您必须预先手动执行通配符

      • where.exe,用于发现外部程序的外部程序基本上只支持可执行文件名称中的通配符模式(例如where find*.exe),而不支持路径中的通配符模式,这将基于通配符的查找限制为位于PATH环境变量中列出的目录中的可执行文件。

        :: OK - "*" is part of a *name* only
        where.exe find*.exe
        
        :: !! FAILS: "*" or "?" must not be part of a *path*
        :: !! -> "ERROR: Invalid pattern is specified in "path:pattern"."
        where.exe C:\Windows\System32\find*.exe
        
        Run Code Online (Sandbox Code Playgroud)
      • 通配符dir似乎仅限于最后一个路径组件中的通配符:

        :: OK - "*" is only in the *last* path component.
        dir C:\Windows\System32\attri*
        
        :: !! FAILS: "*" or "?" must not occur in *non-terminal* components.
        :: !! -> "The filename, directory name, or volume label syntax is incorrect."
        dir C:\Windows\System3?\attri*
        
        Run Code Online (Sandbox Code Playgroud)
      • 以编程方式使用手动通配符结果非常麻烦cmd.exe并且需要使用for语句(其通配符匹配具有与命令相同的限制dir);例如,使用批处理文件.cmd或多个.bat文件)的语法:

        • 要使用解析的可执行文件路径进行调用(假设只有一个文件匹配):

          @echo off
          setlocal
          
          :: Use a `for` loop over a wildcard pattern to enumerate
          :: the matching filenames - assumed to be just *one* in this case,
          :: namely attrib.exe, and save it in a variable.
          for %%f in (C:\Windows\System32\attr*.exe) do set "Exe=%%f"
          
          :: Execute the resolved filename to show its command-line help.
          "%Exe%" /?             
          
          Run Code Online (Sandbox Code Playgroud)
        • 要将匹配的文件名作为多个参数传递给单个命令:

          @echo off
          setlocal enableDelayedExpansion
          
          :: Use a `for` loop over a wildcard pattern to enumerate
          :: matching filenames and collect them in a single variable.
          set files=
          for %%f in (*.txt) do set files=!files! "%%f"
          
          :: Pass all matching filenames to `echo` in this example.
          echo %files%
          
          Run Code Online (Sandbox Code Playgroud)

背景资料:

  • 在类Unix平台上,POSIX 兼容的 shell(例如 Bash)本身会在目标命令看到结果文件名之前执行通配符(将文件名通配符模式解析为匹配的文件名) ,作为称为shell 扩展的功能集的一部分(链接是 Bash手动的)。

  • Windows上(cmd.exe旧版 shell 也称为命令提示符)不会执行此类扩展,而 PowerShell大多数情况下不会执行此类扩展。

    • 也就是说,通常由每个目标命令来解释通配符模式并将其解析为匹配的文件名。

      • 也就是说,在 PowerShell 中,许多内置命令(称为cmdlet确实支持 PowerShell 的通配符模式,特别是通过提供程序-Pathcmdlet的参数,例如.Get-ChildItem

      • 此外,更普遍的是,表示名称的cmdlet 参数通常也支持通配符;例如,Get-Process exp*列出所有映像名称以 开头的进程exp,例如explorer

      • 请注意,Windows 上缺乏 Unix 风格的 shell 扩展也意味着未加引号的参数和带引号的参数之间没有语义区别(例如,与):目标命令通常将两者视为逐字的*.txt"*.txt" *.txt

    • PowerShell中,自动通配确实会在以下有限情况下发生:

      • 也许令人惊讶的是,可以通过通配符模式调用可执行文件路径

        • 按原样,如果模式未包含在'...'or中"..."和/或不包含变量引用或表达式;例如:

           C:\Windows\System3?\attri?.exe
          
          Run Code Online (Sandbox Code Playgroud)
        • via &呼叫操作员,否则;例如:

           & $env:SystemRoot\System32\attri?.exe
          
          Run Code Online (Sandbox Code Playgroud)
        • 但是,此功能的实用性值得怀疑- 您什么时候不想预先知道您正在调用什么特定的可执行文件?-鉴于在其他上下文中也存在不适当的通配符处理表面,目前尚不清楚它是否是通过设计实现的 - 请参阅GitHub 问题 #4726

          • 此外,至少在 PowerShell 7.2.4 之前,如果两个或多个可执行文件与通配符模式匹配,则会出现误导性错误,表明找到匹配的可执行文件 - 请参阅GitHub 问题 #17468;该问题的一个变体还会影响将与多个可执行文件匹配的基于通配符的路径(而不是单纯的名称Get-Command)传递到.

          • 在 POSIX 兼容的 shell 中,多重匹配场景的处理方式不同,但同样无用:调用第一个匹配的可执行文件,所有其他匹配的可执行文件都作为其参数传递。

      • 仅在类Unix平台上,PowerShell在调用外部程序时模拟POSIX 兼容 shell 的通配功能,以努力表现得更像平台本机 shell;如果 PowerShell 不这样做,那么简单的事情ls *.txt就会失败,因为外部实用程序将逐字/bin/ls接收作为其参数。 *.txt

        • 但是,从 PowerShell 7.2.4 开始, 此模拟有局限性: