Консоль в массы. Переход на светлую сторону. Bash

  • Tutorial
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. Текст статьи обновлен на основании обсуждений в комментариях. Спасибо всем, кто принимал активное участие в обсуждениях. Вы сделали статью еще лучше. И не только статью.
Share post

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 108

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

      Добрый день.


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

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

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


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

        –3
        bash устарел,

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

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


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

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

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

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

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

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

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

                    Добрый день.


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


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

                    • UFO just landed and posted this here
                        0
                        На 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’
                        
                        • UFO just landed and posted this here
                            0

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

                            • UFO just landed and posted this here
                              • UFO just landed and posted this here
                                  0

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


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

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

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


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

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


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

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

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


                          #!/bin/sh

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


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

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

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

                            А вот что то посерьезнее — установщики, сложные бэкап скрипты, все что > 25 строк, я бы предложил Питон, D, Lua, или JScript (windows)
                              –1
                              Все вами перечисленное в принципе уберется в 25 строк на баше (даже меньше, если уметь пользоваться rsync и манипуляцией файлов). Если только нет множества вариантов пользовательских параметров для своего скрипта.
                              • UFO just landed and posted this here
                                  –2
                                  Ваш случай просто подтверждает факт, что для каждой задачи должен быть свой инструмент. Не пишите на bash большие вещи, он для другого сделан.
                                    –2
                                    это проблема постановки задачи и выбора неверного инструмента для ее решения в чистом виде, а не bash'а
                                0

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

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

                                Добрый день.


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

                                  –2

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


                                  . ./script.sh
                                    +2

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

                                      +2

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

                                        0

                                        ваша правда.

                                  +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 раза реже).

                                    0

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


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


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

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


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

                                    Обновил


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

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

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

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


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

                                        0

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

                                          0

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


                                          К примеру, это просто стечение обстоятельство, что на многих системах 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, а вашему скрипту/программе. С одной стороны — гибкость. С другой — уязвимость. Где-то тут на Хабре недавно эксплуатация этой особенности демонстрировалась.

                                            +2

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


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

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

                                              0

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

                                              • UFO just landed and posted this here
                                          0

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

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

                                          Как раз вот


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

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


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

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

                                            Нет уж, извольте
                                          0
                                          Вопрос: ну и зачем вызывался which?


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

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

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

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

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

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

                                            • UFO just landed and posted this here
                                                0
                                                Ну почему bash?


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

                                                  Добрый день.


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

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

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


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

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

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

                                                    +1

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


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


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

                                                      +1

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

                                                        0

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

                                                          0

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

                                                        +2

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


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


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

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


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


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

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

                                                          0

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

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

                                                          Не более.
                                                            +2
                                                            А там systemd :)
                                                              0
                                                              Это была шутка.

                                                              Не более.

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

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

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

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

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

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

                                                                    Вот потом и разгребай такие скрипты…
                                                                    0
                                                                    Давайте расскажите мне про совместимость Python 2 и Python 3 в случае переноса с системы на систему.
                                                                    +1
                                                                    Переход на светлую сторону. Bash
                                                                    Покайтесь, ибо грядёт Ansible с python'ом и j2 под капотом
                                                                      0

                                                                      Добрый день.


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

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

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

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

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

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

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


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

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

                                                                              0
                                                                              Более высокоуровневое API автоматизации, красивые архитектурные решения, описать которые в одном посте не возьмусь, но вот для статьи он точно интереснее чем bash, про которые всё написано даже на русском не по одному разу.
                                                                          0
                                                                          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.

                                                                          есть подозрение что вы путаете лопату и перфоратор, лопатой тоже стену можно пробить, но зачем?
                                                                          +4
                                                                          Возможно, кому-то пригодится Shell Style Guide от Google.
                                                                            0

                                                                            Добрый день.


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

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


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

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

                                                                              –2

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


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

                                                                                0

                                                                                Добрый день.


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


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

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


                                                                                #!/bin/bash

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

                                                                                • UFO just landed and posted this here
                                                                                    0
                                                                                    Мы же серьезные люди и не будем ничего доказывать частными случаями?

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


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

                                                                                      0

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

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

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


                                                                                  #!/usr/bin/env bash
                                                                                    –1

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


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


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


                                                                                    #!/bin/bash

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


                                                                                    #!/usr/bin/env bash

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


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

                                                                                      0
                                                                                        0

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

                                                                                        0
                                                                                        Должен заметить что я тоже постоянно пишу #!/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 уже успел скинуть ссылку на «стэк» постом выше.
                                                                                      0
                                                                                      var1=$1
                                                                                      var2=$2
                                                                                      


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

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


                                                                                        0

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


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

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

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

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

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

                                                                                          0

                                                                                          Only users with full accounts can post comments. Log in, please.