Показать сообщение отдельно

  #2  
Старый 10.04.2006, 16:19
c411k
Reservists Of Antichat - Level 6
Регистрация: 16.07.2005
Сообщений: 653
Провел на форуме:
8854279

Репутация: 2727


По умолчанию

Как можно избежать использования goto?

Любой хоть сколько-то осмысленный *.bat-файл длиной больше 50 строк является ярким лозунгом в поддержку работы Дейкстры «О вреде оператора goto». Мешанина из переходов вперед и назад действительно является кодом «только для записи». Можно ли что-то предпринять по этому поводу?

На самом деле можно. Как правило, большинство меток и переходов используются для организации ветвлений при проверке условий, т.е. банальных if-then-else блоков. В оригинале, bat-язык поддерживал только одну команду в блоке then, что автоматически приводило к идиомам вида:
Код:
if condition goto :THEN
rem Команды ветки ‘else’
rem ...
goto IF_END
:THEN
rem Команды ветки ‘then’
rem ...
:IF_END
Но к счастью, командный интерпретатор cmd.exe современных ОС Windows 2000 и старше поддерживает блоки команд в конструкциях ветвления, что устраняет необходимость применения меток. Блоки команд заключаются в круглые скобки. Выглядит это так (имитируя C/C++ indentation style):
Код:
if condition (
    rem Команды ветки ‘then’
    rem ...
) else (
    rem Команды ветки ‘else’
    rem ...
)
Конкретный пример использования:
Код:
@echo off

set BUILDMODE=%1

if "%BUILDMODE%" == "" (
    echo FAIL: Аргумент является обязательным ^(--debug, --release^)
    exit /b 1
)

rem Удаляем из аргумента все дефисы для упрощения обработки
set BUILDMODE=%BUILDMODE:-=%

if "%BUILDMODE%" == "debug" (
    echo INFO: Устанавливаем debug-режим окружения
    set CCFLAGS=/Od /MDd /Z7
) else (
    echo INFO: Устанавливаем release-режим окружения
    set CCFLAGS=/O2 /MD
)
На мой взгляд, с этим уже вполне можно жить. Но, как всегда, жизнь не так проста, как кажется. Есть одна проблема. Переменные, использующиеся в блоках then и else, раскрываются перед началом выполнения этих блоков, а не в процессе выполнения. В приведенном примере это не вызывает никаких проблем, однако в следующем вызовет:
Код:
if "%BUILDMODE%" == "debug" (
    echo INFO: Устанавливаем debug-режим окружения
    set OPTFLAGS=/Od
    set CCFLAGS=%OPTFLAGS% /MDd /Z7
) else (
    echo INFO: Устанавливаем release-режим окружения
    set OPTFLAGS=/O2
    set CCFLAGS=%OPTFLAGS% /MD
)
Загвоздка в том, что в обоих блоках подстановка переменной OPTFLAGS произойдет до того, как она будет изменена в процессе выполнения этого блока. Соответственно, в CCFLAGS будет подставлено то значение, которое OPTFLAGS имела на момент начала выполнения данного if-блока.

Решается эта проблема путем использования отложенного раскрытия переменных. Переменные, заключенные в !…! вместо %…%, будут раскрыты в их значения только в момент непосредственного использования. Данный режим по умолчанию отключен. Включить его можно либо использованием ключа /V:ON при вызове cmd.exe, либо использованием команды
Код:
setlocal enabledelayedexpansion
в тексте самого bat-файла. Второй способ мне представляется более удобным – не очень здорово требовать от кого-то запуска твоего сценария с определенным параметром.

С учетом сказанного предыдущий «неправильный» пример может быть исправлен так:
Код:
setlocal enabledelayedexpansion

rem ...

if "%BUILDMODE%" == "debug" (
    echo INFO: Setting up debug mode environment
    set OPTFLAGS=/Od
    set CCFLAGS=!OPTFLAGS! /MDd /Z7
) else (
    echo INFO: Setting up release mode environment
    set OPTFLAGS=/O2
    set CCFLAGS=!OPTFLAGS! /MD
)
Вот теперь это почти полноценный if-then-else блок. Почти, потому что если в одной из команд echo у вас встретится закрывающая круглая скобка, то вам необходимо заэкранировать ее символом ^, иначе синтаксический анализатор путается…

Но в любом случае, это гораздо лучше безумного количества меток и переходов.

Как обработать текстовый файл?

Иногда в командном файле необходимо получить доступ к содержимому некоторого текстового файла и некоторым образом это содержимое обработать. Например, прочитать файл настроек программы.

Для привнесения еще большей конкретики в процесс изучения зададимся целью прочитать файл с настройками следующего содержания:
Код:
# Это простой файл с настройками

# Режим сборки
buildmode=release

# Компилятор
compiler=cl.exe

# Архитектура
arch=x86
Ничего сверхъестественного – простой key=value формат с возможностью вставки Unix-style комментариев. Помочь в чтении и обработке этого файла нам сможет команда for. Ее дополнительные опции позволяют задать и разделители, и символ начала комментария, и кое-что еще. Вот командный файл, который выполняет поставленную задачу:
Код:
@echo off

rem Читаем настройки из файла settings.txt, который должен располагаться в
rem том же каталоге, что и bat-файл. Если не удалось распарсить настройки -
rem выходим с ненулевым кодом возврата.
call :read_settings %~dp0\settings.txt || exit /b 1

rem Прочитанные настройки:
echo Build mode  : %BUILDMODE%
echo Compiler    : %COMPILER%
echo Architecture: %ARCH%

rem Выход из сценария. Дальше - только функции.
exit /b 0

rem
rem Функция для чтения настроек из файла.
rem Вход:
rem       %1           - Имя файла с настройками
:read_settings

set SETTINGSFILE=%1

rem Проверка существования файла
if not exist %SETTINGSFILE% (
    echo FAIL: Файл с настройками отсутствует
    exit /b 1
)

rem Обработка файла c настройками
rem Здесь:
rem     eol=# указывает на то, что содержимое строки начиная с символа #
rem     и до ее конца может быть пропущено как комментарий.
rem
rem     delims== указывает, что разделителем значений является символ =
rem 
rem     tokens=1,2 приводит к тому, что в переменную %%i будет занесен первый
rem     токен, а в %%j - второй.
rem 

for /f "eol=# delims== tokens=1,2" %%i in (%SETTINGSFILE%) do (
    rem В переменной i - ключ
    rem В переменной j - значение
    rem Мы транслируем это в переменные окружения
    set %%i=%%j
)

exit /b 0
Обильные комментарии должны помочь легко разобраться, что к чему. За подробностями, как обычно, отошлю к:
Код:
for /?
Кстати, возможности команды for не ограничиваются чтением из файла. Возможно также чтение вывода другой команды. Например, так:
Код:
@echo off

for /f "tokens=* usebackq" %%i in (`cmd.exe /c ver`) do (
    set VERSION=%%i
)

echo %VERSION%
Особенно меня умиляет наличие опции “usebackq”, которая делает синтаксис отдаленно похожим на юниксовый. И в стенах царства Билла есть граждане, скучающие по /bin/sh и пытающиеся хоть как-то скрасить существование свое и окружающих. Следующий совет это также косвенно подтверждает.

Что это за упомянутые ранее операторы объединения команд?

Это операторы &, && и ||. Они практически совсем не освещены в документации, но полезны в повседневности. Они позволяют объединять несколько команд в одну, т.е. примерно так:
Код:
command1 & command2
command1 && command2
command1 || command2
Форма этих операторов весьма соответствует их содержанию. В случае, пожалуй, наименее полезного оператора & вторая команда будет просто выполнена после первой, т.е. это равносильно простой записи:
Код:
command1
command2
Оператор && гарантирует, что вторая команда будет выполнена только, если первая была выполнена успешно, т.е. с нулевым кодом возврата (он же %errorlevel%). Такие конструкции очень популярны в мире shell-сценариев Unix. Например:
Код:
cd sources && make clean
Я был приятно удивлен, узнав, что cmd.exe тоже умеет выполнять такие конструкции. Это безопаснее и правильнее, нежели простое последовательное выполнение этих команд, и короче и проще, чем строгая проверка и обработка кодов возврата. Очень удобно при написании на скорую руку. Не менее полезен иногда и оператор ||. Суть его тоже логична – выполнить вторую команду, если первая дала сбой. Часто встречается в таких идиомах:
Код:
cd sources || exit 1
Если перейти в каталог sources не удастся, то будет произведен выход с кодом ошибки 1. Если же первая команда отработает нормально, то вторая выполнена не будет. Например, такая простейшая защита помогла бы в случае с cd по UNC-адресу, описанному ранее.

Можно ли написать на bat-языке серьезную программу?

Пожалуй, нет. Серьезная программа должна все-таки выглядеть серьезно. А все написанное на командном языке Windows таковым назвать можно лишь с о-о-о-чень большой натяжкой. Так что для решения более сложных задач автоматизации лучше все-таки взять что-нибудь более функциональное:
  • Perl
  • Python
  • Ruby
  • JScript / VBScript

Последние, кстати, присутствуют в Windows 2000/XP по умолчанию (с некоторыми функциональными различиями) и в целом могут считаться заменой *.bat языку. Однако сдается мне, что *.bat-файлы проживут еще очень долго.

Дай Бог, чтобы я ошибся…

© Автор: Алексей Александров
Источник: RSDN Magazine #2-2005
статейку на sql.ru нашел.
__________________
ПИУ-ПИУ...

Последний раз редактировалось cy4_1o1ka; 11.04.2006 в 01:29.. Причина: ups.. не туда запостил..
 
Ответить с цитированием