keep-calm-and-bin-bash

Вступление


Удобство использования того или иного инструмента заключается в том, насколько он помогает в решении конкретной задачи. Также важно, чтобы мы могли настроить этот инструмент под свои нужды. Приятным бонусом будет и тот факт, что мы можем расширить и дополнить новыми возможностями наш инструмент.

Мы добрались до самой интересной и увлекательной темы — это скрипты на bash. Когда вы запускаете терминал, внутри него работает специальная программа-оболочка — shell (англ) — интерпретатор команд. Shell понимает все команды, которые вы вводите с клавиатуры, и обрабатывает их. Также выводит сообщения об ошибках, следит за корректностью команд и их синтаксисом. Примером таких команд могут быть: сменить директорию, создать новую директорию, добавить текстовый файл, отредактировать текстовый файл, сохранить изменения и другие.

Программ-оболочек довольно много. Одна из первых удачных реализаций называется sh. Ее разработал Стивен Борн в 1977 году (wiki). В 99% случаев, когда вы работаете за компьютером под управлением OS *nix, оболочкой по умолчанию, которая обрабатывает команды, будет bash. Конечно, есть еще такая оболочка как zsh и другие, но это уже совсем другая история.

Важный момент. Данная статья не является исчерпывающим руководством по скриптам на bash. Главная цель — это рассмотреть основы программирования на bash, показать примеры, дать читателю базовые знания, которых хватит, чтобы двигаться дальше. В конце статьи есть полезные ссылки и вы сможете более подробно изучить данную тему.

Основы


Первый раздел посвящен основам. Рассмотрим с чего должен начинаться любой скрипт. Как определяются переменные и как менять значения этих переменных. Также разберемся как передавать аргументы скрипту и обрабатывать эти аргументы внутри скрипта. Следующий раздел — это ветвления. И последний, но не менее важный раздел — циклы.

Любой скрипт на bash должен начинаться со строки:

#!/bin/bash

Тем самым мы говорим интерпретатору какую программу вызвать, если скрипт исполняемый.

Для улучшения переносимости кода следует указывать такой вариант первой строки:

#!/usr/bin/env bash

Не гарантируется, что на различных системах путь к bash будет одинаковым. Указывая env мы защищаем себя от таких неожиданностей. Повторюсь, это в том случае, если вы планируете использовать свои скрипты на разных системах, компьютерах и т.д. Если такой необходимости нет, первый вариант отлично подойдет. Во всех примерах используется первый вариант.

По умолчанию все файлы, которые создает пользователь, не являются исполняемыми. Мы должны явно указать для конкретного файла этот параметр при помощи команды:

chmod +x <filename>

Можно запустить скрипт и без этого параметра при помощи команды:

bash <filename>

Переменные


Переменная — это именованная область в памяти. Это значит, что у переменной есть имя и оно ссылается на определенную ячейку памяти компьютера. Обычно переменные используются для хранения какой-то информации. Любая переменная состоит из двух частей: имя и значение.

Имя переменной:


  • буквы, цифры, знак нижнего подчеркивания (_)
  • не может начинаться с цифры

Значение переменной:


  • числа, строки (если есть пробелы, то в кавычках), отдельные символы

Создание (перезапись) переменной:


path="$HOME"

Обратите внимание, что перед знаком равно и после него не должно быть пробелов.

Чтение переменной:


"$path" или "${path}"

В большинстве случаев оба варианта работают одинаково. В отдельных случаях, при возникновении неоднозначности интерпретации, корректно будет работать только такая форма записи: "${path}"

Передача аргументов скрипту:


./script.sh arg1 arg2 arg3 … argN

Обработка аргументов внутри скрипта:


"$1" # первый аргумент
"$2" # второй аргумент
"$0" # имя скрипта
"$#" # количество аргументов
"$*" # все аргументы в виде одной строки (слова)
"$@" # аналогичен $*, но при этом каждый параметр представлен как отдельная строка (слово),
   # т.е. параметры не подвергаются какой либо интерпретации

Ну что, пришло время написать первый скрипт. Как это и должно быть, скрипт будет выводить на экран пользователя строку «Hello, world!».

Вот так просто, на bash можно реализовать пример с «Hello, world!». Напоминаю, что все примеры вы можете найти в репозитории. Давайте рассмотрим еще один пример. На этот раз поработаем с переменными, а также с аргументами, которые пользователь передает скрипту.

Обратите внимание на то, что все программы, написанные на bash, обычно имеют расширение .sh.

Ветвления


С основами и переменными немного разобрались. Теперь перейдем к ветвлениям. Для работы с ветвлениями в bash нам доступны следующие конструкции:

  1. if
  2. if/else
  3. if/elif/else
  4. case/in/esac

Первые три варианта схожи и каждая следующая конструкция дополняет предыдущую. Рассмотрим самый простой вариант:

Обратите внимание на то, что в конце каждой строки отсутствует точка с запятой. Есть возможность писать условия (и не только) в одну строку, тогда точка с запятой нужны.

При работе с ветвлениями нам нужно как-то проверять, пуста строка или нет, равно число нулю или не равно, работаем мы с файлом или это директория. Для таких задач в bash существует свой синтаксис. Я его разделил на четыре категории: строки, числа, файлы, логические. Давайте рассмотрим их.

Условия (строки):



Условия (числа/строки):



Условия (файлы):



Условия (логические):



Вариант if/else мы пропустим. А вот if/elif/else предлагаю рассмотреть на примере:

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

Нам осталось рассмотреть последний вариант ветвления — это case/in/esac. Если проводить аналогию с JavaScript, то это вариант реализации switch/case. Рассмотрим эту конструкцию тоже на примере.


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

Циклы


Следующий раздел, который мы рассмотрим — это работа с циклами. Для организации циклов в арсенале bash присутствуют такие конструкции: for/in и while.

Давайте рассмотрим синтаксис for/in:


i — это название переменной, в которую будет передаваться элемент массива. array — это сам массив. На каждой последующей итерации i получает следующее значение из массива. Рассмотрим пример:


Есть список значений: 1 2 3 4 5. Запускаем цикл, который проходится по всем значениям. Создаем переменную file_name. Потом проверяем существует ли файл с таким именем. Если файла нет — создаем его, если есть пропускаем шаг создания.

Вы могли заметить слово continue — это ключевое слово. Позволяет пропустить итерацию и продолжить работу дальше. Также есть ключевое слово break — прерывает выполнение скрипта. Используются эти ключевые слова только в контексте if/else.

Следующая конструкция, которую мы рассмотрим для работы с циклами, будет while:


Предлагаю сразу же рассмотреть пример:


В данном примере есть две строки с ключевым словом read. Рассмотрим более подробно как работают эти строки. А именно: строка номер двенадцать и строка номер восемь. Начнем со строки номер двенадцать. Пока переменная $again равна значению "yes", программа просит ввести имя пользователя и выводит его на экран. После этого скрипт спрашивает, хотим ли мы продолжить. И так до тех пор, пока пользователь не введет любую другую строку вместо yes или просто нажмет Enter. Но гораздо интереснее код на строке номер восемь. Присутствует переменная $name, которая не объявлена до этого. Эта переменная создается динамически и потом считывается ее значение.

Полезные ссылки


  1. Basics of Bash
  2. bash-handbook
  3. Подводные камни Bash
  4. Advanced Bash-Scripting Guide [en] ( Konkase спасибо за ссылку )
  5. Advanced Bash-Scripting Guide ( aso спасибо за ссылку )
  6. Shell Style Guide от Google ( leoismyname спасибо за ссылку )
  7. В чём смысл и преимущества #!/usr/bin/env? ( Borz спасибо за ссылку )
  8. Learn the Command Line
  9. The Command Line Crash Course
  10. Linux Command Line in Русский
  11. Basic Unix commands

Вместо заключения


На этой позитивной ноте мы закончим знакомство с bash. Данной информации достаточно, чтобы реализовать простенькие скрипты, а также автоматизировать повседневные задачи.

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

На этом все. Спасибо за внимание. Кто дочитал до конца, отдельное спасибо. До встречи в следующей статье.

UPD. Текст статьи обновлен на основании обсуждений в комментариях. Спасибо всем, кто принимал активное участие в обсуждениях. Вы сделали статью еще лучше. И не только статью.
Поделиться с друзьями
-->

Комментарии (108)


  1. Konkase
    17.01.2017 00:05
    +3

    Удивительно что нет http://tldp.org/LDP/abs/html/ в полезные ссылках


    1. var_bin
      17.01.2017 10:08
      +2

      Добрый день.


      Спасибо за ссылку. Добавил


  1. NeoCode
    17.01.2017 00:06
    +4

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


    1. monah_tuk
      20.01.2017 06:35

      Боюсь нарваться на минусы. Но синтаксис почти всегда дело привычки и вкусовщина. К примеру, да у bash синтаксис вырвиглазный, но шелл всегда под рукой и логично его для мелких автоматизаций использовать и, со временем, к нему привык и беглый взгляд на скрипты сейчас не вызывает отторжения. С другой стороны, по мне, так в том же Rust и Go синтаксис тоже не менее вырвиглазный, но пока потребностей их использовать не возникало и при взгляде на исходники возникает ощущение отторжения, что не умаляет их прочих достоинств.


      А вот коллеге — нравится синтаксис Rust. А баш вызывает реакцию, похожую на вашу :)


  1. Siemargl
    17.01.2017 00:43
    -3

    bash устарел,

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


    1. NorthDakota
      17.01.2017 01:01
      +7

      Баш это скорее инструмент, а не язык программирования


      1. keydon2
        17.01.2017 05:14
        +7

        Довольно посредственный инструмент.
        Думаю если бы не распространненость никто бы и не юзал.


        1. leoismyname
          17.01.2017 08:11
          +1

          Так можно смело сказать о чем угодно, реально. Баш, как основу для реализации какой-то хитрой запутанной логики, я бы не рекомендовал использовать, слишком многословно получится и, в конечном итоге, неудобно, но для мелких задач – это неплохой инструмент с некоторыми странностями синтаксиса :)


        1. hanovruslan
          19.01.2017 13:25

          Я думаю, что вы еще просто не сталкивались с ситуациями, когда задачу надо решить минимальными средствами. в том числе с минимумом сторонних зависимостей.


          Да, в баше много чего нет. Мне, например, для удобства пришлось самому реализовать передачу внутрь функций и обратно из функций массивов. Точнее, закостылить (порядка 30 строк на всё), но это точно лучше, чем тянуть десятки и то и сотни мегабайт для подобного готового в питоне или чем-то еще более монструозном.


    1. PastorGL
      17.01.2017 01:41
      +6

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

      Вообще, специфические инструменты — они для специфических применений. Те же init.d скрипты с минимальной логикой удобнее всего писать именно на баше, получая бонусом неплохую переносимость.

      Так что я бы не рискнул сказать, что баш устарел. Может, он и не секси по нынешним временам, но по-прежнему полезен.


    1. Nakosika
      17.01.2017 08:50
      -5

      Что написано на баше можно запустить на любой никсовой машине без бубна.


      1. ghostinushanka
        17.01.2017 11:28
        +8

        Я вам приведу простой пример, чтобы было понятнее почему вам минусов накидали.
        Пишете скрипт на bash из под Centos 7.x, кидаете на Ubuntu и оказывается, что там bash линкуется на dash, и ключевого слова function для декларации функции в «даше» нету.
        Потом кидаете этот же скрипт на солярис и выясняете, что версия там доступного баша не 4.х, а 3.х и что новые вкусности вроде associative array, которые вы с такой радостью использовали, не будут работать.
        Потом кидаете этот же скритп на HPUX и оказывается что там баша из коробки нет вообще, а только sh и понятное дело полскрипта просто не срабатывает.
        А дальше либо начинаются танцы с бубном с приведением баша до нужной версии на всех системах (а это не всегда возможно), либо приходится скрипт переписывать с учётом доступных вещей в версии 2.x — для полной обратной совместимости с sh, чтобы-таки работало везде (и то я побоюсь дать 100% гарантию).
        Немного утрированно, но вполне себе реальная рабочая ситуация с которой я тоже сталкивался.

        А после этого начинаются танцы с бубном с флагами для других программ, потому что они могут оказаться очень разными и порой один и тот же флаг делает разные вещи. Посмотрите, скажем, на netstat в Linux и BSD.


        1. var_bin
          17.01.2017 12:20
          -1

          Добрый день.


          Спасибо за ваш комментарий. Да, такая ситуация действительно возможна.
          За время работы с удаленным сервером и на Ubuntu не было таких проблем. Даже больше, все скрипты, что приведены в статье работают в ConEmu и на Windows 10.


          Но опять же, учитывая все те системы, что вы назвали, очень даже реальная ситуация.


          1. nukedsun
            17.01.2017 13:29
            +1

            За время работы с удаленным сервером и на Ubuntu не было таких проблем
            Конечно не было, потому что ваши скрипты начинаются с #!/bin/bash (как я вижу из статьи). Просто ghostinushanka ошибся, bash — он и в ubuntu — bash. На dash линкуется не bash, а /bin/sh. А если в скрипте используются специфические bash-фичи, то по крайней мере странно начинать его с #!/bin/sh


            1. ghostinushanka
              17.01.2017 14:03

              На dash линкуется не bash, а /bin/sh.

              Чтобы не быть голословным я сейчас специально скачал 16.04.1 посмотреть. Тут вы правы. Либо я подзабыл, либо это поведение изменили (на даш я натыкался лет 5 назад)
              А если в скрипте используются специфические bash-фичи, то по крайней мере странно начинать его с #!/bin/sh

              Из собствсенного опыта — многие «Шапочники», к сожалению, этим грешат, и вот почему:
              user@server:[~]: cat /etc/redhat-release
              Red Hat Enterprise Linux Server release 7.1 (Maipo)
              user@server:[~]: stat -c %N $(which sh)
              ‘/usr/bin/sh’ -> ‘bash’
              
              user@server:[~]: cat /etc/redhat-release
              Red Hat Enterprise Linux Server release 6.5 (Santiago)
              user@server:[~]: stat -c %N $(which sh)
              ‘/usr/bin/sh’ -> ‘bash’
              


              1. nukedsun
                17.01.2017 14:42

                либо это поведение изменили (на даш я натыкался лет 5 назад)
                Поведение не меняли. Нашел у себя в парке раритет :)
                # lsb_release -a
                Distributor ID: Ubuntu
                Description:    Ubuntu 9.10
                Release:        9.10
                Codename:       karmic
                
                # ls -l /bin/bash
                -rwxr-xr-x 1 root root 917960 2009-09-14 09:08 /bin/bash
                
                # ls -l /bin/sh
                lrwxrwxrwx 1 root root 4 2009-11-20 18:24 /bin/sh -> dash
                
                Просто это достаточно распространенная ошибка — писать скрипт на bash с использованием его специфики, а начинать его с #!/bin/sh. К примеру, написали скрипт на openSUSE, там все работает (потому что sh — линк на bash). Запустили на Ubuntu — облом.


                1. monah_tuk
                  20.01.2017 06:45

                  На самом деле это недоработка Bash. Нужно напрячься и поискать пруфы, но вроде существует соглашение, если оболочка запускается симлинком с именем sh, то начинать в работать в режиме совместимости с классическим Shell. Тут как раз вопрос переносимости.


                  1. Sirikid
                    20.01.2017 08:42

                    Нашел сейчас упоминание этой фичи в мане `mksh`, насчет `bash` не в курсе.


                  1. nukedsun
                    20.01.2017 12:02

                    Да, действительно, вы правы, есть такое дело. В мане:

                    Если bash вызван с именем sh, он пытается, наколько это возможно, имитировать поведение при запуске старых версий интерпретатора sh, соответствуя при этом стандарту POSIX
                    Мне практика показывает, что слова «наколько это возможно» (as closely as possible) там не зря написаны. В общем, если хочется переносимости, надо следовать posix.


                    1. ZyXI
                      20.01.2017 14:05

                      Кроме того, целью режима совместимости является запуск как можно бо?льшего числа POSIX?совместимых скриптов, а не полная эмуляция POSIX shell. В идеале — все скрипты для POSIX shell запускаются и работают как в POSIX shell, а про несовместимые ничего не говорится — вполне могут и работать.


                      В zsh точно так же.


          1. ghostinushanka
            17.01.2017 13:37
            +1

            ConEmu, это всё-таки эмулятор терминала, а не шелл, так что скрипты работают не в нём, а в шелле конкретной версии, который в ConEmu запускается (а там у вас либо Bash 3/4.x в поставке Cygwin, либо тот же Ubuntu-Bash 4.x в поставке с WSL).
            Чтобы понимать разницу между терминалом, эмулятором терминала, CLI и шеллом, и как оно всё взаимосвязано когда вы запускаете тот же ConEmu, советую покурить хотябы английскую википедию, там неплохо написано.


            1. var_bin
              17.01.2017 13:46
              +1

              Полностью согласен с вами.


              Чтобы понимать разницу между терминалом, эмулятором терминала, CLI и шеллом, и как оно всё взаимосвязано когда вы запускаете тот же ConEmu, советую покурить хотябы английскую википедию, там неплохо написано.

              Курил ) Норм зашло.


              Не хотел ввести в заблуждение.


        1. monah_tuk
          20.01.2017 06:41

          кидаете на Ubuntu и оказывается, что там bash линкуется на dash

          стоп. Bash никуда не линкуется. А вот sh линкуется на dash по умолчанию и поэтому многие скрипты, которые пишут:


          #!/bin/sh

          а используют башизмы начинают обламываться. Но мне одному кажется, что это сродни использования, например, memfd и ругаться, что переносимость у C хреновая, когда на целевой OS (не Linux или Linux < 3.17) оно не заработало или начало валиться?


          UPD: уже увидел обсуждение этого нюанса.


    1. bogolt
      17.01.2017 10:37
      +1

      Писал как-то установщик для линусовой тулзы. Вначале сделал все на питоне, потом переделал на баш. Стало меньше кода, он стал проще и понятнее.


      1. Siemargl
        17.01.2017 11:23

        Можно запостить сюда оба варианта под спойлеры?

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


    1. vvpoloskin
      17.01.2017 10:42
      +1

      Ну-ка расскажите нам, чем bash устарел и на чем проще накидать однострочник (в том числе в кронтаб) или на каком языке проще (и быстрее) манипулировать файлами и обработкой консольного вывода?


      1. Siemargl
        17.01.2017 11:15

        При чем здесь однострочник? Его можно на чем угодно писать.

        А вот что то посерьезнее — установщики, сложные бэкап скрипты, все что > 25 строк, я бы предложил Питон, D, Lua, или JScript (windows)


        1. vvpoloskin
          17.01.2017 11:30
          -1

          Все вами перечисленное в принципе уберется в 25 строк на баше (даже меньше, если уметь пользоваться rsync и манипуляцией файлов). Если только нет множества вариантов пользовательских параметров для своего скрипта.


          1. sleeply4cat
            17.01.2017 11:39
            +3

            Я работал с баш-скриптом размером чуть менее 5k строк, который вырос из «да что тут сложного, просто eval'им все тестовые сценарии из папки и считаем количество фэйлов». «25 строк» — это чистой воды шапкозакидательство.

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


            1. vvpoloskin
              17.01.2017 11:46
              -2

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


            1. wsf
              17.01.2017 12:21
              -2

              это проблема постановки задачи и выбора неверного инструмента для ее решения в чистом виде, а не bash'а


      1. monah_tuk
        20.01.2017 06:49

        Про устаревание говорят те, кто по жизни забивает гвозди, пусть не микроскопом, так пассатижами просто потому, что они у них всегда в кармане лежат.


  1. igorch96
    17.01.2017 01:50

    Хотелось бы добавить, что совсем не обязательно выставлять скрипту атрибут исполняемого файла. Скрипт можно запустить и командой: bash <имя файла скрипта>.


    1. var_bin
      17.01.2017 18:05

      Добрый день.


      Спасибо за ваш комментарий. Добавил в статью упоминание об этом.


    1. Borz
      17.01.2017 20:09
      -2

      а можно и без указания шела:


      . ./script.sh


      1. ZyXI
        17.01.2017 20:47
        +2

        Нет, нельзя. Ни в коем случае не запускайте так скрипты: bash script.sh запускается в отдельном процессе. Если скрипт для своих нужд перейдёт в другой каталог, добавит/удалит путей в $PATH, изменит $IFS или что?то подобное и, разумеется, не восстановит состояние (разумеется — это же скрипт — оболочка, в которой он будет запущен, будет убита по окончанию скрипта), то с последствиями всего этого при использовании команды . вам придётся иметь дело в своей более не уютной интерактивной сессии, т.к. скрипт будет запущен внутри этого процесса.


        1. ZyXI
          17.01.2017 20:57
          +2

          И ещё одно соображение: скрипт запускается с настройками, предназначенными для исполнения скриптов. А при использовании . из интерактивной сессии будут использоваться интерактивные настройки, что может изменить поведение. В т.ч. активны aliasы (обычно не особо проблемно, но если что?то не так, то вы получите ошибку на ровном месте, а aliasов у меня много, в т.ч. на распространённые вещи вроде alias cp='cp -iR').


          1. Borz
            17.01.2017 21:44

            ваша правда.


  1. ZyXI
    17.01.2017 01:55
    +15

    Для начала нам нужно знать где находится bash. Эта информация нам понадобиться при написании самих скриптов. Чтобы узнать где находиться bash, выполните следующую команду:
    which bash # где находится bash


    Любой скрипт на bash должен начинаться со строки:
    #!/bin/bash

    Вопрос: ну и зачем вызывался which?


    Обработка аргументов внутри скрипта:

    Куда делась $@?


    Первые три варианта схожи и каждая следующая конструкция дополняет предыдущую. Рассмотрим самый простой вариант:
    if [[ condition ]]
    …

    Такое описание if мне категорически не нравится, оно предполагает, что [[ … ]] является частью синтаксиса if, хотя это совершенно не так. После if идёт команда — любая. Упрощения неуместны, вещи вроде if grep 'foo' ../bar встречаются слишком часто, чтобы это игнорировать и отложить «на потом». Аналогично с while, здесь даже хуже: [в /usr/share/bash-completion] echo $[1.0 * $(ag 'if \[\['|wc -l)/$(ag 'if \w'|wc -l)] даёт 6.3 (ваш вариант используется в шесть с лишним раз чаще), тогда как echo $[1.0 * $(ag 'while \[\['|wc -l)/$(ag 'while \w'|wc -l)] даёт 0.74 ([[ используется в 1,4 раза реже).


    1. var_bin
      17.01.2017 17:25

      Добрый день, Николай.


      Спасибо за ваш комментарий.


      Вопрос: ну и зачем вызывался which?

      Чтобы узнать где находится сам bash


      Куда делась $@?

      Обновил


      Такое описание if мне категорически не нравится, оно предполагает...

      Да с if, while немного незадача вышла. Хотя на разных ресурсах предлагаю по-разному. И тот же Google рекоммендует if в разных вариациях


      1. ZyXI
        17.01.2017 17:38
        +1

        Чтобы узнать где находится сам bash

        А зачем это узнавать, если в первой строчке всё равно #!/bin/bash? И оно должно так и оставаться, потому что это стандарт де?факто. Даже если в вашем дистрибутиве /bin — это ссылка на /usr/bin и она отсутствует в $PATH по?умолчанию (и, соответственно, which bash выдаст не то).


        Я это к тому, что вы предложили узнать, где bash, но дальше полученное знание нигде не используется.


        1. ZyXI
          17.01.2017 17:48

          Тут ниже говорят, что новым стандартом де?факто должно быть #!/usr/bin/env bash. Но и с ним which не нужен.


          1. monah_tuk
            20.01.2017 06:58

            На эту тему очень много неоднозначностей.


            К примеру, это просто стечение обстоятельство, что на многих системах env лежит в /usr/bin/. Встречаются и такие, на которых /bin/env (мне какая-то сборка специализированная попадалась). Расположение этой команды не регламентирутся никакими стандартами, типа FHS.


            Т.е. собственно на такой паттерн можно рассчитывать точно так же, как на /bin/bash.


            Затем, это повод для вмешательств такого рода:


            cd /tmp
            touch bash
            chmod +x bash
            vim bash
            export PATH=/tmp:$PATH

            после чего, выполняя любой скрипт с #!/usr/bin/env bash, он будет передаваться на управление не bash, а вашему скрипту/программе. С одной стороны — гибкость. С другой — уязвимость. Где-то тут на Хабре недавно эксплуатация этой особенности демонстрировалась.


            1. ZyXI
              20.01.2017 14:15
              +2

              Не получится с bash, подменят так mv. И вообще, если есть доступ к переменным окружения то можно и такое устроить:


              env BASH_FUNC_echo%%='() { builtin echo vulnerable $@; }' bash -c 'echo abc'

              Это я к тому, что никаких новых уязвимостей #!/usr/bin/env не создаст.


              1. monah_tuk
                23.01.2017 08:29

                О, про такой трюк не знал. Но было бы неплохо иметь гарантию нахождения env в каком-то определённом месте. И лучше бы это было просто /bin/env. А то /usr/bin/env ни разу не регламентировано, хотя, де-факто, стандарт и дистростроители вряд ли это будут ломать.


                1. Sirikid
                  23.01.2017 08:41

                  GoboLinux
                  Не знаю как ребята решали проблемы с совместимостью, сам недавно узнал про этот дистр и идея очень понравилась, но покопаться в кишочках времени ещё не нашел.


        1. var_bin
          17.01.2017 17:53

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


      1. ZyXI
        17.01.2017 21:09

        Куда делась $@?
        Обновил

        Как раз вот


        "$*" # все аргументы в виде одной строки (слова)

        можно было бы и опустить: быстрое исследование ((cd /usr/share/bash-completion ; ag '\$(?:\{[#!]?)?\*')) показывает, что во всём каталоге /usr/share/bash-completion $* встречается ровно 1 (один!) раз — в строке 258 local items="${*}" файла helpers/gentoo-common.sh. Ещё один раз в комментарии и два раза в виде \$* (т.е., по сути, не переменной $*). Заметьте: подсветка синтаксиса хабра даже не подсветила эту бесполезную вещь как переменную:


        всё подсвечено, а $* — нет


        1. var_bin
          17.01.2017 21:19

          > можно было бы и опустить…

          Нет уж, извольте


    1. taujavarob
      17.01.2017 18:30

      Вопрос: ну и зачем вызывался which?


      Версия: Чтобы узнать где находиться bash и поместить его (если он не там) в /bin/bash?

      Возможно. ;-)


  1. ZyXI
    17.01.2017 02:06
    +7

    И ещё: var1=$1, [[ -f $1 ]], rm $1 и множество подобных — можете мне точно сказать, в каких случаях не нужно использовать "" вокруг переменной и почему? Лучше сразу показывать новичкам безопасный код и писать кавычки просто везде, поскольку это проще, чем помнить, где их можно и не писать и исправлять опасные привычки.


    1. saege5b
      17.01.2017 10:13
      +1

      Вот да.
      Я надеялся что этот момент будет освещён.
      Я 99% времени трачу потом на попытку хоть как-то расставить кавычки, что бы оно хоть как-то работало.


    1. var_bin
      17.01.2017 18:25

      По поводу кавычек вы правы. Хотя и спорный вопрос. Тут можно растянуть обсуждение надолго. Если кратко, то желательно использовать двойные кавычки ("") при обращении к переменным. Это предотвратит интерпретацию специальных символов, которые могут содержаться в именах переменных, за исключением $, ` (обратная кавычка) и \ (escape — обратный слэш).

      Но опять же, в данном, конкретном случае — это лишнее


      1. ZyXI
        17.01.2017 18:45
        +2

        При чём тут имена? Двойные кавычки нужны, чтобы файл с именем home с именем malicious -rf (с пробелом в конце) не привёл к удалению содержимого /home, когда вы будете писать rm $1. Кавычки нужны, чтобы значения переменных оставались одним куском, имена тут ни при чём. И в данном конкретном случае это совсем не лишнее: во?первых, писать кавычки нужно всегда, просто чтобы написать их и когда нужно. Во?вторых, вы пишете rm $1, причём $1 получен от пользователя. А если я хочу подобным скриптом удалить что?то в ~/.wine/drive_c/Program Files?


  1. Sirikid
    17.01.2017 03:14
    +4

    В 99% случаев, когда вы работаете за компьютером под управлением OS *nix, оболочкой по умолчанию, которая обрабатывает команды, будет bash.

    Ну почему bash? В 99% случаев человек напишет скрипт не используя bash-специфичные вещи, но по прежнему будет зависеть от него. bash медленный и не стандартизированный, пишите на POSIX sh и получите множество совместимых реализаций и пресловутую переносимость.


    Почему ничего не сказано про test aka [?


    1. aso
      17.01.2017 08:45

      Ну почему bash?


      В нём есть ряд забавных приколов, которые весьма удобны при программировании — и чего нет в sh/dash.
      Да, и автор зря, пмсм — не указал вот эту замечательную книжку.
      Хоть она и «advanced», но написано неплохо, и кривая освоения в ней достаточно плавная.


      1. var_bin
        17.01.2017 09:26

        Добрый день.


        Спасибо за ссылку. Добавил


  1. rule
    17.01.2017 04:24
    +1

    я всем начинающим консолеводам рекомендую fish, понятно что bash и shell — это основные игроки, но fish намного проще и интуитивней, особенно для начинающих, ну и поставить фиш не так сложно.


    1. ZyXI
      17.01.2017 04:50
      +2

      fish слишком ограничен. Если твоё понимание того, как должна вести себя консоль совпадает с авторскими и тебя устраивают урезанные возможности по написанию однострочников — то всё в порядке. Но с моей точки зрения это не «начинающий консолевод», а «я иногда немного пользуюсь консолью и не собираюсь пользоваться ею интенсивнее».


      И я не знаю, с чего вы решили, что «поставить фиш не так сложно» — fish, вообще?то, единственная оболочка, которой требуются runtime файлы (то, что в /usr/share/fish) для базовых возможностей (того же math). Bash такое нужно только для автодополнения. Zsh — автодополнение и некоторые дополнительные возможности, но она вполне обходится и без них. Различным POSIX shell ничего вообще не нужно: продвинутого возможностей нет, даже более?менее интелектуального автодополнения. Ну а там, где можно ставить fish/bash/zsh обычно установка любой из альтернатив — дело одного запроса пакетному менеджеру. Несмотря на требования наличия runtime файлов никаких трудностей ни с одной оболочкой я не испытывал, что ставя пакетным менеджером, что используя make install для установки тестируемой версии в нестандартный (не /usr или /usr/local, а что?то вроде ~/.local-fish) каталог.


      И что такое «shell»? Вообще?то это слово обозначает просто «оболочка» — класс программ, в который входят и zsh, и fish, и bash, и busybox, и dash, и много чего ещё.


      1. rule
        17.01.2017 05:30
        +1

        1. чем наличие рантайма плохо? Вы же не собираетесь деплоить фиш с одной машины на другую.
        2. фиш легче освоить так как там более понятный и привычный синтакисис, как минимум для программистов, я честно пользоватлся bash лет 12, и до сих пор не могу привыкнуть к его синтаксису, хотя мой юз-кейс ограничен гитом, cat и скриптами автоматизации максиму строк на 200-300.
        3. Я не считаю что более сложные вещи, нежели то что можно уместить в 20-40 команд нужно писать на скрипте интерпретатора, поддерживать такое хозяйство тяжело, мне лично проще взять ruby или golang и накидать быстренько более сложную логику в виде пиложения: намного богаче набор библиотек, легко поддерживать и тд. Хотя сложные вещи пытался писать: системы сборки, тулчейны и деплой скрипты. Всё это геморойно, как по мне.
        4. shell, я имею вввиду «sh» — Bourne shell.


        1. ZyXI
          17.01.2017 08:23

          1. Тем, что чем больше файлов, тем больше потенциальных проблем. С моей точки зрения fish не отличается от zsh по сложности установки, но если бы меня спросили, какая оболочка должна была бы быть более сложной, то я бы назвал fish.
          2. Я не отрицаю, что он простой. Но ограниченный: Cannot save multi-line output in a variable — запросу уже четыре полных года. Я не могу нормально работать с таким языком, даже несмотря на то, что он понятнее zsh. А на zsh можно отлично писать write-only однострочники там, где в других оболочках пришлось бы писать скрипты.
          3. Я тоже почти никогда не пишу какие?то сложные скрипты для оболочки. А вот разные сложные однострочники (часто что?то массово скачивающие из интернета) бывает — обычно что?то, подо что perl теоретически подходит лучше, только zsh я знаю лучше. Иногда пишу автоматические тесты, но при большой сложности они переписываются на основном языке проекта.


            Проблема в том, что для fish я писал в основном только powerline-setup.fish, и fish бесил меня даже в таком простом файле:


            1. test, env, math: создаётся впечатление, что у fish просто нет своих возможностей: лишние процессы на каждый чих (math использует внешние программы).
            2. Конструкция вида (dirname (status -f))/../../../scripts/powerline-config меня напрягает: я отлично понимаю, что теоретически dirname может вернуть имя каталога с новой строкой внутри.
            3. Отлаживать совершенно невозможно: нет set -x. fish -d выдаёт слишком много либо слишком мало информации, уровень выдачи информации не изменяется во время работы, на некоторые вопросы ответить по выдаче сложно или нельзя (пытался найти в логе пользователя, кто, где и почему изменяет fish_prompt — не нашёл ничего внятного). И да, если с установкой кучи runtime файлов проблем нет, то вот с тем, что они спамят вывод fish -d проблемы есть.

          4. А она где?то ещё используется? В linux дистрибутивах сейчас в качестве /bin/sh обычно либо bash, либо dash. Иногда busybox, но это больше для систем с ограниченными ресурсами. Теоретически ещё могут быть zsh, а также другие клоны bourne shell вроде posh. В целом это дело обычно называют «POSIX?совместимыми оболочками» и предназначается исключительно для скриптов. Busybox ещё предполагает интерактивное исполнение, но вот у posh и dash оно скорее для галочки и нет даже автодополнения, поэтому игроками они считаться не могут.


            В некоторых дистрибутивах (я использовал из них grml и babun (сборка cygwin)) используется предустановленный zsh. Ещё как?то ставил на виртуалку FreeBSD для тестов, там tcsh в качестве интерактивной оболочки по?умолчанию (скрипты на этом писать на порядок сложнее, чем на любом другом языке из семейства *sh, что я видел).



    1. olegkrasnov
      17.01.2017 14:56

      Как-то узнал про fish и поставил его, протащился. Радости хватило на пару дней. Потом стало неуютно. Узнал про zsh, поставил, протащился, нахлобучил настроек. Через пару дней начались тормоза. Плюнул, запустил man bash, почитал, настроил в .bashrc почти все нужные мне фишки от zsh/fish и успокоился.

      С тех пор всем советую, прежде чем к любому софту искать книги, курсы и прочий хлам, сначала read the fucking manual.


      1. Crandel
        18.01.2017 00:12

        Года два мучался с корявым синтаксисом баша, пока не узнал про fish. И моя душа питониста-перфекциониста наконец-то нашла покой


  1. akzhan
    17.01.2017 04:49
    +1

    да уж, про условия в bash написано вообще ужасно. нет if grep, if [, только if [[.


    остальное на совести автора.


    Ну и понятно, всегда лучше поставить oh-my-zsh и не париться.


    1. ZyXI
      17.01.2017 04:54
      +1

      Oh-my-zsh имеет плохую репутацию у разработчиков самой zsh. Подробно можно расспросить на канале в freenode, но насколько я понял претензии такие: во?первых, OMZ выполняет работу, которую уже выполняет zsh. Во?вторых, когда что?то идёт не так и пользователь приходит на канал, чтобы спросить совета, отлаживать OMZ на порядок сложнее.


      1. akzhan
        17.01.2017 23:47

        Бессмысленно критиковать, не предлагая альтернативу. Если бы они предложили что-то более правильное и удобное, мы бы все уже перешли, включая авторов OMZ.


        1. ZyXI
          18.01.2017 10:04

          Спросите на канале. Я не использую OMZ или альтернативы (если они есть), мне достаточно того, что я могу настроить самостоятельно. Насколько я понял по обсуждениям, именно последнее и предлагается. Ещё в списке рассылки к менеджерам дополнений (не к сборкам как OMZ, а просто к очередным языкоспецифичным пакетным менеджерам) относятся достаточно лояльно (но их я тоже не использую).


    1. var_bin
      17.01.2017 13:43
      +2

      Добрый день, Акжан.


      Спасибо за ваш комментарий.


      да уж, про условия в bash написано вообще ужасно. нет if grep, if [, только if [[.

      В начале статьи есть раздел Важный момент. Сосбственно из-за этого и не упоминались все возможные варианты использования if. Предполагается, что если читателю будет интересно, то он прочитает дополнительную информацию из ресурсов в "Полезных ссылках".


      Привел пример синтаксиса, который использую ежедневно. Не испытываю проблем с написанием if [[ ... ]]. Хотя тут во всех примерах if [ ... ]. А тут в большинстве рекоммендаций используется вариант if [[ ... ]]


      Ну и понятно, всегда лучше поставить oh-my-zsh и не париться.

      Сарказм детектед или нет?


      1. akzhan
        17.01.2017 23:48

        Скорее из жизни. Я и вправду bash использую только в железобетонных скриптах, где есть уверенность только в наличии bash.


  1. d_olex
    17.01.2017 07:35
    +1

    А в чем заключается реальный прикладной смысл писать что-либо на bash в 2017 году? За все ОС не скажу, но OS X и самые популярные Linux дистрибутивы даже в минимальной инсталяции включают в себя Python — он проще, производительнее и удобнее bash, для него есть много всяких батареек для удобного и лаконичного написания шелл скриптов.


    1. S_A
      17.01.2017 08:11
      -2

      Надо знать bash…
      … чтобы генерировать скрипты service application command :)

      Не более.


      1. d_olex
        17.01.2017 09:45
        +2

        А там systemd :)


      1. S_A
        17.01.2017 13:47

        Это была шутка.

        Не более.

        P.S. В баше уже более 12 лет почти каждый день работаю.
        P.P.S. Даже свой терминал с картинками делал на python.


    1. acmnu
      17.01.2017 11:45
      +2

      Python очень многословен на фоне bash. Не надо забывать, что сам по себе шелл довольно плох, но его в здравом уме и не используют для написания серьёзных программ. Он нужен как клей в unix среде и с этой ролью он справляется лучше чем python, поскольку имеет специально заточенные под это примитивы, типа перенапрвления потоков, опроса выходных параметров и управления фоновыми задачами. На python все это разумеется есть, но не на уровне примитивов.


    1. Hormiga
      17.01.2017 12:22
      +2

      Вообще, спорно. Языки разные с разными целями и подходом. Есть вещи, которые на bash делает в одну команду, а python — в несколько и нетривиальных.
      Примеры:
      1) Сохранить вывод программы в файл (>, grep, cut и т.д. по необходимости против ProcessOpener и работы с потоками вручную)
      2) Скачать файл на диск (wget против urllib2 подключением ProxyHandler)


      1. Vjatcheslav3345
        17.01.2017 19:27

        Роль клея ещё прочили тиклю. Но что то его не вспоминают сейчас, в отличие от питона.
        А bash лучше применять с известной долей осторожности — современные условия могут потребовать больших переносимых скриптов, на которые он не проектировался.


      1. pansa
        18.01.2017 02:07

        Дооо…
        $ cat /etc/redhat-release
        CentOS Linux release 7.2.1511 (Core)
        $ wget
        -bash: wget: command not found
        $

        Ооопс. С коих пор wget стал bash'ем-то? =)

        Вот потом и разгребай такие скрипты…


    1. Jammarra
      18.01.2017 10:33

      Давайте расскажите мне про совместимость Python 2 и Python 3 в случае переноса с системы на систему.


  1. ls1
    17.01.2017 08:13
    +1

    Переход на светлую сторону. Bash
    Покайтесь, ибо грядёт Ansible с python'ом и j2 под капотом


    1. var_bin
      17.01.2017 10:00

      Добрый день.


      А можно более развернутый комментарий? А то не очень понятно, что вы хотели сказать


      1. ls1
        17.01.2017 11:12

        Просто забеспокоился, что за чёрную магию вы использовали, что даже bash после неё — «светлая сторона». А Ansible — имхо, то чем стоит сегодня заменять bash скрипты


        1. foxmuldercp
          17.01.2017 12:39

          Вы собираетесь использовать Ansible в роли быстрого "однострочника" для запуска перла/грепа/авка/седа?
          Извините.


          1. ls1
            17.01.2017 12:43

            grep != bash, он сам по себе. Не говоря уже про awk


            1. foxmuldercp
              17.01.2017 12:48

              Ну обычно баш — это конвеерная обработка много-данных грепами, седами, перлами, авками и прочими мелкими утилитами.
              Зачем вы предлагаете использовать для этого ансибл — мне непонятно от слова совсем.


              1. ls1
                17.01.2017 13:00

                Если бы роль баша ограничивалась конвеером это бы еще ладно, но ведь большая часть кода написанного на нем — сложные скрипты автоматизации с очень специфичным языком. К тому же как оболочка для конвеера баш не уникален, подойдет любой другой shell


        1. var_bin
          17.01.2017 13:10

          Ни о какой "Черной магии" не идет речь. "Светлая сторона" — это посыл всей серии статей про консоль.


          А Ansible — имхо, то чем стоит сегодня заменять bash скрипты

          Можно пруффы? А то только и слышу, что "bash ужастен...", "Он не годится..."
          Чем лучше Ansible?


          1. ls1
            17.01.2017 13:41

            Более высокоуровневое API автоматизации, красивые архитектурные решения, описать которые в одном посте не возьмусь, но вот для статьи он точно интереснее чем bash, про которые всё написано даже на русском не по одному разу.


    1. wsf
      17.01.2017 14:09

      Ansible is a radically simple IT automation system. It handles configuration-management, application deployment, cloud provisioning, ad-hoc task-execution, and multinode orchestration — including trivializing things like zero downtime rolling updates with load balancers.

      есть подозрение что вы путаете лопату и перфоратор, лопатой тоже стену можно пробить, но зачем?


  1. leoismyname
    17.01.2017 08:16
    +4

    Возможно, кому-то пригодится Shell Style Guide от Google.


    1. var_bin
      17.01.2017 09:29

      Добрый день.


      Спасибо за ссылку. Добавил


  1. FFiX
    17.01.2017 08:45
    +3

    Любой скрипт на bash должен начинаться со строки:
    #!/bin/bash
    


    Вообще говоря, лучше так не писать, за исключением /bin/sh, потому что bash совершенно не обязан лежать в /bin. В той же FreeBSD он ставится из портов совсем не в /bin (возможно, что-то изменилось с моего последнего использования этой системы). А в скриптовых языках вроде python абсолютные пути (помимо того, что бинарь может лежать совершенно не там, где ожидает автор скрипта) еще и ломают virtualenv.

    Более универсальна форма записи:
    #!/usr/bin/env bash
    


    1. acmnu
      17.01.2017 11:53
      -2

      Лучше вообще не писать на баш с расчетом на кросиспользование с Freebsd, поскольку там будет куча проблем c совместимостию gnu и bsd утилит, а расчитывать на установленные порты это рисковано. Поэтому только голый sh, и максимально древние спеки sed, awk, grep.


      Ну и во-вторых, ну ребят, ну все уже. Мне конечно ещё приходится иногда писать с учетом Unix/Aix/Solaris, но для большинства вопросы совместимости уже не стоят. Linux захватил Unix сервера прочно.


    1. var_bin
      17.01.2017 13:21

      Добрый день.


      Спасибо за ваш комментарий.


      Более универсальна форма записи:
      #!/usr/bin/env bash

      Пытался найти пруффы на ваш комментарий тут и тут. Нет такого. Плюс ко всему, большинство литературы и статей, что читал и читаю по bash используют эту строку:


      #!/bin/bash

      Не будете ли вы так любезны поделиться ссылками, где подробно описано, зачем использовать именно такую форму записи?


      1. Sirikid
        17.01.2017 13:32
        +1

        Вики


        Те скрипты, что приведены в статье работают на: Ubuntu, ConEmu, Windows 10, удаленный сервер.

        Мы же серьезные люди и не будем ничего доказывать частными случаями?


        1. var_bin
          17.01.2017 13:53

          Мы же серьезные люди и не будем ничего доказывать частными случаями?

          Тут даже не буду спорить. Не стоит переходить на такой уровень )


          А вот по поводу ссылки на вики, это вы лихо


        1. Borz
          17.01.2017 14:43

          дополнил вики ссылкой на пояснение в ru.stackoverflow


  1. Borz
    17.01.2017 08:49
    +1

    Любой скрипт на bash должен начинаться со строки:
    #!/bin/bash

    Вроде как давно уже рекомендуется эта запись:


    #!/usr/bin/env bash


    1. var_bin
      17.01.2017 13:28
      -1

      Добрый день, Виктор.


      Спасибо за ваш комментарий.


      Выше в ответе на комментарий FFiX описал сиутацию, почем так. Повторюсь. С данной формой записи:


      #!/bin/bash

      за все время использования bash скриптов проблем не испытывал. Поделитесь ссылками, где описано, почему лучше писать:


      #!/usr/bin/env bash

      Чтобы к следующей статье я был более подготовлен и не использовал в своей работе устаревшие подходы и решения


      Те скрипты, что приведены в статье работают на: Ubuntu, ConEmu, Windows 10, удаленный сервер.


      1. Borz
        17.01.2017 14:12

        1. var_bin
          17.01.2017 14:55

          Спасибо за ссылку. Добавил в список полезных. Позже обновлю примеры


      1. ghostinushanka
        17.01.2017 14:17

        Должен заметить что я тоже постоянно пишу #!/usr/bin/env что_то_там, и не только для баша. Я не вспомню где я это в своё время нарыл, но связано это с тем что баш (да и не только) не обязательно валяется в /bin, см. взять хотябы HP-UX из моего поста выше.
        Вот простой пример:

        [user@server ~]$ uname -a
        HP-UX server_name B.11.31 U ia64 redacted_machine_id unlimited-user license
        [user@server ~]$ which bash
        /usr/bin/bash
        

        Сталкивался со случаями, когда в системе была одна версия баша/пайтона/перла, а для конкретного пользователя/задачи ставилась другая куда-нибуть в /opt, например просто потому что нет возможности поставить нужные библиотеки. Через environment variable можно тем самым для конкретного пользователя задать где валяется бинарка и иметь переносимый между системами (например dev и prod) код.

        Г-н Borz уже успел скинуть ссылку на «стэк» постом выше.


  1. inkvizitor68sl
    17.01.2017 15:50

    var1=$1
    var2=$2
    


    Дальше читать не стал.

    var1="${1}"
    var2="${2}"
    



    1. var_bin
      17.01.2017 17:18

      Добрый день, Владислав.


      Спасибо за ваш комментарий. В данном конкретном примере это не критично. Что вас не устроило ?


      1. inkvizitor68sl
        17.01.2017 17:33
        +3

        В _примере_ это всегда критично.
        В своём коде пишите как хотите.


    1. ZyXI
      17.01.2017 17:45

      Вообще?то это проблема гигиены кода, отсутствие кавычек здесь не создаёт опасной ситуации, насколько я знаю. Я лично всё равно предпочту написать кавычки — это проще, чем закопаться в man bash, чтобы подтвердить экспериментально проверенное отсутствие проблем. Но вот писать в кавычках ещё и фигурные скобки — зачем? Там текста дальше нет, будет текст — поправите, подсветка синтаксиса в редакторе не даст пропустить ошибку.


      1. inkvizitor68sl
        17.01.2017 18:25

        Отсутствие кавычек и {} в примере под названием «одной переменной присваиваем значение другой» создаёт очень опасную ситуацию в виде тонн говнокода на баше и всяческих сказок вида «этого на баше нельзя, это на баше писать плохо» и прочей чуши.


        1. ZyXI
          17.01.2017 18:48

          С кавычками согласен, с фигурными скобками — нет. Но упомянуть «если нужно, то пишите» не лишне.