1 | |
Наиболее частые ошибки, заметки особенностей программинга BAT файлов, баги интерпретатора*10.01.2013, 15:41. Показов 119582. Ответов 116
Метки %~dp0, chcp 1251, кодировка в консоли, оптимизация кода, режим вывода команд на экран (echo) включен, текущий рабочий каталог bat, украинский cmd/bat, экранирование спецсимвола (Все метки)
Эта тема - ответвление Тонкости языка, редкие команды и сложные скрипты
Постим сюда детали, которые Вы получили опытным путем. Которые считаете уникальными, или могут быть полезными при наборе кода BAT файлов, лучшего понимания принципов работы среды командной строки. Пишем ошибки, которые иногда допускаете, а потом ломаете голову, почему не работает Писать можно много и часто, даже если это мелочь. Все соберем вместе. Весомые замечания перенесем в указанную выше тему. Собрано по категориям: Файловые операции 5) Использование рабочего каталога Bat файла в роле начального для выполнения команд в нем на ОС >= Vista ссылка 8) Как проверить - существует ли папка ссылка 10) Не использовать && после команды Del. ссылка 12) После перехода в другой каталог проверять успех операции ссылка 18) Листинг текущего каталога или корневого ссылка 24) Работа с файлами/папками, в именах которых есть буквы украинского алфавита. ссылка Символы 1) Экранирование спецсимвола ссылка 13) Сохранение концевого пробела в переменную ссылка 17) Запятая и точка с запятой - разделители аргументов ссылка 30) Экранирование номера потока в перенаправлении вывода Echo ссылка Переменные 2) Использование одноименных переменных без обнуления ссылка 3) Пренебрежение Setlocal ссылка 3.1) Не указав Setlocal EnableDelayedExpansion, используем знаки восклицания (!) для раскрытия значения переменных ссылка 6) Инициализация числового типа данных ссылка 14) Обход ошибки "Режим вывода команд на экран (ECHO) включен" ссылка 16) Пробелы тоже могут являться частью названия переменной ссылка 23) Для команды SET всегда заключайте в кавычки переменную и значение, если ним является изменяемое имя файла ссылка 28) Двойное раскрытие переменной. Первыми раскрываются проценты. ссылка 29) Конструкция вида Echo.!Var:~0,1! не работает. ссылка Циклы 7) EOL в цикле FOR - правильный порядок модификаторов ссылка 9) Использовать UseBackQ при чтении содержимого файла, имя которого может меняться ссылка 25) Получение даты и времени файла через цикл и команду For без ключа /S (рекурсия) и подпрограммы ссылка Кодировка 4) Сохранение BAT-файла с кодировкой перевода строк в UNIX-стандарте ссылка 26) Кодировка в консоли ссылка 27) Текстовой файл не читается циклом по неизвестной причине ссылка Тесты, оптимизация и граничные возможности интерпретатора 15) Граничные значения для числового типа в CMD ссылка 19) Максимальная глубина рекурсии = 593*. ссылка 20) Максимальная длина значения строки ссылка 21) Оптимизация кода ссылка 22) Тест замедления работы операторов при перегрузке оперативной памяти ссылка Другое 1) Указывая метку подпрограммы, можно через пробел указывать ее описание. Среда не будет "ругаться" ссылка 11) Внимательно выбирайте имя для BAT(CMD)-файла ссылка
0
|
10.01.2013, 15:41 | |
Ответы с готовыми решениями:
116
Ошибки при запуске bat-файлов Наиболее частые значения в таблицах Lua Как определить количество цветов в подгружаемом рисунке и наиболее частые цвета Ошибки при создании заметки |
10.01.2013, 16:34 [ТС] | 2 | |||||
Начну,
Ошибки: 1) Экранирование спецсимвола потерялся символ экрана ^ в циклах FOR, а также в запросах WMIC, где легко запутаться, например: Bash for /f %%A in ('dir /b "c:\temp"^|find /v /c ""') do Echo Всего файлов в папке temp: %%A
Символ канала | как и другие спецсимволы, например поток < > предваряют экраном ^, чтобы в данном контексте они передавались в качестве строкового типа данных, а не по-умолчанию выполняемой ними функции. Если этого не делать, то в строке команды: Но, естественно этого не произойдет, т.к. мы получим синтаксическую ошибку в жирной части строки. Поэтому пишем ^| и тогда часть Строка команд внутри скобок ('...') после ключевого слова IN Bash dir /b "c:\temp"|find /v /c "" При этом один из потоков (или оба?) можно занулить, чтобы их результат не попал под разбор циклом. Пример: зануляем поток ошибок (StdErr) команды, обрабатываемой циклом:
2
|
10.01.2013, 16:34 [ТС] | 3 | |||||
Ошибки:
2) Использование одноименных переменных без обнуления например, в подпрограмме (м.б. даже чужую скопировали) и своей программе. 3) Пренебрежение Setlocal Команда Setlocal локализует все используемые в конкретном бат-файле переменные так, что после выхода из него, либо принудительного задания команды EndLocal, все переменные будут обнулены автоматически. Если команду Setlocal опустить, при повторном запуске программы в той же сессии интерпретатора все переменные остаются не обнулены. Последствия: например, неправильно будет работать счетчик (если Вы его не обнуляете принудительно до момента использования), либо команда If Defined будет показывать не ожидаемый Вами результат и пр. 3) Не указав Setlocal EnableDelayedExpansion, используем знаки восклицания (!) для раскрытия значения переменных Заметки: 1) Указывая метку подпрограммы, можно через пробел указывать ее описание. Среда не будет "ругаться" (смотрим строку № 5)
4) Сохранение BAT-файла с кодировкой перевода строк в UNIX-стандарте (в конце строки стоит 1 управляющий символ 0x0A (LF) вместо двух - 0x0D, 0x0A (CrLf) В таком случае от среды можно получить разные невменяемые ошибки, наподобие:
Сообщение от cmd
2
|
10.01.2013, 17:44 [ТС] | 4 | |||||||||||||||
Критические ошибки:
5) Использование рабочего каталога Bat файла в роле начального для выполнения команд в нем на ОС >= Vista Любые команды в bat/cmd файле типа:
правой кнопкой мыши "От имени Администратора", задав командам ключи массовой обработки без подтверждения, и без принудительного указания начальной папки, как в примере. Дело в том, что в Windows Vista, Windows 7 (и, вероятно, выше) при запуске батника с повышенными правами, рабочий каталог файла автоматически сменяется на %windir%\system32 (т.е. папка, где находится CMD.exe). В этом можно убедится запустив файл с кодом:
в качестве начального или относительного пути вне зависимости от прав запуска, обязательно используйте нижеуказанную конструкцию: Например, удаление всех папок в каталоге с батником (с подтверждением действий): Bash Rmdir "%~dp0*.*" Bash Del "%~dp0Test\*.*" /p При этом модификатор ~dp обозначает вывод только диска и пути к файлу (без имени). О правильности работы можно убедиться запустив с повышенными правами такой код:
5
|
Модератор
5198 / 2080 / 406
Регистрация: 06.01.2013
Сообщений: 4,794
|
|
10.01.2013, 18:49 | 5 |
1
|
887 / 189 / 16
Регистрация: 18.07.2011
Сообщений: 260
|
|||||||||||
11.01.2013, 00:27 | 6 | ||||||||||
А это ещё одна популярная ошибка!
Эта команда не работает, если батник запущен из сетевой папки. Поэтому лучше делать так, как написал Dragokas. А теперь моя любимая ошибка (делаю её много лет каждый день):
9
|
11.01.2013, 03:16 [ТС] | 7 | |||||
Замечу, что при этом
но это частный случай, и так делать не нужно. Правильный способ показан gimntut.
0
|
Модератор
5198 / 2080 / 406
Регистрация: 06.01.2013
Сообщений: 4,794
|
|
11.01.2013, 13:41 | 8 |
Спасибо за информацию. После этих строк пошарил в инете и понял, что cd сменится на C:\Windows. Глупая смерть.
0
|
18.01.2013, 16:48 [ТС] | 9 | |||||||||||||||||||||||||
Заметки
6) Инициализация числового типа данных Переменную числового типа можно инициализировать изначально как строковой тип. Это будет полезно, когда мы не знаем заранее, попадет ли в нее вообще значение. Например,
7) EOL в цикле FOR - правильный порядок модификаторов EOL - это модификатор цикла, который определяет знак комментария. Если такой знак встречается первым в строке разбираемого файла, цикл пропускает данную строку. Код
;Эта строка будет опущена при разборе циклом for /f %%a in (file.txt) do echo.%%a А; эта будет напечатана Чтобы установить пустой знак (без пропуска комментариев), EOL должен обязательно стоять последним среди модификаторов: Bash for "UseBackq delims= eol=" %%a in ("file.txt") do Echo.%%a
Добавляем в конец имени бекслеш (\) Bash if exist "c:\folder\" echo Папка folder существует& exit /B if exist "c:\folder" echo Файл folder существует& exit /B echo Ни папка, ни файл под именем folder не существуют Если такая папка не существует файл будет скопирован в предыдущий каталог с новым именем (предполагаемым названием этой папки). Мало того теперь и папку нельзя будет создать (ведь в одном каталоге не могут существовать папка и файл с одинаковым именем!) Чтобы обойти эту случайную (досадную) ошибку, дописываем в конец целевого каталога бекслеш (\) Bash copy "file.txt" "c:\folder\" if %errorlevel% neq 0 echo Возникла ошибка но ведь разные ситуации бывают, не правда ли? 9) Использовать UseBackQ при чтении содержимого файла, имя которого может меняться Есть 2 варианта опций цикла FOR, которые обеспечивают чтение содержимого файла построчно: Bash for /f "delims=" %%a in (file.txt) do echo %%a Bash for /f "UseBackQ delims=" %%a in ("file.txt") do echo %%a Но, если вместо file.txt подставить переменную, имя которой (или путь) будут содержать пробелы (а мы заранее можем и не знать), 1-я конструкция не подойдет и будет сначала разбивать строку по пробелам, а уж затем искать данный файл. Поэтому конструкция с модификатором UseBackQ здесь наиболее приемлема. Рекомендации 10) Не использовать && после команды Del. Del возвращает ErrorLevel 0 при возникновении ошибок вида "Отказано в доступе". Используйте вместо этого конструкцию If not exist Bash ::Так не рекомендую del file.txt&& ren file2.txt file.txt ::Так следует делать del file.txt if not exist file.txt (ren file2.txt file.txt) else (echo файл file.txt занят другой программой) Имя не должно носить название распространенных программ и тем более внутренних команд CMD. Иначе рано или сразу Вы получите зацикливание (вызов батником самого себя). Вырезка из темы: Неоднозначный ответ ping 12) После перехода в другой каталог проверять успех операции Иначе может получиться, что Вы работаете совершенно с другой папкой. Причины могут быть разные, например, доступ к папке запрещен, не хватает прав, или папка не существует... Bash ::Переход в другой каталог с одновременной сменой диска (если такова требуется) chdir /d "d:\test" ::Проверка, достигли ли цели if %errorlevel%==0 Echo Выполняем нужные действия ::Или, как вариант, проверяем где сейчас находимся if "%cd%"=="d:\test" Echo Продолжаем банкет :^) Заметки 13) Сохранение концевого пробела в переменную
Если есть вероятность попадания под Echo переменной без значения, сразу после Echo ставим точку: Bash Echo.%var% 15) Граничные значения для числового типа в CMD Числовой тип в CMD может принимать целые значения в пределах от -2147483647 до 2147483647. Тем не менее, код возврата может принимать значение на 1 (единичку) меньше минимума. Проверим? Bash Call :ErrorLevelMinimal Echo %ErrorLevel% pause&goto :eof :ErrorLevelMinimal exit /b -2147483648 Ошибки 16) Пробелы тоже могут являться частью названия переменной Частая ошибка новичков
Добавлено через 15 часов 20 минут Интересное обсуждение Открывающаяся скобка в командном процессоре Подключайтесь!
9
|
23.01.2013, 01:57 [ТС] | 10 | ||||||||||
Ошибки
17) Запятая и точка с запятой - разделители аргументов Точка с запятой ( ; ) и запятая ( , ) являются наравне с пробелом - разделителями аргументов. Помните об этом, когда передаете параметры BAT-файлу или подпрограмме. Например, время %time% в формате 23:59:59,99
18) Листинг текущего каталога или корневого Если указать Bash Dir C: команда выведет листинг текущего каталога, а не корневого. Вот так верно: Bash dir c:\
2
|
25.01.2013, 22:31 [ТС] | 11 | |||||
19) Максимальная глубина рекурсии = 593*.
Это означает, что подпрограмма может вызвать себя рекурсивно 592 раза (+1 - вызов из основного тела скрипта). После этого завершается с критическим падением.
Эта цифра будет также зависеть от: 1) кол-ва и объема данных, передаваемых по каждой цепочке рекурсии 2) размера стэка? - что на него влияет и как настроить - тут я уже не знаю
0
|
26.01.2013, 02:48 [ТС] | 12 | |||||
20) Просто эксперименты предельных возможностей переменной
(или бенчмаркинг языка интерпретатора и стэка, называйте, как хотите) Максимальная длина значения переменной по результатам тестов составляет 8185 символов. Присвоение такого значения другой переменной возможно только через знаки ( ! ) % раскрывает переменные со значением макс. длинной 8183 знака. Иначе возникает критическая ошибка "Слишком длинная входная строка". При этом ком. интерпретатор иногда начинает вести себя нестабильно уже при длине строки > 8174 знака. Оператор IF может нормально сравнивать операнды с переменными длиной 8181 знак (+ макс. 11 знаков рядом с ней опционально), иначе возвращает FALSE. Сам тест
20.1) Максимальная длинна команды в командной строке составляет 8191 символ Для Microsoft Windows 2000, Windows NT 4.0 (и ранее?) этот показатель составляет 2047 символ. Источник: http://support.microsoft.com/kb/830473 20.2) Максимальная длина строки, которую можно присвоить переменной через поток - 1023 При вводе из консоли - 1021. См. тему: Найти сумму цифр введенного числа 20.3) Ограничение длинны вывода команды FIND - 4095 символов. См. Найти строку с кавычками
0
|
26.01.2013, 02:51 [ТС] | 13 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
21) Оптимизация кода
Тест скорости выполнения идентичных по функционалу операторов. Все тесты проводятся в 10 млн. итераций. Конфигурация стенда
Microsoft Windows 7 Максимальная Service Pack 1 Процессор: Intel Core i5-2500K ~4000 MHZ. Total RAM: 6111 MB (63% free) Системный раздел C: размер 33 GB (29%) Свободно 114 GB. SSD OCZ Agility 3 if "%Variable%"=="" vs if "%Variable%" equ ""
if Defined Variable vs if not Defined Variable
if Defined Variable vs if "%Variable%" neq ""
if not Defined Variable vs if "%Variable%"==""
А теперь внимание. Конструкция Defined чаcто используется в циклах, чтобы определить, имеет ли значение составная переменная. Я специально до этого момента не использовал отложенное раскрытие переменных через знак ( ! ), чтобы не исказить результаты теста. Тест скорости раскрытия переменных if "%Variable%"=="" vs if "!Variable!"==""
А вот именно из-за оптимизации. Кроме того, ставя %% и потом анализируя логику работы программы, я понимаю, что если поставил %% под циклом, значит значение этой переменной по заданной мною логике не изменяется, а если и изменяться, то не используется в данном контексте (внутри текущих скобок по разработанному алгоритму). Не менее интересный тест. Последовательное раскрытие дважды %% и дважды !!. if "%Variable%"=="%Variable%" vs if "!Variable!"=="!Variable!"
А вот с !! беда (53,83 и 39,73). То есть процедура отложенного расширения переменных кушает процессорное время очень сильно. Тест погрешности методики тестирования (расходы на выполнение оператора Rem) == vs neq
Методика тестирования
Новые тесты: Двойное раскрытие переменных. 10 тыс. итераций. Bash set param=!SomeParam%n%! ::против call set param=%%SomeParam%n%%%
Но проблема в том, что первым вариантом не всегда можно воспользоваться, например, когда переменная n также изменяется в цикле. Тогда Set param=!SomeParam!n!! мы уже не сможем написать. Такой вариант раскроется неверно. А %% раскрываются раньше !! Так что же пользоваться тормозным способом скажете. Давайте посмотрим, какие возможности нам дает переход к подпрограмме по Call Тест скорости циклических переходов к метке для двойного раскрытия переменной (часть имени которой тоже меняется в цикле): Ядро кода
Без таймера и изменения части имени переменной под циклом 1) Через метку:
Результаты:
И не забываем, что при этом получаем дополнительный функционал: изменение части имени оперируемой переменной в этом же цикле. Еще схожий тест - скорость получения полного имени файла в подпрограмме Получить полный путь к папке, заданной двумя точками Заказываем еще тесты!
7
|
26.01.2013, 02:52 [ТС] | 14 | |||||
22) Тест замедления работы операторов при перегрузке оперативной памяти
В этом тесте мы попытаемся максимально нагрузить оперативную память записью максимального количества переменных длинной в 5 КБ. При этом будем измерять время, которое понадобилось для выполнения операторов присвоения с шагом в заполнении 200 переменных. Чтобы заполнить 1 GB ОЗУ понадобится использовать 209715,2 переменных. И в идеале время на инициализацию и присвоение каждым 200-м переменным значения в 5 КБ должно быть одинаковым, но что мы видим в результатах - см. скрин. Получается, что уже при превышении границы в 3 MB операторы начинают работать в 10 раз медленнее. Методика теста
0
|
28.01.2013, 02:49 [ТС] | 15 | ||||||||||
Ошибки синтаксиса:
23) Для команды SET всегда заключайте в кавычки переменную и значение, если ним является изменяемое имя файла Правило касается команды Set, которая находится внутри скобок. Пример правильного синтаксиса:
Демонстрация ошибки:
Чтобы увидеть ошибку, отладку можно осуществить, как обычно, запустив сначала CMD, а уже в нем BAT-файл. Также замечу, что особенностью командного процессора является отображение на экране критических ошибок не после, а до вывода строки команды, ее вызвавшей.
2
|
28.01.2013, 03:13 [ТС] | 16 | |||||||||||||||||||||||||
Заметки
24) Работа с файлами/папками, в именах которых есть буквы украинского алфавита. При попытке произвести одиночные файловые операции с конкретным файлом (папкой), в имени которых есть, к примеру, буква " і " (украинская) получим некритическую ошибку синтаксиса команды. Для исправления ошибки требуется подставить в эту команду имя файла в соответствующей ему исходной кодировке, а именно CodePage 1251
Ее нужно использовать перед командой получения списка файлов. Чаще всего, это цикл FOR.
будут выводиться на экран крякозябрами, ровно как и кириллические имена файлов. Как мы знаем, команда получения файлов dir отрабатывает сразу, а не при каждой итерации цикла. Это означает, что внутри цикла мы можем смело вернуть исходную кодировку. При этом имена файлов мы будем получать уже в CHCP-1251.
Из этого есть 2 выхода: 1) Использовать флаг и выполнить chcp 866 всего 1 раз, и затем сразу сбросить флаг.
2) Предварительно записать имена файлов во временный файл. Вернуть кодировку по-умолчанию. Циклом читать данные из сформированного файла.
2
|
94 / 94 / 4
Регистрация: 25.06.2012
Сообщений: 278
|
|
28.01.2013, 10:48 | 17 |
0
|
29.01.2013, 20:05 [ТС] | 19 | |||||||||||||||||||||||||
Заметка
25) Получение даты и времени файла через цикл и команду For без ключа /S (рекурсия) и подпрограммы В справке к команде For у нас указаны такие основные модификаторы: Для их использования необходимо, чтобы в переменную цикла попал полный путь к файлу, иначе получим пустую строку. Такой вариант выдаст пустую строку вместо даты/времени, т.к. Dir вернет только имена файлов:
Есть 2 варианта: 1) Подставив к переменной цикла часть с полным путем к файлу: 1.1. с использованием подпрограммы: Кликните здесь для просмотра всего текста
1.2. Вложенным циклом (более быстрый вариант от FraidZZ): Кликните здесь для просмотра всего текста
2) установить рабочий каталог = каталогу, где ведется поиск файла. Для этого, предварительно временно перейдем в этот каталог (pushd) и вернемся в исходный (popd) после завершения операции:
2
|
1779 / 761 / 130
Регистрация: 09.04.2011
Сообщений: 1,325
|
||||||
29.01.2013, 21:28 | 20 | |||||
Альтернативный вариант получения свойств файла в заданной директории
Upd. Принцип тот-же, о чём и писал Dragokas - свойства файла можно получить только тогда, когда в цикле указан полный путь до этого файла
2
|
29.01.2013, 21:28 | |
29.01.2013, 21:28 | |
Помогаю со студенческими работами здесь
20
Частые ошибки Частые ошибки winapi Частые ошибки в работе компьютера Частые системные ошибки windows 8 Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |