it-roy-ru.com

Диалог выбора файла/папки из пакетного скрипта Windows

Как правило, обращение к пользователю с просьбой предоставить имя файла для пакетного сценария - сложная задача, не требующая орфографических ошибок, кавычек вокруг путей с пробелами и так далее. К сожалению, пользователи не очень известны за точность. В ситуациях, когда местоположение входного файла неизвестно до времени выполнения, использование графического интерфейса для ввода выбора файла снижает вероятность ошибки пользователя.

Есть ли способ вызвать средство выбора файлов графического интерфейса в стиле File... Open или пакетный сценарий Windows?

Если у пользователя скрипта установлен PowerShell или .NET, это возможно. Смотрите ответ ниже.

Мне также интересно посмотреть, какие другие решения может предложить кто-либо еще.

30
rojo

Браузер файлов

Обновление 2016.3.20:

Поскольку PowerShell в настоящее время является встроенным компонентом практически всех современных установок Windows, я заявляю, что откат C # больше не нужен. Если вам все еще это нужно для совместимости с Vista или XP, я переместил его в новый ответ . Начиная с этого редактирования, я переписываю сценарий как гибрид Batch + PowerShell и включаю возможность выполнять множественный выбор. Это гораздо легче читать и настраивать по мере необходимости.

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

Это приводит к диалогу выбора файла.

file chooser

Результат выбора выводит You chose C:\Users\me\Desktop\tmp.txt на консоль. Если вы хотите принудительно выбрать один файл, просто измените свойство $f.Multiselect на $false.

(Команда PowerShell безжалостно выведена из Just Tinkering Blog .) См. Документацию OpenFileDialog Class для других свойств, которые вы можете установить, таких как Title и InitialDirectory.


Браузер папок

Обновление 2015.08.10:

Поскольку уже существует метод COM для вызов средства выбора папок , довольно легко создать однострочную оболочку 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 для получения списка допустимых значений.

Это приводит к диалогу выбора папки.

enter image description here

Результат выбора выводит You chose C:\Users\me\Desktop на консоль.

См. Документацию класс FolderBrowserDialog для других свойств, которые вы можете установить, например, RootFolder. Мои оригинальные решения .NET System.Windows.Forms PowerShell и C # можно найти в revision 4 этого ответа, если это необходимо, но этот метод COM намного проще для чтения и обслуживания.

39
rojo

Это должно работать начиная с XP и не требует файла гибрида, он просто запускает 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
10
Antoni Gual Via

Windows Script Host


Выбор файла

Windows XP имела загадочный объект UserAccounts.CommonDialog WSH , который позволял VBScript и JScript запускать запрос выбора файла. Очевидно, это считалось угрозой безопасности и было удалено в Vista.


Выбор папки

Однако метод объекта WSH Shell.Application BrowseForFolder по-прежнему позволяет создавать диалоговое окно выбора папки. Вот гибридный пакет + пример JScript. Сохраните его с расширением .bat.

@if (@[email protected]) @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

В методе BrowseForFolder() четвертый аргумент указывает корень иерархии. См. ShellSpecialFolderConstants для получения списка допустимых значений.

8
rojo

Выбор файла/папки может быть сделан с помощью Batch, как показано ниже. Конечно, ощущения и внешний вид не так приятны, как с графическим интерфейсом, но они работают очень хорошо, и, на мой взгляд, они проще в использовании, чем версия с графическим интерфейсом. Метод выбора основан на команде CHOICE, поэтому он должен был бы загрузить его в версиях Windows, в которых он отсутствует, и слегка изменить его параметры. Конечно, код может быть легко изменен, чтобы использовать SET/P вместо CHOICE, но это изменение устранит очень простой и быстрый метод выбора, который требует только одного нажатия клавиши для навигации и выбора.

@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
4
Aacini

Еще два способа.

1. Использование гибридного сценария .bat/hta (должен быть сохранен как bat). Он может использовать vbscript или javascript, но пример с javascrtipt. Не создавать временные файлы. Выбор папки не так прост и потребует внешнего javascript библиотеки, но выбрать файл легко

<!-- : 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, вы можете создать самоскомпилированный гибрид jscript.net. Для компиляции он не потребует файл temp 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);
}
2
npocmaka

Пакетное + PowerShell + C # решение для полиглота

Это то же решение, что и пакетный + гибридный PowerShell , но с добавленным запасным компонентом C # для XP и совместимости с Vista. Выбор нескольких файлов был добавлен по запросу 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 }

Чтобы выбрать папку для XP или Vista, используйте решение WSH или решение HTA npocmaka .

1
rojo

Другое решение с командой прямого запуска PowerShell в пакетном режиме

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%
1
Esperento57

Мне было написано мое собственное портативное решение: https://sourceforge.net/p/contools/contools/HEAD/tree/trunk/Utilities/Sources/_gui/wxFileDialog/

Вы можете скачать исполняемый файл здесь: https://sourceforge.net/p/contools/contools/HEAD/tree/trunk/Utilities/Binaries/wxFileDialog.exe

Утилита зависит от wxWidgets 3.1.x, так что вы можете собрать ее для других операционных систем.

0
Andry