Windows批处理脚本中的文件/文件夹select器对话框
通常,要求用户向批处理脚本提供文件名是一件杂乱的事情,不需要拼写错误,空格的path引号等等。 不幸的是,用户的准确性不是很出名。 在input文件位置直到运行时才知道的情况下,使用GUI进行文件selectinput可降低用户错误的可能性。
有没有办法调用File... Open
从Windows批处理脚本File... Open
样式gui文件select器或文件夹select器?
如果脚本用户安装了PowerShell或.NET,则可以。 请参阅下面的答案。
我也有兴趣看看其他人可以提供什么解决scheme。
文件浏览器
更新2016.3.20:
由于PowerShell是当今几乎所有现代Windows安装的本地组件,因此我声明C#后备已不再需要。 如果你仍然需要它的Vista或XP兼容性,我把它移到一个新的答案 。 从这个编辑开始,我将脚本重写为Batch + PowerShell混合,并且可以执行多重select。 阅读并根据需要进行调整非常容易。
<# : chooser.bat :: launches a File... Open sort of file chooser and outputs choice(s) to the console :: https://stackoverflow.com/a/15885133/1683264 @echo off setlocal for /f "delims=" %%I in ('powershell -noprofile "iex (${%~f0} | out-string)"') do ( echo You chose %%~I ) goto :EOF : end Batch portion / begin PowerShell hybrid chimera #> Add-Type -AssemblyName System.Windows.Forms $f = new-object Windows.Forms.OpenFileDialog $f.InitialDirectory = pwd $f.Filter = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*" $f.ShowHelp = $true $f.Multiselect = $true [void]$f.ShowDialog() if ($f.Multiselect) { $f.FileNames } else { $f.FileName }
这会导致文件select器对话框。
select输出的结果You chose C:\Users\me\Desktop\tmp.txt
到控制台。 如果要强制单个文件select,只需将$f.Multiselect
属性更改为$false
。
(PowerShell命令无情地从Just Tinkering Blog中抽取 )。请参阅OpenFileDialog Class文档以获取可设置的其他属性,例如Title
和InitialDirectory
。
文件夹浏览器
更新2015.08.10:
由于已经有一个调用文件夹select器的COM方法,所以构build一个可以打开文件夹select器并输出path的PowerShell非常简单。
:: fchooser.bat :: launches a folder chooser and outputs choice to the console :: https://stackoverflow.com/a/15885133/1683264 @echo off setlocal set "psCommand="(new-object -COM 'Shell.Application')^ .BrowseForFolder(0,'Please choose a folder.',0,0).self.path"" for /f "usebackq delims=" %%I in (`powershell %psCommand%`) do set "folder=%%I" setlocal enabledelayedexpansion echo You chose !folder! endlocal
在BrowseForFolder()
方法中,第四个参数指定层次的根。 有关有效值的列表,请参阅ShellSpecialFolderConstants 。
这会导致文件夹select器对话框。
select输出的结果You chose C:\Users\me\Desktop
到控制台。
有关可以设置的其他属性,请参阅FolderBrowserDialog类文档,例如RootFolder
。 如果需要,我的原始.NET System.Windows.Forms
PowerShell和C#解决scheme可以在此答案的修订版本4中find,但此COM方法更容易阅读和维护。
Windows脚本主机
文件select
Windows XP有一个神秘的UserAccounts.CommonDialog
WSH对象,它允许VBScript和JScript启动文件select提示。 显然,这被认为是安全风险,并在Vista中删除。
文件夹select
但是,WSH Shell.Application对象的BrowseForFolder方法仍然允许创build文件夹select对话框。 这是一个混合批处理+ JScript示例。 用.bat
扩展名保存。
@if (@a==@b) @end /* :: fchooser2.bat :: batch portion @echo off setlocal for /f "delims=" %%I in ('cscript /nologo /e:jscript "%~f0"') do ( echo You chose %%I ) goto :EOF :: JScript portion */ var shl = new ActiveXObject("Shell.Application"); var folder = shl.BrowseForFolder(0, "Please choose a folder.", 0, 0x00); WSH.Echo(folder ? folder.self.path : '');
在BrowseForFolder()
方法中,第四个参数指定层次的根。 有关有效值的列表,请参阅ShellSpecialFolderConstants 。
这应该从XP向上工作,并不需要一个hibrid文件,它只是用一个很长的命令行运行mshta:
@echo off set dialog="about:<input type=file id=FILE><script>FILE.click();new ActiveXObject set dialog=%dialog%('Scripting.FileSystemObject').GetStandardStream(1).WriteLine(FILE.value); set dialog=%dialog%close();resizeTo(0,0);</script>" for /f "tokens=* delims=" %%p in ('mshta.exe %dialog%') do set "file=%%p" echo selected file is : "%file%" pause
文件/文件夹select可以用纯批处理完成,如下所示。 当然,感觉和外观并不像GUI那么愉快,但是它工作的很好,在我看来比GUI更容易使用。 select方法基于CHOICE命令,所以需要在不包含它的Windows版本中下载并稍微修改其参数。 当然,为了使用SET / P而不是CHOICE,代码可以很容易地修改,但是这种改变将消除非常简单和快速的select方法,只需要一个按键来导航和select。
@echo off setlocal rem Select a file or folder browsing a directory tree rem Antonio Perez Ayala rem Usage examples of SelectFileOrFolder subroutine: call :SelectFileOrFolder file= echo/ echo Selected file from *.* = "%file%" pause call :SelectFileOrFolder file=*.bat echo/ echo Selected Batch file = "%file%" pause call :SelectFileOrFolder folder=/F echo/ echo Selected folder = "%folder%" pause goto :EOF :SelectFileOrFolder resultVar [ "list of wildcards" | /F ] setlocal EnableDelayedExpansion rem Process parameters set "files=*.*" if "%~2" neq "" ( if /I "%~2" equ "/F" (set "files=") else set "files=%~2" ) rem Set the number of lines per page, max 34 set "pageSize=30" set "char=0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" rem Load current directory contents set "name[1]=<DIR> .." :ProcessThisDir set "numNames=1" for /D %%a in (*) do ( set /A numNames+=1 set "name[!numNames!]=<DIR> %%a" ) for %%a in (%files%) do ( set /A numNames+=1 set "name[!numNames!]= %%a" ) set /A numPages=(numNames-1)/pageSize+1 rem Show directory contents, one page at a time set start=1 :ShowPage set /A page=(start-1)/pageSize+1, end=start+pageSize-1 if %end% gtr %numNames% set end=%numNames% cls echo Page %page%/%numPages% of %CD% echo/ if %start% equ 1 (set base=0) else set "base=1" set /A lastOpt=pageSize+base, j=base for /L %%i in (%start%,1,%end%) do ( for %%j in (!j!) do echo !char:~%%j,1! - !name[%%i]! set /A j+=1 ) echo/ rem Assemble the get option message if %start% equ 1 (set "mssg=: ") else (set "mssg= (0=Previous page") if %end% lss %numNames% ( if "%mssg%" equ ": " (set "mssg= (") else set "mssg=%mssg%, " set "mssg=!mssg!Z=Next page" ) if "%mssg%" neq ": " set "mssg=%mssg%): " :GetOption choice /C "%char%" /N /M "Select desired item%mssg%" if %errorlevel% equ 1 ( rem "0": Previous page or Parent directory if %start% gtr 1 ( set /A start-=pageSize goto ShowPage ) else ( cd .. goto ProcessThisDir ) ) if %errorlevel% equ 36 ( rem "Z": Next page, if any if %end% lss %numNames% ( set /A start+=pageSize goto ShowPage ) else ( goto GetOption ) ) if %errorlevel% gtr %lastOpt% goto GetOption set /A option=start+%errorlevel%-1-base if %option% gtr %numNames% goto GetOption if defined files ( if "!name[%option%]:~0,5!" neq "<DIR>" goto endSelect ) else ( choice /C OS /M "Open or Select '!name[%option%]:~7!' folder" if errorlevel 2 goto endSelect ) cd "!name[%option%]:~7!" goto ProcessThisDir :endSelect rem Return selected file/folder for %%a in ("!name[%option%]:~7!") do set "result=%%~Fa" endlocal & set "%~1=%result% exit /B
还有两种方法。
1.使用混合.bat / hta(必须保存为bat
)脚本。它可以使用VBScript或JavaScript,但例子是使用javascrtipt。不创build临时文件。select文件夹不是那么容易,将需要一个外部的JavaScript图书馆,但select文件很容易
<!-- : starting html comment :: FileSelector.bat @echo off for /f "tokens=* delims=" %%p in ('mshta.exe "%~f0"') do ( set "file=%%~fp" ) echo/ if not "%file%" == "" ( echo selected file is : %file% ) echo/ exit /b --> <Title>== FILE SELECTOR==</Title> <body> <script language='javascript'> function pipeFile() { var file=document.getElementById('file').value; var fso= new ActiveXObject('Scripting.FileSystemObject').GetStandardStream(1); close(fso.Write(file)); } </script> <input type='file' name='file' size='30'> </input><hr><button onclick='pipeFile()'>Submit</button> </body>
1.1 – 未提交rojo提议的表格(见评论):
<!-- : starting html comment :: FileSelector.bat @echo off for /f "tokens=* delims=" %%p in ('mshta.exe "%~f0"') do ( set "file=%%~fp" ) echo/ if not "%file%" == "" ( echo selected file is : "%file%" ) echo/ exit /b --> <Title>== FILE SELECTOR==</Title> <body> <script language='javascript'> function pipeFile() { var file=document.getElementById('file').value; var fso= new ActiveXObject('Scripting.FileSystemObject').GetStandardStream(1); close(fso.Write(file)); } </script> <input id='file' type='file' name='file' size='30' onchange='pipeFile()' > </input> <hr> <button onclick='pipeFile()'>Submit</button> <script>document.getElementById('file').click();</script> </body>
2.你已经在使用powershell / net,你可以创build自编译的jscript.net hybrid.It不需要临时的cs文件进行编译,直接使用内置的jscrript.net编译器。也不需要使用powershell,代码是更可读:
@if (@X)==(@Y) @end /* JScript comment @echo off :: FolderSelectorJS.bat setlocal for /f "tokens=* delims=" %%v in ('dir /b /s /a:-d /o:-n "%SystemRoot%\Microsoft.NET\Framework\*jsc.exe"') do ( set "jsc=%%v" ) if not exist "%~n0.exe" ( "%jsc%" /nologo /out:"%~n0.exe" "%~dpsfnx0" ) for /f "tokens=* delims=" %%p in ('"%~n0.exe"') do ( set "folder=%%p" ) if not "%folder%" == "" ( echo selected folder is %folder% ) endlocal & exit /b %errorlevel% */ import System; import System.Windows.Forms; var f=new FolderBrowserDialog(); f.SelectedPath=System.Environment.CurrentDirectory; f.Description="Please choose a folder."; f.ShowNewFolderButton=true; if( f.ShowDialog() == DialogResult.OK ){ Console.Write(f.SelectedPath); }
批+ PowerShell + C#多语种解决scheme
这与Batch + PowerShell混合使用的解决scheme是一样的,但是为了兼容XP和Vista,重新添加了C#的备用内容。 在xNightmare67x的请求中添加了多个文件select。
<# : chooser_XP_Vista.bat :: // launches a File... Open sort of file chooser and outputs choice(s) to the console :: // https://stackoverflow.com/a/36156326/1683264 @echo off setlocal enabledelayedexpansion rem // Does powershell.exe exist within %PATH%? for %%I in ("powershell.exe") do if "%%~$PATH:I" neq "" ( set chooser=powershell -noprofile "iex (${%~f0} | out-string)" ) else ( rem // If not, compose and link C# application to open file browser dialog set "chooser=%temp%\chooser.exe" >"%temp%\c.cs" ( echo using System; echo using System.Windows.Forms; echo class dummy { echo public static void Main^(^) { echo OpenFileDialog f = new OpenFileDialog^(^); echo f.InitialDirectory = Environment.CurrentDirectory; echo f.Filter = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*"; echo f.ShowHelp = true; echo f.Multiselect = true; echo f.ShowDialog^(^); echo foreach ^(String filename in f.FileNames^) { echo Console.WriteLine^(filename^); echo } echo } echo } ) for /f "delims=" %%I in ('dir /b /s "%windir%\microsoft.net\*csc.exe"') do ( if not exist "!chooser!" "%%I" /nologo /out:"!chooser!" "%temp%\c.cs" 2>NUL ) del "%temp%\c.cs" if not exist "!chooser!" ( echo Error: Please install .NET 2.0 or newer, or install PowerShell. goto :EOF ) ) rem // Do something with the chosen file(s) for /f "delims=" %%I in ('%chooser%') do ( echo You chose %%~I ) rem // comment this out to keep chooser.exe in %temp% for faster subsequent runs del "%temp%\chooser.exe" >NUL 2>NUL goto :EOF :: // end Batch portion / begin PowerShell hybrid chimera #> Add-Type -AssemblyName System.Windows.Forms $f = new-object Windows.Forms.OpenFileDialog $f.InitialDirectory = pwd $f.Filter = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*" $f.ShowHelp = $true $f.Multiselect = $true [void]$f.ShowDialog() if ($f.Multiselect) { $f.FileNames } else { $f.FileName }
对于XP或Vista的文件夹select器,请使用WSH解决scheme或npocmaka的HTA解决scheme 。