{
    "version": "https:\/\/jsonfeed.org\/version\/1.1",
    "title": "Блоги: заметки с тегом cmd",
    "_rss_description": "Автоматически собираемая лента заметок, написанных в блогах на Эгее",
    "_rss_language": "ru",
    "_itunes_email": "",
    "_itunes_categories_xml": "",
    "_itunes_image": false,
    "_itunes_explicit": "no",
    "home_page_url": "https:\/\/blogengine.me\/blogs\/tags\/cmd\/",
    "feed_url": "https:\/\/blogengine.me\/blogs\/tags\/cmd\/json\/",
    "icon": false,
    "authors": [
        {
            "name": "Илья Бирман",
            "url": "https:\/\/blogengine.me\/blogs\/",
            "avatar": false
        }
    ],
    "items": [
        {
            "id": "127159",
            "url": "https:\/\/bolknote.ru\/all\/cmd-sh\/",
            "title": "cmd.sh",
            "content_html": "<p>Я тут случайно в спор ввязался — можно ли переписать бат-файл так, чтобы он запускался и выводил примерно одинаковый результат на трёх основных операционных системах — Виндоузе, Линуксе и МакОСи.<\/p>\n<p>Речь шла о вполне определённом файле, там запрограммировано небольшое меню и в зависимости от выбранного пункта запускается та или иная, одинаковая для всех ОС, последовательность команд.<\/p>\n<p>Мой собеседник мне не верил, когда я утверждал, что это вполне возможно, и требовал доказательств.<\/p>\n<p>Что ж. Основная моя идея была в том, чтобы заменить команды батника на вызовы баш-функций, которые я спрячу в специальных конструкциях. В Виндоузе файл обработает обычный <tt>cmd.exe<\/tt>, на остальных операционках — <tt>bash<\/tt>.<\/p>\n<p>То, что мне предстояло адаптировать начиналось вполне стандартно:<\/p>\n<pre class=\"e2-text-code\"><code class=\"php\">@ECHO off\nCHCP 1251\nCLS<\/code><\/pre><p>Что тут происходит? Выключается вывод команд на экран (при адаптации это можно игнорировать), ставится однобайтовая кодировка <i>CP1251<\/i> (это хорошо бы учесть) и очищается экран.<\/p>\n<p>Поскольку «баш» этих команд не знает, надо определить три функции, спрятав их от <tt>cmd.exe<\/tt>. Сделать это несложно. Команду «двоеточие» <tt>cmd.exe<\/tt> трактует как начало метки и на дальнейшее не реагирует, а «баш» считает её пустой командой, которую можно точкой с запятой отделить от строки, которую мы хотим спрятать:<\/p>\n<pre class=\"e2-text-code\"><code class=\"bash\">:; @ECHO() { :;}; CLS() { clear; }; CHCP() { ENC=&quot;$1&quot;; }<\/code><\/pre><p>Следующее, чему надо научиться — выводить текст в указанной в батнике кодировке. Это просто:<\/p>\n<pre class=\"e2-text-code\"><code class=\"bash\">:; ECHO.() { echo $@ | iconv -f &quot;CP$ENC&quot;; }<\/code><\/pre><p>Если теперь сделать файл запускаемым и добавить впереди <tt>#!\/bin\/bash<\/tt> (и пренебречь тем, что <tt>cmd.exe<\/tt> успевает ругнуться на эту строку до очистки экрана), у нас получится следующий файл, работающий во всех трёх операционках (кодировка должна быть <i>Windows-1251<\/i>):<\/p>\n<pre class=\"e2-text-code\"><code class=\"bash\">#!\/bin\/bash\n:; @ECHO() { :;}; CLS() { clear; }; CHCP() { ENC=&quot;$1&quot;; }\n@ECHO off\nCHCP 1251\nCLS\n:; ECHO.() { echo $@ | iconv -f &quot;CP$ENC&quot;; }\nECHO. Всем привет<\/code><\/pre><p>А вот дальше сложнее. Само меню организовано в оригинале так:<\/p>\n<pre class=\"e2-text-code\"><code class=\"php\">SET \/p opt=Введите цифру:\n\nIF %opt%==1 GOTO dns_auto\nIF %opt%==2 GOTO dnschange\nIF %opt%==3 GOTO exit<\/code><\/pre><p>В «баше» <tt>goto<\/tt> (переход к метке) отсутствует и это проблема. Аналогов тоже нет. Единственное, что тут можно сделать — использовать вызов функций, но синтаксис в «бате» и «баше» сильно различается.<\/p>\n<p>Как быть? Во-первых, сэмулировать функцией команду <tt>SET<\/tt>, во-вторвых, упростить это место, чтобы меньше было писать кода на «баше»:<\/p>\n<pre class=\"e2-text-code\"><code class=\"bash\">:; SET() { shift; ECHO. -n &quot;${@#*=}&quot;; read -n1 &quot;${1%%=*}&quot;; echo; }\n:; CALL() { eval &quot;$(sed &#039;s\/%\\(.*\\)%\/$\\1\/g&#039; &lt;&lt;&lt; &quot;$1&quot;)&quot; 2&gt;&amp;-; }\n\nSET \/p opt=Введите цифру:\nCALL :menu_%opt%<\/code><\/pre><p>Первая функция парсит команду <tt>SET<\/tt> выводит строку после равно, ждёт ввода и записывает значение в имя переменной, указанной слева от равно.<\/p>\n<p>Вторая — заменяет <tt>CALL<\/tt>. В ней <tt>%variable%<\/tt> заменяется на <tt>$variable<\/tt>, выполняется подстановка переменной и получившееся имя выполняется как команда или функция «баша». Конструкция <tt>2&gt;&amp;-<\/tt> нужна, чтобы избежать вывода ошибки в ситуации, если пользователь введёт что-нибудь не то.<\/p>\n<p>Теперь надо как-то научиться определять функции так, чтобы их нормально «видел» и <tt>cmd.exe<\/tt>, и «баш». В батнике функции — просто любое место программы, начинающееся с метки и заканчивающееся вызовом <tt>GOTO :EOF<\/tt>:<\/p>\n<pre class=\"e2-text-code\"><code class=\"php\">:function_1\nECHO это якобы функция\nGOTO :EOF<\/code><\/pre><p>В «баше» то же самое могло бы выглядеть, например, так:<\/p>\n<pre class=\"e2-text-code\"><code class=\"bash\">function_1() {\n    echo это функция\n}<\/code><\/pre><p>Как это объединить? Я придумал следующий подход:<\/p>\n<pre class=\"e2-text-code\"><code class=\"bash\">:; GOTO() { :; };\n\nGOTO ;#start\n:function_1 (){\nECHO. это [якобы] функция\nGOTO :EOF\n:; }\n\n:;#start<\/code><\/pre><p>В первой строке определяется пустая функция для «баша», потому что эта часть синтаксиса бат-файла нам не нужна.<\/p>\n<p>Ниже идёт строка <tt>GOTO ;#start<\/tt>. С точки зрения «баша» тут две конструкции — <tt>GOTO<\/tt>, которая вызывает определённую выше пустую функцию и <tt>#start<\/tt> — строка комментария, так как в«баше» с «решётки» начинаются комментарии.<\/p>\n<p>В «батнике» же эта же строка будет означать переход к метке с именем <tt>;#start<\/tt>. Этот переход нужен нам, чтобы «обогнуть» строки, которые определены ниже, иначе <tt>cmd.exe<\/tt> начнёт сразу их выполнять, а нам этого не нужно. Их нельзя «спрятать» ниже основной программы, так как в «баше» функции должны определены раньше их вызова.<\/p>\n<p>Что происходит дальше? С точки зрения <tt>cmd.exe<\/tt> ниже расположена метка <tt>:function_1<\/tt> (как показывают мои эксперименты, часть после пробела просто отбрасывается), потом тело функции, команда её завершения <tt>GOTO :EOF<\/tt> и метка с именем <tt>:;}<\/tt>.<\/p>\n<p>С точки зрения «баша» там определяется функция с именем <tt>:function_1<\/tt>, ниже идут вызовы уже определённых мною функций <tt>ECHO.<\/tt> и <tt>GOTO<\/tt>, а ещё ниже — уже знакомая нам пустая команда <tt>:<\/tt> и фигурная скобка, завершающая тело функции.<\/p>\n<p>Соединяем всё вместе и получается следующая программа:<\/p>\n<pre class=\"e2-text-code\"><code class=\"bash\">#!\/bin\/bash\n:; @ECHO() { :;}; CLS() { clear; }; CHCP() { ENC=&quot;$1&quot;; }\n@ECHO off\nCHCP 1251\nCLS\n\n:; GOTO() { :; };\n:; ECHO.() { echo $@ | iconv -f &quot;CP$ENC&quot;; }; PAUSE() { read; }; \n:; SET() { shift; ECHO. -n &quot;${@#*=}&quot;; read &quot;${1%%=*}&quot;; }\n:; CALL() { eval &quot;$(sed &#039;s\/%\\(.*\\)%\/$\\1\/g&#039; &lt;&lt;&lt; &quot;$1&quot;)&quot; 2&gt;&amp;-; }\n\nGOTO ;#start\n\n:menu_1 (){\nECHO. Пункт первый\nGOTO :EOF\n:; }\n\n:menu_2 (){\nECHO. Пункт второй\nGOTO :EOF\n:; }\n\n:;#start\n\nSET \/p opt=Введите цифру:\nCALL :menu_%opt%<\/code><\/pre><p>Мой оппонент признал, что в споре я победил.<\/p>\n",
            "date_published": "2024-04-06T00:26:05+05:00",
            "date_modified": "2024-04-06T15:19:40+05:00",
            "tags": [
                "bash",
                "cmd",
                "программирование"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Sat, 06 Apr 2024 00:26:05 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "127159",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        }
    ],
    "_e2_version": 4079,
    "_e2_ua_string": "Aegea 11.0 (v4079e)"
}