Как разделить строки с двойными кавычками со встроенными пространствами, разделенными пробелами в пакетном файле?

Я борюсь с улучшением script, который я предложил в качестве ответа Как написать пакетный файл с указанием пути к исполняемому файлу и версии Python для обработки сценариев Python в Windows? вопрос. Чтобы предотвратить диалоговое окно Open With, я хотел бы прочитать вывод команды ftype, извлечь путь для выполнимый из него и проверить, существует ли он.

После этого

@echo off
setlocal EnableDelayedExpansion 
rem c:\ftype Python.File ->
rem Python.File="c:\path with spaces, (parentheses) and % signs\python.exe" "%1" %*
for /f "tokens=2 delims==" %%i in ('ftype Python.File') do (
    set "reg_entry=%%i"
)

reg_entry's Содержимое

"c:\path with spaces and (parentheses) and % signs\python.exe" "%1" %*

Как мне разделить это, чтобы получить "c:\path with spaces, (parentheses) and % signs\python.exe", "%1" и %*?

ИЗМЕНИТЬ
Я пробовал использовать call после прочтения ответа Aacini, и он почти работает. Однако он не обрабатывает знак %.

@echo off
setlocal EnableDelayedExpansion 
set input="c:\path with spaces and (parentheses) and %% signs\python.exe" "%%1" %%*
echo !input!
call :first_token output !input!
echo !output!
goto :eof

:first_token
set "%~1=%2"
goto :eof

Выход

"c:\path with spaces and (parentheses) and % signs\python.exe" "%1" %*
"c:\path with spaces and (parentheses) and 1"

Ответ 1

Альтернативный синтаксический анализатор, который очень похож на парсер CALL, является простым FOR. Существуют два осложняющих фактора:

1- Функция FOR не должна быть расширена, если расширенное расширение включено, если оно содержит !. Это легко обрабатывается.

2- Содержимое не должно содержать подстановочные знаки * или ?. ? можно временно заменить, а затем возвратить. Но нет простого способа поиска и замены *.

Поскольку эта проблема пытается разобрать путь, а пути не могут содержать подстановочные знаки, эту проблему легко решить без использования CALL. Я добавил ! в тестовый пример для полноты.

@echo off
setlocal disableDelayedExpansion
set input="c:\path with spaces, ampersand &, carets ^ and (parentheses)! and %% signs\python.exe" "%%1" %%*
set input
set "output="
setlocal enableDelayedExpansion
for %%A in (!input!) do if not defined output endlocal & set output=%%A
set output

Если мы можем положиться на то, что первый токен всегда будет заключен в кавычки, тогда решение еще проще. Мы можем использовать FOR/F с EOL и DELIMS, установленными на ".

@echo off
setlocal disableDelayedExpansion
set input="c:\path with spaces, ampersand &, carets ^ and (parentheses)! and %% signs\python.exe" "%%1" %%*
set input
set "output="
setlocal enableDelayedExpansion
for /f eol^=^"^ delims^=^" %%A in ("!input!") do endlocal & set output="%%A"
set output

Однако я просто посмотрел на мой вывод FTYPE и обнаружил, что некоторые записи не цитировались, даже если они содержат пробелы в пути! Я не думаю, что любой из ответов на этой странице справится с этим. На самом деле все предпосылки, стоящие за вопросом, могут быть ошибочными.

Ответ 2

Это прямая возможность пакета. В Batch параметры Batch файла разделяются пробелами, а параметр может быть заключен в кавычки, поэтому просто передайте значение reg_entry в качестве параметров пакетного файла внутри каждого из них:

C:\>type test.bat
@echo off
:loop
echo %1
shift
if not "%1" == "" goto loop

.

C:\>echo %reg_entry%
"c:\path with spaces and (parentheses) and % signs\python.exe" "%1" %*

.

C:\>test %reg_entry%
"c:\path with spaces and (parentheses) and % signs\python.exe"
"%1"
%*

Ответ 3

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

В принципе вам нужно построить конечный конечный автомат в вашем .cmd файле с метками и условными gotos. FSA имеет состояния, которые обрабатывают различные части элемента, который вы хотите собрать. В начальном состоянии вы решаете, видите ли вы пробел (пропустите и вернитесь к началу), двойную кавычку (перейдите к части FSA, которая обрабатывает строки с двумя кавычками) или что-то непустое (перейдите в часть FSA, который собирает непустые символы).

Часть FSA, которая собирает строки с двойными кавычками, отбирает символы, пока не найдет другую двойную кавычку; это позволяет записывать пробелы внутри строк с двойным цитированием. Я думаю, вам нужно проверить "сбежавшую" двойную кавычку (две из них в строке), и если она найдена, замените их на одну двойную кавычку и продолжайте собирать символы.

Это довольно уродливо, потому что CMD script имеет действительно ужасные возможности обработки строк. Каждая (уродливая) вещь, которую вы должны знать, можно найти, набрав HELP SET в командной строке DOS. В частности, подстрока имеет вид %VAR:~n,m%, который выделяет символы m, начинающиеся с индекса n в переменной окружения %VAR%. Я нашел это полезным для SET TEMP=%VAR%, а затем отключил символы %TEMP% один за другим с помощью простых последовательностей, таких как

SET CHAR=%TEMP:~0,1%
SET TEMP=%TEMP:~1%

Enjoy.

Ответ 4

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

Чтобы избежать потери % знаков с помощью CALL, вы можете удвоить их перед расширением CALL.
Ключевая строка set "input=!input:%%=%%%%!", процентные знаки наполовину находятся в одной из фаз парсера, поэтому заменяются одиночные % на %%.

Но даже тогда это решение не идеально!

Это решение имеет проблемы со специальными символами типа &<>|, в вашем случае только &, так как это единственный законный символ в имени файла/пути.
Этого можно избежать, изменив строку set "%~1=%2" на set ^"%~1=%2", это гарантирует, что% 2 использует окружающие кавычки.

Но теперь у вас другая проблема, все карьеры удвоены,
поэтому я должен сделать еще одну замену для вывода с помощью set "output=!output:^^=^!".

Новый код будет выглядеть следующим образом:

@echo off
setlocal EnableDelayedExpansion 
set input="c:\path with spaces, exlcamation mark^!, ampersand &, carets ^ and (parentheses) and %% signs\python.exe" "%%1" %%*
echo !input!
set "input=!input:%%=%%%%!"
call :first_token output !input!
set "output=!output:^^=^!"
echo !output!
goto :eof

:first_token
set ^"%~1=%2"
goto :eof

EDIT: для обработки также восклицательных знаков !
Вам нужно изменить функцию :first_token на

:first_token
setlocal DisableDelayedExpansion
set ^"temp=%2"
set ^"temp=%temp:!=^!%"
(
endlocal
set ^"%~1=%temp%"
)
goto :eof