Пакетный файл для цикла - PullRequest
1 голос
/ 21 июля 2011

Я пишу командный файл для запуска на Windows Server 2008 R2, в нем есть цикл for, и независимо от того, сколько уроков я читаю онлайн и пробую, он не будет запускать цикл for.

echo "Starting blur detection..."
if not exist %root%\output mkdir output
set tempnum = dir root | find /i "file"
set num = tempnum-5
for /l %idx in (0, 1, num) do (
   %root%\blurDetection.exe %root%\%img%__%idx%.tif %root%\output
   echo "blurDetection" %idx " of " %num )

Windows PowerShell говорит, что "idx был неожиданным в это время"Любые предложения?

РЕДАКТИРОВАТЬ: На самом деле я думаю, что эта строка вызывает проблему, я не думаю, что она получает и целочисленное значение в ответ.

set tempnum = dir root | find /i "file"

Ответы [ 2 ]

4 голосов
/ 24 июля 2011

Хорошо, на первый взгляд я вижу пять шесть семь (с половиной) явных ошибок здесь:

1.Кавычки вокруг аргументов echo.

echo являются встроенной оболочкой, и их синтаксис был довольно фиксирован 25 лет назад в DOS и CP / M.echo просто печатает все, что идет после него, до конца команды (это могут быть такие вещи, как разрыв строки, ), &, | и т. Д.), Поэтому вам нужно:

echo Starting blur detection ...

и

echo blurDetection %i of %num%

2.Пробелы вокруг знака равенства в set.

set, как и echo - встроенная оболочка.Его синтаксис также немного неудобен (хотя у вас есть такая же проблема и в некоторых оболочках Unix).Дело в том, что все между set и знаком равенства - это имя переменной, все после нее - это значение.В этом случае это означает, что имя вашей переменной заканчивается пробелом , а значение начинается с пробела .Поэтому, что бы вы ни делали, никогда не ставьте пробелы вокруг знака равенства.Язык командного процессора не слишком прост в использовании пробелов в отличие от многих других языков программирования.Поэтому:

set foo=bar

, а не

set foo = bar

, иначе вы никогда не найдете переменную с именем foo, потому что вам нужно будет использовать %foo % (обратите внимание на пробел).

3.set не выполняет арифметику, пока вы не скажете это.

set не вычислит ничего, если вы явно не скажете это.Это делается с помощью set /a:

Переключатель /A указывает, что строка справа от знака равенства является числовым выражением, которое оценивается.

Поэтому вам нужно

set /a num=tempnum - 5

С /a % вокруг имен переменных можно опустить с правой стороны, что делает код немного более понятным, обычно.

4.set только устанавливает значения, он не выполняет никакого кода.

В

set tempnum=dir root | find /i "file"

вы, очевидно, пытаетесь установить переменную на основе вывода некоторыхкоманда.Это не может работать, так как это просто устанавливает tempnum в dir root и отправляет вывод этой команды (нет) в find из-за канала.Я не совсем уверен, что это должно делать, но я предполагаю, что вы подсчитываете файлы.

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

set num=0
for %%f in (%root%\*) do set /a num+=1

Обратите внимание, что я предполагаю, что %root% - это каталог, файлы которого вас интересуют. Хотя вы ранее использовали %root%.Если вам повезет, %root% - это просто root, иначе ваш код никогда не сможет правильно работать.

Поскольку вы, очевидно, хотите вычесть 5 для некоторых файлов, которые не являются частью того, что вам нужно, вы можете сделать

set /a num-=5

впоследствии.Другой вариант, если файлы, которые вас интересуют, имеют общую черту, состоял бы в том, чтобы ограничить шаблон таким, например,

for %%f in (%root%\*.tif) do set /a num+=1

, чтобы вам не нужно было вычитать что-то из окончательного числа.

5.for переменные только однобуквенные.

Самая большая из них здесь, вероятно, на которую уже указывал STATUS_ACCESS_DENIED : переменные, используемые в операторах for, являются специальными.Они не являются переменными окружения и состоят только из одной буквы.Поэтому код должен читать

for /l %%i in ...

6.for переменные должны начинаться с префикса двух знаков процента в пакетных файлах.

В командной строке оператор for, такой как

for /l %x in (1,1,5) do @echo %x

просто отлично, но в пакетных файлах синтаксический анализатор будет спотыкаться об этом, вероятно, ожидая аргумента для программы (это %1, %2 и т. Д.).Как уже указано в документации к for, внутри пакетных файлов переменные должны использовать два %:

for /l %%x in (1,1,5) do @echo %%x

Это, в свою очередь, сломается при использовании непосредственно в командной строке. TANSTAAFL .

Однако это не относится к переменным среды.Там нет удвоения знаков процента (см. Также ниже).

Также обратите внимание, что у вас есть другая ошибка в пакете во второй строке, где вы используете idx как переменную среды, даже если выпопробуйте использовать его как переменную for в другом месте:

%root%\%img%__%idx%.tif

должно читать

%root%\%img%__%%i.tif 

7.Переменные окружения должны быть окружены %.

Строка

for /l %i in (0, 1, num) do ...

никогда не будет работать и, вероятно, будет жаловаться на неожиданность num.Чтобы получить значение переменной окружения, вам нужно заключить его в знаки процента:

for /l %%i in (0, 1, %num%) do ...

в противном случае это просто строка.

В аналогичном ключе это также относится и к окончательному echo.%%i - это переменная for, поэтому только префикс, но num - это переменная окружения, поэтому ее необходимо заключить в %:

echo blurDetection %%i of %num%

Иногда это может сбивать с толку,но, к счастью, мы еще не рассматривали отложенное расширение: -).

И последнее, хотя и незначительное:

8.Всегда заключайте в кавычки имена файлов.

В вашем случае весь пакет сломается, как только %root% содержит путь с пробелами.Это начинается во второй строке, где вы ищите существующий каталог.Сделайте это более надежным, заключив путь в пробелы:

if not exist "%root%\output" mkdir output

То же самое с вызовами внутри цикла:

"%root%\blurDetection.exe" "%root%\%img%__%%i.tif" "%root%\output"

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


Теперь, когда мы это получили, мы можем посмотреть, как выглядит ваша партия:

echo Starting blur detection ...
if not exist "%root%\output" mkdir "%root%\output%"
set num=0
for %%f in (%root%\*.tif) do set /a num+=1
for /l %%i in (0, 1, %num%) do (
  "%root%\blurDetection.exe" "%root%\%img%__%%i.tif" "%root%\output"
  echo blurDetection %%i of %num%
)

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

  1. Создать выходной каталог, если он еще не существует
  2. Найдите количество изображений для обработки (которые называются последовательно)
  3. Обработайте их и напечатайте вывод состояния.

Но мы еще не закончили.Как вы заметили в приведенных выше объяснениях, for уже может перебирать файлы.С шагами 2 и 3 мы дважды перебираем набор изображений, что не совсем необходимо.Все, что требуется, - это один цикл и некоторые изменения:

for %%f in (%root%\%img%__*.tif) do "%root%\blurDetection.exe" "%%f" "%root%\output"

Но теперь мы пропускаем вывод статуса.И файлы больше не обрабатываются по порядку (что может или не может быть проблемой; я предполагаю, что это не так).Если вам отчаянно не нужно сообщение «Обработанный файл», то подойдет следующее:

for %%f in (%root%\%img%__*.tif) do (
  "%root%\blurDetection.exe" "%%f" "%root%\output"
  echo blurDetection %%~nf
)

, которое просто выводит последнее обработанное имя файла, но все равно будет обрабатывать изображения в порядке 1, 10, 11, ..., 19, 2, 20 ... поэтому число, скорее всего, не совсем точно соответствует прогрессу.Если вам нужен счетчик, то вам снова нужен цикл из предыдущего, который подсчитывает файлы.Я думаю, что я оставляю это сейчас как есть.

Несколько вещей, которые мне показались любопытными:

  • Ни root, ни img нигде не определены.Я предполагаю, что это всего лишь фрагмент из более крупной программы, но читателю нехорошо наткнуться на переменные, которые не имеют видимой инициализации.
  • Хотя %root% используется во многих местах,текущий рабочий каталог кажется идентичным (строка 2, где вы проверяете наличие %root%\output и, если нет, создаете просто output. Вероятно, pushd в начале и popd в конце будет более чистымспособ обработки этого.
  • Вы утверждаете, что заключили в кавычки сообщение об ошибке PowerShell, но это сообщение об ошибке cmd. Если вы запустили этот сценарий через PowerShell, он уже умрет во второй строке.

Другое дело, поскольку вы упомянули, что это должно работать на Server 2008 R2: PowerShell установлен там по умолчанию, и если ваша политика выполнения установлена ​​правильно, вы также можете сделать это с помощью сценария PowerShell:

Write-Host Starting blur detection ...
if (!(Test-Path $root\output)) { New-Item -ItemType Directory $root\output }
$files = Get-ChildItem $root\*.tif
$files | ForEach-Object { $i = 1 } {
    & "$root\blurDetection.exe" $_ $root\output
    Write-Host blurDetection ($i++) of ($files.Count)
}
4 голосов
/ 21 июля 2011

(добавлено после первоначального поста) Да, и я пропустил самую большую проблему.Что в действительности представляет собой эту строку:

set tempnum = dir root | find /i "file"

Вы хотите захватить вывод dir, верно?Это возможно, AFAIK можно выполнить только внутри for, если только вы не можете жить с выводом в файл и использовать его в качестве ввода позже.

Синтаксис в таком случае:

FOR /F ["options"] %variable IN ('command') DO command [command-parameters]

Примечание: это , а не обратные метки.

Лучшие объяснения для сценариев NT for: http://www.robvanderwoude.com/ntfor.php

Еще одно примечание:, поскольку вы, похоже, полагаетесь на имя файла как на некоторое число, я подозреваю, что dir /b будет лучшим выбором.Обычные dir также будут выводить дату и размер файла и т. Д. *


Имена переменных IIRC for не могут содержать более одной буквы.Кроме того, внутри файлов сценариев (в отличие от командной строки) эти переменные выглядят следующим образом:

%%i

Кроме того,

set num=tempnum-5

должно быть, вероятно,

set /a num=%tempnum%-5

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

С for /?:

FOR %variable IN (set) DO command [command-parameters]

  %variable  Specifies a single letter replaceable parameter.
  (set)      Specifies a set of one or more files.  Wildcards may be used.
  command    Specifies the command to carry out for each file.
  command-parameters
             Specifies parameters or switches for the specified command.

To use the FOR command in a batch program, specify %%variable instead
of %variable.  Variable names are case sensitive, so %i is different
from %I.

Обратите особое внимание на эти два утверждения:

  • %variable Указывает заменяемый на одну букву параметр.
  • Чтобы использовать команду FOR в пакетной программе, укажите %%variable вместо %variable.

Еще одна особенность цикла for заключается в том, что вы можете разделить входные данные, и токены будут присвоены переменным в алфавитном порядке.Так что внутри for %a ... токены будут назначены на %a, %b и так далее ... set tempnum = dir root |find / i "file"

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