File / folder chooser dialog from a Windows batch script

后端 未结 9 585
一生所求
一生所求 2020-11-22 11:58

Typically, asking the user to supply a file name to a batch script is a messy affair, requiring no misspellings, quotes around paths with spaces, and so forth. Unfortunatel

相关标签:
9条回答
  • 2020-11-22 12:36

    Batch + PowerShell + C# polyglot solution

    This is the same solution as the Batch + PowerShell hybrid, but with the C# fallback stuff re-added for XP and Vista compatibility. Multiple file selection has been added at xNightmare67x's request.

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

    For a folder chooser for XP or Vista, use either the WSH solution or npocmaka's HTA solution.

    0 讨论(0)
  • 2020-11-22 12:39

    A file / folder selection may be done with pure Batch, as shown below. Of course, the feel and look is not as pleasant as a GUI, but it works very well and in my opinion it is easier to use than the GUI version. The selection method is based on CHOICE command, so it would require to download it in the Windows versions that don't include it and slightly modify its parameters. Of course, the code may be easily modified in order to use SET /P instead of CHOICE, but this change would eliminate the very simple and fast selection method that only requires one keypress to navigate and 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
    
    0 讨论(0)
  • 2020-11-22 12:41

    Windows Script Host


    File Selection

    Windows XP had a mysterious UserAccounts.CommonDialog WSH object which allowed VBScript and JScript to launch the file selection prompt. Apparently, that was deemed a security risk and removed in Vista.


    Folder Selection

    However, the WSH Shell.Application object BrowseForFolder method will still allow the creation of a folder selection dialog. Here's a hybrid batch + JScript example. Save it with a .bat extension.

    @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 : '');
    

    folder selection dialog

    In the BrowseForFolder() method, the fourth argument specifies the root of the hierarchy. See ShellSpecialFolderConstants for a list of valid values.

    0 讨论(0)
  • 2020-11-22 12:42

    Two more ways.

    1.Using a hybrid .bat/hta (must be saved as a bat) script .It can use vbscript or javascript but the example is with javascrtipt.Does not create temp files.Selecting folder is not so easy and will require an external javascript libraries , but selecting file is easy

    <!-- : 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 - without submit form proposed by rojo (see comments):

    <!-- : 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.As you already using powershell/net you can create selfcompiled jscript.net hybrid.It will not require temp cs file for compilation and will directly use the built-in jscrript.net compiler.There's no need of powershell too and the code is far more readable:

    @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);
    }
    
    0 讨论(0)
  • 2020-11-22 12:43

    I has been wrote my own portable solution: https://sourceforge.net/p/contools/contools/HEAD/tree/trunk/Utilities/src/_gui/wxFileDialog/

    You can download executable from here: https://sourceforge.net/p/contools/contools/HEAD/tree/trunk/Utilities/bin/wxFileDialog.exe

    The utility has dependency on wxWidgets 3.1.x, so you can actually build it for other operating systems.

    0 讨论(0)
  • 2020-11-22 12:51

    This should work from XP upwards and does'nt require an hibrid file, it just runs mshta with a long command line:

    @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
    
    0 讨论(0)
提交回复
热议问题