54

Occasionally, the cmd shell's inability to expand wildcard paths can really be an inconvenience. I had to pass 100 files in a directory to a program, and couldn't type *.ext. Instead, I used mingw's 'ls' to dump the list to a file, then replaced newlines with spaces, copied and pasted into cmd. Quite a nightmare.

I suspect the answer will be no, but has anyone dealt with this or come up with any way to make this easier?

cemulate
  • 1,199

8 Answers8

26

DOS, and consequently Windows' cmd.exe, never supported wildcard expansion by the shell. It was always up to the application to do the expansion.

This means you will always have to find another route if you're using an application that doesn't support expansion. You could:

  • Use a FOR loop to run some commands against all the files you're interested in
  • Use dir /b > list.txt to use the dir command to perform the expansion and put the list of files into a text file (you could then use something like an Excel formula if you're really desperate to produce a list of commands to paste into cmd, or you could use Excel to transpose the cells so all the filenames are on the same line.)
Malvineous
  • 2,798
13

Just use Powershell, which is preinstalled on Windows 7. Powershell is capable of running cmd commands and does understand wildcards at any place in a path.

To start Powershell just type "powershell" in your start menu search box and hit enter.


In case the application expects a string with all filenames in it this is the right script:

$delimiter = " "
[string]$files = $nothing ; ls *.txt | % { $files += $_.fullname + $delimiter } ; application.exe $files

Change $delimiter = " " to $delimiter = "," if your application expects a comma separated list of file names.

Explanation of code:

  • [string]$files = $nothing - creates an empty variable of type string
  • ; - is a separator for multiple commands, not a pipeline!
  • ls *.txt | % { $files += $_.fullname + $delimiter } - gets a list of all text files and creates a string with all filenames separated by the delimiter
  • application.exe $files - calls the application and passes the file list to it

You can even search for a file pattern recursively by adding -recurse to ls *.txt so the complete code would look like this:

$delimiter = " "
[string]$files = $nothing ; ls *.txt -recurse | % { $files += $_.fullname + $delimiter } ; application.exe $files

Edit:
To avoid irritations, ls and dir are aliases of Get-ChildItem and % is an alias to ForEach-Object. I keep my code with aliases used because it's shorter.

EDIT 2018/04/25:
Back in 2012 I've been fairly new to PowerShell. Of course there is an easier way though it's not as easy as unix/linux' capability of glob expansion:

app.exe $(ls *.txt | % {$_.FullName})

Explanation:

  • $() will evaluate the expression inside first and will replace itself with the output of that expression (much like backticks do in bash)
  • ls *.txt gets FileInfo objects of all files that match the glob *.txt
  • because PowerShell is object oriented, we have to output the fullname of each FileInfo object. Simply naming an attribute/property in PowerShell outputs it by default. % { $_.FileName } does that for us. % loops over all elements returned by ls and outputs each objects FileName.
wullxz
  • 2,876
11

This will give you a list and also put it in variable named expanded_list. Put it in batch file and run with myBatchFile name myPattern. Enclose pattern with quotation marks if it includes spaces. Matches files, no dirs. Run without parameters matches all.

@echo off
set expanded_list=
for /f "tokens=*" %%F in ('dir /b /a:-d "%~1"') do call set expanded_list=%%expanded_list%% "%%F"

echo expanded_list is: 
echo %expanded_list%

You can then run you command with my_command_exec %expanded_list%

WARNING! Maximum size of cmd variable is 8191 characters (XP onwards), so it's possible to overflow it! You cannot count on the utility to always give you complete list. On the other hand, maximum cmd line length is also 8191 so you would not be able to execute it anyway.

wmz
  • 7,358
5

Note, this is in Comment to the posters in a separate thread user619818 and styfle)

DIR can and does allow searching all subdirectories for files matching a specific type.

Note: The Root Directory to check which all subdirectories lie under is assumed to be "C:\My Program\Root" as the author did not give a path name under which these directories are located

DIR /B /S "C:\My Program\Root\*.ext"

this will give the full file paths and filenames of the source files.

if you only want file names you would have to do as follows

FOR /F "Tokens=*" %A IN ('DIR /B /S "C:\My Program\Root\*.ext"') DO @( Echo.%~nxA )

so assuming you want to move all of these files from "C:\My Program\Root\Sub Directories\.ext" to "D:\Singlefolder\.ext" you simply do this:

FOR /F "Tokens=*" %A IN ('DIR /B /S "C:\My Program\Root\*.ext"') DO @( MOVE "%~A" "D:\Singlefolder\%~nxA" /Y )

Hope that helps others in the future. :)

Ben Personick
  • 279
  • 3
  • 6
3

This works in cmd.exe

dir /b *.ext

or

echo | dir /b *.ext
user619818
  • 336
  • 4
  • 6
  • 14
2

This is old, but with the Linux subsystem for windows, its pretty common to have bash in your PATH now. If that is the case, then this might do the trick for you:

bash -c 'cat *.txt'
Richard
  • 209
1

There is a really simple solution to this given by Microsoft: https://docs.microsoft.com/en-us/cpp/c-language/expanding-wildcard-arguments?view=msvc-170

Basically, you just link with one of two libraries that will expand wildcards into the argv[] list fed to your program.

To clarify by example, say you have the code:

// ExampleFileExpansion.cpp : This outputs the cmd line arguments
//

#include <iostream> #include <tchar.h>

using namespace std;

int wmain(int argc, _TCHAR* argv[]) { for (int i = 0; i < argc; i++) { wcout << i << "\t" << argv[i] << endl; } }

Which normally gives output like:

C:\x64\Debug>ExampleFileExpansion *.*

0 ExampleFileExpansion 1 .

Within Visual Studio, navigate to the project properties page and choose the linker input options | Additional Dependencies. Then, add the library (in this case "wsetargv.obj") and apply the change. Then, Build the project again, from the main menu.

After the change, running the same code gives something like:

C:\x64\Debug>ExampleFileExpansion *.*

0 ExampleFileExpansion 1 ExampleF.a660785c.tlog 2 ExampleFileExpansion.exe 3 ExampleFileExpansion.exe.recipe 4 ExampleFileExpansion.ilk 5 ExampleFileExpansion.log 6 ExampleFileExpansion.obj 7 ExampleFileExpansion.pdb 8 ExampleFileExpansion.vcxproj.FileListAbsolute.txt 9 vc143.idb 10 vc143.pdb

Here are some screen shots from VS 2022 to illustrate the steps:

Visual Studio Project Properties Menu

Choose Linker input options, then Edit to get the dialog

Enter the appropriate obj file name either wsetargv.obj or setargv.obj depending on whether wmain or main function is declared

0

Why not for loop to pass the list of files to the program:

for %i in (*.txt) do start notepad %i

Or if you want to pass all files (list) to run one instance of the program (eg. pass a list of files separated by spaces):

set list=.& (for %i in (*.txt) do set list=!list! %i)& start app.exe !list:~2!

In 2nd case, the list variable contains all the files represented by *.txt:

echo !list:~2!

If the program can't detect filenames with spaces, then quote the names:

set list=.& (for %i in (*.txt) do set list=!list! "%i")& start app.exe !list:~2!

Tested in Win CMD

Zimba
  • 1,291