Почему существование файла влияет на то, как его путь к файлу анализируется в пакетном скрипте? - PullRequest
3 голосов
/ 04 мая 2019

Я что-то не понимаю в том, как работает пакетная команда for при использовании с /f и командой.

Я пытался зациклить на выходе исполняемого файла. Оказалось, что исполняемый файл не существует, и это нормально.

Но вместо того, чтобы получить ожидаемую ошибку о неверном пути, сценарий, казалось, самопроизвольно неправильно токенизировал строку. Это привело меня к кроличьей норе, думая, что я отформатировал цикл for и / или неправильно использовал кавычки или обратные тики.

Размещение исполняемого файла в этом месте устраняет проблему, заставляя ее выглядеть так, как будто токенизация строки пути зависит от наличия фактического файла по этому пути.

Почему следующий пакетный файл

@echo off
for /f "usebackq delims=" %%i in ( `"C:\Program Files\BOGUS_PATH\FAKE.exe"`) do (
    rem
)

вывод 'C:\Program' is not recognized as an internal or external command, operable program or batch file. вместо The system cannot find the path specified.?

Ответы [ 3 ]

4 голосов
/ 04 мая 2019

Во-первых, моя любимая мозоль, usebackq не нужна. Более простая и функционально эквивалентная команда:

@echo off
for /f "delims=" %%i in ('"C:\Program Files\BOGUS_PATH\FAKE.exe"') do (
    rem
)

Требуется только usebackq, когда вы пытаетесь прочитать файл, а путь к файлу содержит разделители маркеров, такие как пробел.

for /f "usebackq" %%A in ('"some file.txt"')

Во всех других ситуациях можно использовать «нормальные» формы

for /f %%A in (fileName) do...
for /f %%A in ("string") do...
for /f %%A in ('someCommand') do...

Теперь, чтобы ответить на ваш актуальный вопрос: -)

Наличие или отсутствие файла фактически не изменяет синтаксический анализ.

Сначала вам нужно понять возможные сообщения об ошибках при попытке выполнить несуществующую программу из командной строки. Есть три варианта:

1) Если «команда» включает двоеточие в любом месте, кроме 2-й позиции, то вы можете получить следующее

c:\test\>abc:fake.exe
The filename, directory name, or volume label syntax is incorrect.

c:\test\>abc:\test\fake.exe
The filename, directory name, or volume label syntax is incorrect.

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

2) Если «команда» включает в себя обратную косую черту, которая указывает путь, и путь недопустим, тогда вы получите

c:\test\>c:bogus\fake.exe
The system cannot find the path specified.

c:\test\>\bogus\fake.exe
The system cannot find the path specified.

c:\test\>abc:bogus\fake.exe
The system cannot find the path specified.

3) Если исполняемый файл не может быть найден, и либо «команда» не содержит информацию о пути, либо указанный путь действителен, то вы получите

C:\test\>fake.exe
'fake.exe' is not recognized as an internal or external command,
operable program or batch file.

C:\test>c:\test\fake.exe
'c:\test\fake.exe' is not recognized as an internal or external command,
operable program or batch file.

Последняя загадка состоит в том, почему "C:\Program Files\BOGUS_PATH\FAKE.exe" выдает ошибку 3) вместо 2)

Если вы выполняете из командной строки с кавычками, то вы получите ожидаемый результат:

C:\test>"C:\Program Files\BOGUS_PATH\FAKE.exe"
The system cannot find the path specified.

Если вы выполняете из командной строки без кавычек, то вы получите ожидаемый результат:

C:\test>C:\Program Files\BOGUS_PATH\FAKE.exe
'C:\Program' is not recognized as an internal or external command,
operable program or batch file.

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

Есть две части, чтобы понять это.

Прежде всего, FOR / F выполняет команду, выполняя

C:\WINDOWS\system32\cmd.exe /c "C:\Program Files\BOGUS_PATH\FAKE.exe"

Вы можете подумать, что ваша «команда» заключена в кавычки, но cmd.exe играет в игры с кавычками, что является второй частью объяснения. Правила цитирования cmd.exe описаны в справке:

c:\test\>help cmd
Starts a new instance of the Windows command interpreter
...
If /C or /K is specified, then the remainder of the command line after
the switch is processed as a command line, where the following logic is
used to process quote (") characters:

    1.  If all of the following conditions are met, then quote characters
        on the command line are preserved:

        - no /S switch
        - exactly two quote characters
        - no special characters between the two quote characters,
          where special is one of: &<>()@^|
        - there are one or more whitespace characters between the
          two quote characters
        - the string between the two quote characters is the name
          of an executable file.

    2.  Otherwise, old behavior is to see if the first character is
        a quote character and if so, strip the leading character and
        remove the last quote character on the command line, preserving
        any text after the last quote character.

Большинство условий в 1. выполнены , за исключением для последнего - строка не указывает на допустимый исполняемый файл. Таким образом, используются правила 2., поэтому команда становится

C:\Program Files\BOGUS_PATH\FAKE.exe

И теперь результат имеет смысл - «команда» ломается в пробел.

'C:\Program' is not recognized as an internal or external command,
operable program or batch file.

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

for /f "delims=" %%A in ('""c:\some path\file.exe" "arg 1" arg2"') do ...

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

for /f "delims=" %%A in ('""c:\some path\file.exe" "this^&that" arg2"') do ...

Но я нахожу это неловким, потому что команда, которую вы используете в командной строке, не соответствует команде, которую вы используете в FOR /F. Я предпочитаю избегать дополнительного набора заключающих в кавычки, чтобы все, что работает в командной строке, также работало в FOR /F.

for /f "delims=" %%A in ('^""c:\some path\file.exe" "this&that" arg2^"') do ...
3 голосов
/ 04 мая 2019

FOR является внутренней командой командного процессора Windows cmd.exe.Использование команды FOR с параметром /F и команды для выполнения приводит к запуску с %ComSpec% /c и указанной командной строке в качестве дополнительных параметров еще одного командного процесса, выполняющегося в фоновом режиме.

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

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

Это можно увидетьпо использованию бесплатного инструмента Sysinternals (Microsoft) Process Monitor .

  • Загрузить архив ZIPe, содержащий Process Monitor .
  • Извлечение ZIP-файла в любой локальный каталог.
  • Запуск Procmon.exe с Запуск от имени администратора , который требуется длязапустите Process Monitor .
  • В Фильтр монитора процесса Сначала отобразится диалоговое окно Добавьте запись Имя процесса - cmd.exe и закройте диалоговое окно с помощью кнопки OK .
  • . Отключите последние пять параметров на панели инструментов, за исключением символа шкафа заполнения с помощью всплывающей подсказки Показать активность файловой системы .
  • Нажмите Ctrl + X , чтобы очистить список.
  • Запустите пакетный файл.
  • Переключитесь обратно на Process Monitor инажмите Ctrl + E , чтобы остановить захват.
  • Убедитесь, что среди столбца Имя процесса также указан столбец PID .Щелкните правой кнопкой мыши заголовок списка и щелкните левой кнопкой мыши пункт контекстного меню Выбрать столбцы ... , если столбец PID еще не отображается, установите флажок Идентификатор процесса и закройте диалоговое окно.окно с OK .Я рекомендую переместить столбец PID с помощью перетаскивания заголовка столбца PID вправо к столбцу Имя процесса .

Прокрутить вверхчтобы начать список действий с записанной файловой системой, найдите строку, где отображается cmd.exe с идентификатором процесса, отличным от первого.Второй cmd.exe с другим PID - это экземпляр командного процессора Windows, запущенный FOR .

Щелкните правой кнопкой мыши на этом втором cmd.exe, щелкните левой кнопкой мыши в контекстном меню на Свойства , выберите вкладку Обработка и посмотрите Командная строка , показывающая:

C:\Windows\system32\cmd.exe /c "C:\Program Files\BOGUS_PATH\FAKE.exe"

Хорошо.Итак, теперь мы знаем, что FOR соответственно cmd.exe делает при запуске команды для захвата выходных данных этой команды.Закройте окно свойств.

Просмотрите список, и можно увидеть, какие обращения к файловой системе осуществляются вторым cmd.exe, чтобы найти указанный исполняемый файл, который не существует в указанном и существующем каталоге.Затем выполняется поиск C:\Program Files\BOGUS_PATH\FAKE.exe.*, также без такого файла в качестве результата.

Теперь первая строка аргумента разделена на разделители аргументов, которым принадлежит символ пробела среди некоторых других, таких как запятая или знак равенства.Таким образом, первая строка аргумента C:\Program Files\BOGUS_PATH\FAKE.exe снова разделена аргументом, в результате чего C:\Program теперь интерпретируется как строка первого аргумента.

cmd.exe проверяет, существует ли путь к каталогу, являющийся сейчас просто C:\, который является истиннымкак это было раньше для пути к каталогу C:\Program Files\BOGUS_PATH.

Далее проверяется, существует ли файл, соответствующий шаблону C:\Program.*, который не возвращает такого файла.Затем cmd.exe проверяет, существует ли файл C:\Program, который снова не приводит к такому файлу.

Теперь второй cmd.exe прекращает поиск файла, который может быть выполнен, и выводит сообщение об ошибке.

Это действительно сбивает с толку, если указывать исполняемый файл с полным путем, заключенным в двойные кавычки, как требуется, из-за того, что место для вывода не выводится

'C: \ Program Files \ BOGUS_PATH \ FAKE.exe' не распознается как внутренняя или внешняя команда
работающая программа или командный файл.

Этого можно ожидать, но выводится сообщение об ошибке:

«C: \ Program» не распознается как внутренняя или внешняя команда
работающая программа или командный файл.

Я думаю, что анализ первой строки аргумента еще раз с использованием разделителей аргументов при невозможности найти файл для выполнения выполняется, например, если пользователь запускает cmd.exe с командой (аргумент)

"C:\Programs\MyApplication.exe C:\Temp\FileToProcess.txt"

вместо

"C:\Programs\MyApplication.exe" "C:\Temp\FileToProcess.txt"

Итак, вопрос:

Этот автоматический аргумент разбирается хорошо или плохо?

Ну, это трудно ответить. По крайней мере, это как-то задокументировано в помощи cmd.exe, как описано dbenham в его ответе .

0 голосов
/ 04 мая 2019

Что вам, вероятно, нужно сделать, это заключить вашу исполняемую команду в двойные кавычки:

@Echo Off
For /F Delims^=^ EOL^= %%A In ('""C:\Program Files\BOGUS_PATH\FAKE.exe""'
)Do Echo(%%A
Pause

Если внутренний набор двойных кавычек предназначен для защиты известного пространства в пути, а внешний набор для защиты внутренних, когда содержимое передается экземпляру cmd.exe, в котором выполняется команда в скобках .

Затем, чтобы предотвратить появление какого-либо сообщения об ошибке в команде, вы должны перенаправить вывод STDERR на NUL:

@Echo Off
For /F Delims^=^ EOL^= %%A In ('""C:\Program Files\BOGUS_PATH\FAKE.exe" 2>Nul"'
)Do Echo(%%A
Pause

Если исполняемый файл не существует, будет выдано сообщение об ошибке, The system cannot find the path specified. и команда Do не будет выполняться, (поскольку STDOUT для обработки не существует) .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...