понедельник, 22 марта 2010 г.

[prog] Как не нужно писать bat-файлы под Windows

Пару недель назад поставил себе Scala 2.7.7 “чисто попробовать” – пришлось исправлять евоный scala.bat-файл. Поматюгался на Unix-овых ламеров, которые под Windows фишку не рубят, да и забыл, не до Scala стало. А надысь потребовалось Maven2 себе поставить. И, сюрпрайз, сюрпрайз, там в mvn.bat точно такие же проблемы!

Дело вот в чем: со времен Windows 95 (когда мелкомягкая корпорация заложила очередную мину пользователям – разрешила пробелы в именах файлов) имена приходится заключать в кавычки для того, чтобы защититься от пробелов. Например, если в текущем каталоге есть файл с именем “tasks and plans.txt”, то чтобы просмотреть его содержимое в командной строке нужно будет вводить:

type “tasks and plans.txt”

или

notepad “tasks and plans.txt”

но не:

type tasks and plans.txt

поскольку в последнем случае type будет получать не один аргумент, а три -- “type”, “tasks”, “plans.txt”.

Авторы bat-файлов для Scala и Maven2 решили защищаться от пробелов в именах файлов по полной программе. Например, им нужно проверять значение переменной среды JAVA_HOME. Но, поскольку внутри ее значения могут быть пробелы, то проверку, по их замыслу, обязательно нужно защитить кавычками:

@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome

Но ирония в том, что это работает только для случая, когда в JAVA_HOME пробелов нет. А вот если есть, тогда указанный if раскрывается во что-то такое:

@REM ==== START VALIDATION ====
if not ""c:\Program Files\Java\jdk_16"" == "" goto OkJHome

И при исполнении bat-файла возникает ошибка, поскольку командный процессор видит набор независимых друг от друга фрагментов – “”, c:\Program, Files\Java\jdk_16“”. А возникает эта ошибка из-за того, что в JAVA_HOME уже есть свои кавычки вокруг значения. Поэтому лишняя перестраховка и обрамление %JAVA_HOME% дополнительными кавычками приводят к проблемам.

Пришлось править чужие bat-файлы вот так:

@REM ==== START VALIDATION ====
if not %JAVA_HOME%__ == __ goto OkJHome

10 комментариев:

  1. Странно, никогда не сталкивался с таким. Есть подозрение, что проблема у Вас в другом. Видимо вы присваиваете JAVA_HOME с кавычками, а надо без. Поясню примером:

    C:\WINDOWS\system32>set JAVA_HOME="c:\Program files\Java\jdk_16"

    C:\WINDOWS\system32>if not "%JAVA_HOME%" == "" (echo no) else (echo yes)
    files\Java\jdk_16"" was unexpected at this time.

    C:\WINDOWS\system32>echo %JAVA_HOME%
    "c:\Program files\Java\jdk_16"

    но если сделать без кавычек, то все ок:

    C:\WINDOWS\system32>set JAVA_HOME=c:\Program files\Java\jdk_16

    C:\WINDOWS\system32>if not "%JAVA_HOME%" == "" (echo no) else (echo yes)
    no

    C:\WINDOWS\system32>if "%JAVA_HOME%" == "" (echo no) else (echo yes)
    yes

    C:\WINDOWS\system32>echo %JAVA_HOME%
    c:\Program files\Java\jdk_16

    ОтветитьУдалить
  2. У меня есть подозрение, что у разработчика было два способа написать bat-файл. Один способ (выбранный разработчиком) зависит от наличия кавычек в переменной среды, а второй способ -- нет. Выбран был первый способ. И проблема оказалась у меня, а не у разработчика.

    ОтветитьУдалить
  3. Некоторый опыт использования bat-файлов убедил, что единственный юз-кейс с которым не бывает проблем - это удаление батников в корзину :)

    Более простой по нервам/времени способ - поставить любой скриптовый язык по выбору и работать с ним (или использовать вездесущий JavaScript).

    Кстати, это сносная тема для флейма - оправдано ли использование bat файлов в свежем проекте?

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

    ОтветитьУдалить
  4. >единственный юз-кейс с которым не бывает проблем - это удаление батников в корзину :)

    Ага, есть такое дело. А под Unix-ами туда же отправляются sh-файлы.

    >Кстати, это сносная тема для флейма - оправдано ли использование bat файлов в свежем проекте?

    Я bat-файлы стараюсь использовать только для настройки окружений для себя (т.е. bat-файлы содержат только множество set-ов и никакой логики). Для всего остального предпочитаю Ruby.

    ОтветитьУдалить
  5. Вспомнил! Иногда использую батники для конвертирования фотографий. Изменить формат, размер; вырезать часть фотки, наложить стандартный элемент (рамку, например) и т.д. Связка imagemagik + bat работает нормально. Правда, в ограничения батников тут тоже уперся довольно быстро, когда раздумывал чего будет стоить добавить водяные знаки. От программистских задач там - сгенерировать пару случайных чисел (координаты) и проверить будет ли помещаться надпись на картинке. Вот делать решение через батники не было совершенно никакого желания :) Для себя решил, что если водяные знаки сильно понадобятся, поставлю что-нибудь скриптовое (Python, например) и разберусь с ним. Толку будет больше.

    ОтветитьУдалить
  6. >Изменить формат, размер; вырезать часть фотки, наложить стандартный элемент (рамку, например) и т.д. Связка imagemagik + bat работает нормально.

    У меня была похожая ситуация -- нужно было заснять свои действия на компьютере и создать анимированную картинку (http://files.rsdn.ru/31476/rcgtool-cpp-open-ns-in-action.gif). Тогда с помощью какого-то тула я получил кучу картинок и через imagemagic пришлось их загонять в gif-ку. Я это делал с помощью Ruby.

    Еще временами приходится mp3-файлы перекодировать/переименовывать -- тут тоже на Ruby все проще делать, чем в bat-никах.

    ОтветитьУдалить
  7. Резюме одно. Батники в топку :)
    Вот кстати, любопытная тулза есть для несложных демонстраций http://www.debugmode.com/wink/. Она простенькая, с некоторыми ограничениями, но свое дело делает.

    ОтветитьУдалить
  8. >для несложных демонстраций http://www.debugmode.com/wink/

    О, спасибо большое. Видел когда-то эту прогу, но потом ее URL-ик потерял. Ща сделал себе закладку.

    ОтветитьУдалить
  9. Бобби Бросьтаблицу из деревни Малые Хакари пишет вам:

    set JAVA_HOME=1==1 ( echo Hahaha! ) else if 1==1 ( echo Bebebe! ) else if ...

    И это он сегодня добрый. А завтра мог бы и format c: подставить.

    ОтветитьУдалить