Diálogo del selector de archivos / carpetas desde un script por lotes de Windows

Por lo general, pedirle al usuario que proporcione un nombre de archivo a un script por lotes es un asunto complicado, que no requiere errores de ortografía, comillas en espacios con espacios, etc. Desafortunadamente, los usuarios no son muy conocidos por su precisión. En situaciones donde la ubicación del archivo de entrada no se conoce hasta el tiempo de ejecución, el uso de una GUI para la entrada de selección de archivos reduce la probabilidad de error del usuario.

¿Hay alguna manera de invocar un File... Open el selector de archivos gui de estilo o el selector de carpetas desde un script por lotes de Windows?

Si el usuario del script tiene instalados PowerShell o .NET, es posible. Vea la respuesta a continuación.

También me interesa ver qué otras soluciones puede ofrecer cualquier otra persona.

Explorador de archivos

Actualización 2016.3.20:

Dado que PowerShell es un componente nativo de prácticamente todas las instalaciones modernas de Windows hoy en día, declaro que la recuperación de C # ya no es necesaria. Si aún lo necesita para compatibilidad con Vista o XP, lo cambié a una nueva respuesta . Comenzando con esta edición, estoy reescribiendo el guión como un híbrido de Batch + PowerShell e incorporando la capacidad de realizar selecciones múltiples. Es profundamente más fácil de leer y modificar según sea necesario.

 < # : 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 } 

Esto da como resultado un cuadro de diálogo selector de archivo.

selector de archivo

El resultado de una selección de salidas You chose C:\Users\me\Desktop\tmp.txt a la consola. Si desea forzar la selección de un solo archivo, simplemente cambie la propiedad $f.Multiselect a $false .

(El comando de PowerShell se ha filtrado sin piedad desde el blog Just Tinkering ). Consulte la documentación de OpenFileDialog Class para conocer otras propiedades que puede establecer, como Title y InitialDirectory .


Navegador de carpetas

Actualización 2015.08.10:

Dado que ya existe un método COM para invocar un selector de carpetas , es bastante fácil crear un PowerShell de una sola línea que pueda abrir el selector de carpetas y dar salida a la ruta.

 :: 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 

En el método BrowseForFolder() , el cuarto argumento especifica la raíz de la jerarquía. Consulte ShellSpecialFolderConstants para obtener una lista de valores válidos.

Esto da como resultado un cuadro de diálogo selector de carpeta.

enter image description here

El resultado de una selección de salidas You chose C:\Users\me\Desktop a la consola.

Consulte la documentación de la clase FolderBrowserDialog para conocer otras propiedades que puede establecer, como RootFolder . Las soluciones originales .NET System.Windows.Forms PowerShell y C # se pueden encontrar en la revisión 4 de esta respuesta si es necesario, pero este método COM es mucho más fácil de leer y mantener.

Windows Script Host


Selección de archivo

Windows XP tenía un misterioso objeto UserAccounts.CommonDialog WSH que permitía a VBScript y JScript iniciar la solicitud de selección de archivos. Aparentemente, eso se consideró un riesgo de seguridad y se eliminó en Vista.


Selección de carpeta

Sin embargo, el método WSH Shell.Application objeto BrowseForFolder aún permitirá la creación de un cuadro de diálogo de selección de carpeta. Aquí hay un ejemplo de lote híbrido + JScript. Guárdelo con una extensión .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 : ''); 

diálogo de selección de carpeta

En el método BrowseForFolder() , el cuarto argumento especifica la raíz de la jerarquía. Consulte ShellSpecialFolderConstants para obtener una lista de valores válidos.

Esto debería funcionar desde XP hacia arriba y no requiere un archivo hibrid, simplemente ejecuta mshta con una larga línea de comandos:

 @echo off set dialog="about:" for /f "tokens=* delims=" %%p in ('mshta.exe %dialog%') do set "file=%%p" echo selected file is : "%file%" pause 

Se puede hacer una selección de archivo / carpeta con Batch puro, como se muestra a continuación. Por supuesto, la sensación y el aspecto no son tan agradables como una GUI, pero funcionan muy bien y, en mi opinión, es más fácil de usar que la versión de la GUI. El método de selección se basa en el comando CHOICE, por lo que sería necesario descargarlo en las versiones de Windows que no lo incluyen y modificar ligeramente sus parámetros. Por supuesto, el código puede modificarse fácilmente para usar SET / P en lugar de CHOICE, pero este cambio eliminaría el método de selección simple y rápido que solo requiere una pulsación de tecla para navegar y seleccionar.

 @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]= .." :ProcessThisDir set "numNames=1" for /D %%a in (*) do ( set /A numNames+=1 set "name[!numNames!]= %%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 "" 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 

Dos formas más

1.Utilizar una secuencia de comandos híbrida .bat / hta (debe guardarse como un bat ). Puede usar vbscript o javascript, pero el ejemplo es con javascrtipt. No crea archivos temporales. La selección de la carpeta no es tan fácil y requerirá un javascript externo bibliotecas, pero seleccionar el archivo es fácil

  == FILE SELECTOR==    

1.1 – sin el formulario de envío propuesto por rojo (ver comentarios):

  == FILE SELECTOR==     

2. Como ya está usando powershell / net, puede crear híbridos jscript.net autocomstackdos. No requerirá archivos temporales para la comstackción y usará directamente el comstackdor jscrript.net incorporado. No es necesario usar Powershell y el código es mucho más legible:

 @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); } 

Solución políglota Batch + PowerShell + C #

Esta es la misma solución que el híbrido Batch + PowerShell , pero con las cosas de respaldo de C # re-added para la compatibilidad con XP y Vista. Se ha agregado una selección de archivos múltiples a solicitud de xNightmare67x .

 < # : 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 } 

Para un selector de carpetas para XP o Vista, use la solución WSH o la solución HTA de npocmaka .

Otra solución con comando de ejecución directa de PowerShell en lote

 rem preparation command set pwshcmd=powershell -noprofile -command "&{[System.Reflection.Assembly]::LoadWithPartialName('System.windows.forms') | Out-Null;$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog; $OpenFileDialog.ShowDialog()|out-null; $OpenFileDialog.FileName}" rem exec commands powershell and get result in FileName variable for /f "delims=" %%I in ('%pwshcmd%') do set "FileName=%%I" echo %FileName%