Home > other >  Globbing patterns in windows command prompt/ powershell
Globbing patterns in windows command prompt/ powershell

Time:06-02

I would like to know if there is any way to achieve this behavior on windows, for example:

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

CodePudding user response:

In PowerShell you can use Resolve-Path which Resolves the wildcard characters in a path, and displays the path contents.

Example: I want to locate signtool.exe from the Windows SDK which typically resides in "c:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64\signtool.exe" where there could be any other version(s) installed.

So I could use: Resolve-Path 'c:\program*\Windows Kits\10\bin\*\x64\signtool.exe'

EDIT:

If you want to execute it directly you can use the & invocation operator e.g.

&(Resolve-Path 'c:\wind?ws\n?tepad.exe')

CodePudding user response:

  • With limited exceptions in PowerShell, on Windows there is no support for shell-level globbing - target commands must perform their own resolution of wildcard patterns to matching filenames; if they don't, globbing must be performed manually and the results passed as literal paths; see the bottom section for background information.

  • PowerShell:

    • Perhaps surprisingly, you can invoke an executable by wildcard pattern, as zett42 points out, though that behavior is problematic (see bottom section):

      # Surprisingly DOES find C:\Windows\System32\attrib.exe
      # and invokes it.
      C:\Windows\System3?\attr*.exe /?
      
      • Generally, you can discover commands, including external programs, via the Get-Command cmdlet.
    • Many file-processing cmdlets in PowerShell do perform their own globbing (e.g., Get-ChildItem, Remove-Item); if you're calling commands that do not, notably external programs that don't, you must perform globbing manually, up front, except on Unix-like platforms when calling _external programs, where PowerShell does perform automatic globbbing (see bottom section):

      • Use Convert-Path to get the full, file-system-native paths of matching files or directories.

        • While Resolve-Path may work too, it returns objects whose .ProviderPath property you need to access to get the same information (stringifying these objects, as happens implicitly when you pass them to external programs, yields their .Path property, which may be based on PowerShell-only drives that external programs and .NET APIs know nothing about.)
      • For more control over what is matched, use Get-ChildItem and access the result objects' .Name or .FullName property, as needed; for instance, Get-ChildItem allows you to limit matching to files (-File) or directories (-Directory) only.

      • PowerShell makes it easy to use the results of manually performed globbing programmatically; the following example passes the full paths of all *.txt files in the current directory to the cmd.exe's echo command as individual arguments; PowerShell automatically encloses paths with spaces in "...", if needed:

         cmd /c echo (Get-ChildItem -Filter *.txt).FullName
        
      • Generally, note that PowerShell's wildcard patterns are more powerful than those of the host platform's file-system APIs, and notably include support for character sets (e.g. [ab]) and ranges (e.g. [0-9]); another important difference is that ? matches exactly one character, whereas the native file-system APIs on Windows match none or one.

        • However, when using the -Filter parameter of file-processing cmdlets such as Get-ChildItem, the host platform's patterns are used, which - while limiting features - improves performance; a caveat is that on Unix-like platforms ? then seemingly acts like on Windows, i.e causing it to match none or one character.
  • cmd.exe (Command Prompt, the legacy shell):

    • cmd.exe does not support calling executables by wildcard pattern; some of cmd.exe's internal commands (e.g., dir and del) and some standard external programs (e.g., attrib.exe) do perform their own globbing; otherwise you must perform globbing manually, up front:

      • where.exe, the external program for discovering external programs fundamentally only supports wildcard patterns in executable names (e.g. where find*.exe), not in paths, which limits wildcard-based lookups to executables located in directories listed in the PATH environment variable.

        :: 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
        
      • Globbing via dir appears to be limited to wildcard characters in the last path component:

        :: 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*
        
      • Using manual globbing results programmatically is quite cumbersome in cmd.exe and requires use of for statements (whose wildcard matching has the same limitations as the dir command); for example, using the syntax for batch files (.cmd or .bat files):

        • To use the resolved executable file path for invocation (assuming only one file matches):

          @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%" /?             
          
        • To pass matching filenames as multiple arguments to a single command:

          @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%
          

Background information:

  • On Unix-like platforms, POSIX-compatible shells such as Bash themselves perform globbing (resolving filename wildcard patterns to matching filenames), before the target command sees the resulting filenames, as part of a feature set called shell expansions (link is to the Bash manual).

  • On Windows, cmd.exe (the legacy shell also known as Command Prompt) does NOT perform such expansions and PowerShell mostly does NOT.

    • That is, it is generally up to each target command to interpret wildcard patterns as such and resolve them to matching filenames.

      • That said, in PowerShell, many built-in commands, known as cmdlets, do support PowerShell's wildcard patterns, notably via the -Path parameter of provider cmdlets, such as Get-ChildItem.

      • Additionally and more generally, cmdlet parameters that represent names often support wildcards too; e.g., Get-Process exp* lists all processes whose image name start with exp, such as explorer.

      • Note that the absence of Unix-style shell expansions on Windows also implies that no semantic distinction is made between unquoted and quoted arguments (e.g., *.txt vs. "*.txt"): a target command generally sees both as verbatim *.txt.

    • In PowerShell, automatic globbing DOES occur in these limited cases:

      • Perhaps surprisingly, an executable file path can be invoked via a wildcard pattern:

        • as-is, if the pattern isn't enclosed in '...' or "..." and/or contains no variable references or expressions; e.g.:

           C:\Windows\System3?\attri?.exe
          
        • via &, the call operator, otherwise; e.g.:

           & $env:SystemRoot\System32\attri?.exe
          
        • However, this feature is of questionable utility - When would you not want to know up front what specific executable you're invoking? - and it is unclear whether it was implemented by design, given that inappropriate wildcard processing surfaces in other contexts too - see GitHub issue #4726.

          • Additionally, up to at least PowerShell 7.2.4, if two or more executables match the wildcard pattern, a misleading error occurs, suggesting that no matching executable was found - see GitHub issue #17468; a variation of the problem also affects passing a wildcard-based path (as opposed to a mere name) that matches multiple executables to Get-Command.

          • In POSIX-compatible shells, the multi-match scenario is handled differently, but is equally useless: the first matching executable is invoked, and all others are passed as its arguments.

      • On Unix-like platforms only, PowerShell emulates the globbing feature of POSIX-compatible shells when calling external programs, in an effort to behave more like the platform-native shells; if PowerShell didn't do that, something as simple as ls *.txt would fail, given that the external /bin/ls utility would then receive verbatim *.txt as its argument.

        • However, this emulation has limitations, as of PowerShell 7.2.4:
          • The inability to use wildcard patterns that contain spaces - see GitHub issue #10683.
          • The inability to include hidden files - see GitHub issue #4683.
          • A still experimental feature, available in preview versions of 7.3, PSNativePSPathResolution, automatically translates wildcard patterns based on PowerShell-only drives to their underlying native file-system paths; however, this feature is currently overzealous - see GitHub issue #13640 - and inherently bears the risk of false positives - see GitHub issue #13644
  • Related