Pull to refresh

Comments 81

Как понять, что команда вернула значение соответствующее ответу "да"?

Ведь это может быть: "True", "Yes", "LONG_TRUE_COMMAND_KEY_YES" , "1" , ...

Изучать генеалогию тараканов и травмы детства автора команды? Если вариант, что будет хоть какой-то стандарт, или всё, поезд ушёл, принять и страдать?

Как понять, что команда вернула значение соответствующее ответу "да"?

Читать маны, они рулез

Например , команда ps вывалила 2 экрана текста. Это "да" или "нет"?

Читать маны, они рулез

Я именно про это и пишу. У каждого своё видение "как оно должно быть" Зоопарк несогласованный в итоге. И это только вершина айсберга. Что творится в config файлах - психушка отдыхает. Креатив через край. Что по словарю, что по оформлению и отступам.

В том же PowerShell нет проблемы понять, что возвращается $true или $false. А здесь я обязан сходить и понять логику каждого "художника, который так видит"?

PS Это не к тому, что "всё тут отстой", а к тому, что рассказывая про преимущества и мощь, стоит таки упомянуть о тяжёлом наследии из-за длинной истории развития, и что так вот сложилось, запоминать и принимать "как есть" придётся очень многое.

Всё с этим нормально у PowerShell. Да/Нет всегда одинаково пишутся.
И как минимум не надо голову ломать, вспоминая слолько символов "-" перед какой фразой в ключах ставить один/два/ни одного )

А причем тут bash, если это зависит от конкретного приложения?

Кстати, очень ценное замечание. Пожалуй это вопрос не к нему, а в целом к экосистеме, где он применяется. Где каждая маленькая, но гордая утилита делает только что-то одно, но отлично, и со своими закидонами правилами именования ввода-вывода. Язык скрипта тут действительно не особо при делах .

ВСЕ команды в posix (и не только) возвращают код возврата 0, что означает success. А дальше - смотрим как эту команду юзать, чтобы ею что-то именно проверитьт

В том же PowerShell нет проблемы понять, что возвращается $true или $false.

Возвращается значение откуда? Если из внешней программы - то при чём тут bash/powershell? Если из своей функции - ну, договоритесь там как то с собой, что вы будете возвращать;)

Можно спросить код выхода последней команды, $?

Там стандарт, там 0 это "да"

0 - это, как правило, отсутствие ошибки (в С/С++ наоборот) . А как вы его собираетесь интерпретировать - "да", "нет", BLACK_COLOR - от логики работы вашего скрипта зависит.

в С/С++ обычно 0 это тоже отсутствие ошибки а остальное - коды ошибок

Да, с true/false немного перепутал ;)

это проблема того, что автор статьи терминологией владеет плохо.
0 = true
не ноль - false

с этим работают конструкции в шелл скриптах ( &&, ||, if)

Наиболее простым вариантом будет переход на Ansible, если нужно автоматизировать что-то в инфраструктуре, чем разбираться с "травмами детства авторов shell команд".


Наличие удобного инструмента (Ansible), позволяет абстрагироваться от более низкого уровня (shell команды) и позволяет экономить огромное количество времени и избавляет от необходимости читать man по каждой отдельной shell команде.

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

Я сейчас изучаю про bash и по bash полно документации и достаточно хорошей. Проблема в том что сам bash жуткое старьё. Стал искать альтернативу. Остановился на zsh - который стандарт на osx.

Я смотрю вы используете zsh. Мне кажется более актуально написать не очередную статью о bash, а от том как перейти с bash на zsh т.е. пример на bash -> Пример на zsh.

Да я все прогонял через zsh, разницы особо и нет, просто так как не все используют zsh я решил не делать на этом упор. Идея хорошая..

Последняя стабильная версия bash - декабрь 2022. Последняя стабильная версия zsh - июль 2022. Какое старье?
Может вы не видите разницы между POSIX и свои фичи?

Куча неточностей и непонимания на глубоком уровне как все работает.

  1. В тексте - " в Linux она отображает $, когда ожидает команды от пользователя. ",
    а на скриншоте вместо $ у вас ">>". Почему несоответствие?

  2. "Это первая строка скрипта. Шебанг говорит оболочке выполнить его через оболочку bash"
    Ничего шебанг не говорит оболочке. Для оболочки это просто комментарий.

  3. "command [OPTIONS] arguments"
    Неправильно. Правильно будет "command [ARGUMENTS]".
    А вот ARGUMENTS уже могут быть или [OPTIONS] или [PARAMETERS], и те и другие - опциональны в зависимости от команды

  4. В примере "echo "сегодня " `date`"
    В современном мире используют $(), а не ``. И вообще сразу использовать command substitute, просто сказав "обратите внимание, что date нужно указать в кавычках и не пояснив а что собственно происходит?

  5. "sh show_all.sh"
    при этом может оказаться что некоторые баш конструкции выдадут ошибку, потому что ниже у вас есть примеры и [] и [[]]

  6. Пишете "В Bash нет типов данных. В Bash переменная может хранить числовые значения, отдельные символы или строки символов. "
    Но это неправильно. В bash есть типы данных, но баш не строго типизированный язык, поэтому тип данных легко конвертируется в другой, при этом могут быть потери, когда попытаетесь текст конвертнуть в integer. Плюс есть массивы.

  7. "touch: Создать новый файл."
    Вообще-то touch был создан для другого, это побочный эффект, что при попытке открыть несуществующий файл, создается пустой файл. Так можно сказать, что и "echo" это команда для создания новых файлов

  8. if [[ condition ]]; then
    Неправильно. Правильно if EXPRESSION; then...

  9. Вдобавок тут же несколько строк ниже у вас "if [ $num -gt 0 ]; then". Куда пропали двойные квадратные скобки? Почему показывая пример синтаксиса у вас одно одно, а в примере использования чуть ниже - другое? Что будет думать новичок?

  10. В примере с циклом у вас "(( i += 1 ))" - это что такое? Куда доллар пропал? Правильно "$(( i+=1 ))"

  11. "case expression in" А тут уже ошибка наоборот. Тут как раз должно быть не expression, а конкретное значение. Да, его можно создать, но expression может быть например просто выполнение команды, результатом которой может быть просто exit code с пустым output, и что тогда?

  12. И тут же в примере ниже куда-то форматирование пропало:
    fruit="apple"case in "apple") echo "This is a red fruit." ;; "banana") echo "This is a yellow fruit." ;; "orange") echo "This is an orange fruit." ;; *) echo "Unknown fruit." ;;esac

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

  13. "используя $? переменная." - опечатка

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

По моему, и такие посты полезны и имеют право на существование)
Поставил себе основной оболочкой KornShell, конкретно mksh из MirBSD. Потому что понравилась (ну и для разнообразия). Об man MKSH(1) поломал мозг).
@saboteur_kiev, может быть, подскажете хороший гайд по KornShell (на английском пойдёт)

У O'Reilly есть книжка Learning the Korn Shell.

ну есть же официальная документация на http://www.kornshell.com/doc/.
Но я бы не советовал переходить на ksh, потому что его разработка заброшена более 10 лет назад...

Попробовать ksh сподвигли рекоммендации suckless.org, которые для меня авторитетны) Конкретно mksh живёт и поддерживается, последний commit датирован прошлой неделей https://github.com/MirBSD/mksh И, пожалуй, ksh более Posix shell compliant нежели bash)

В целом то я к mksh приноровился и научился использовать достоинства. И, даже, поддерживаю собственный форк)
Но постижение через штудирование man отняло несуразно много времени и сил. В man, да и http://www.kornshell.com/doc информация подаётся в чрезвычайно сжатом виде. Поэтому и повторюсь, гайды и туториалы, пусть и не настолько формально точные и компактные как man, имеют право на существование)

за исключением конструкции [[ ]], я не особо припоминаю какие башизмы я использовал в баш.
Возможно что-то из variable expansion, но в принципе можно просто писать Posix compliant скрипты и выполнять их в любом подходящем шелле, в этом смысле разницы между ksh или bash или zsh вы не заметите.
То есть нет особого смысла переходить на ksh, если вы используете глобальный стандарт.

man не самый удачный вариант, да. Там часто критически не хватает информации и примеров.
Можно изучить https://www.tutorialspoint.com/index.htm и полезные gnu тулзы типа sed/awk/grep с регулярками на более продвинутом уровне, и в принципе этого может быть достаточно с головой.
Еще немного поковырять скрипты bash completion, и в принципе можно будет считать себя гуру шелл скриптов

В пункте 10 всё же не ошибка. Конструкция (( expr )) вполне допустима: вычисляется арифметическое выражение внутри скобок. Но не выполняется постановка значения, только устанавливается код ошибки/возврата $?: 0, если результат вычислений ненулевой; 1, если результат 0 (вот такая не очень очевидная, но ожидаемая логика).

Ничего шебанг не говорит оболочке. Для оболочки это просто комментарий.

не могу с этим согласиться: если вызываешь скрипт как bash script.sh - то вероятно так оно и будет, но, если вызов идёт из текущей оболочки ./script.sh, то в этой строке будет задан интерпретатор. Небольшой пример:

#!/usr/bin/python3

print(123)

При выполнении ./script.sh с данным содержимым, программа напечатает 123

Так а что запускается, когда ты делаешь так?
./script.sh

То есть ты считаешь, что когда ты пишешь в консоли ./script.sh, это оболочка его открывает и считывает шебанг?
Или ядро линукса открывает executable файл, считывает сигнатуру, видит что это сигнатура скрипта и согласно правилу считывает интерпретатор.
Ведь executable файл может быть например elf, а не шелл скрипт.

То есть ты считаешь, что когда ты пишешь в консоли ./script.sh, это оболочка его открывает и считывает шебанг?

Процесс оболочки взывает к функции execve, и там внутренняя магия libc читает сигнатуру файла и решает как его запускать. Так что в некотором роде да ;)

Ну и какое же в некотором роде, если сигнатуру считывает не баш, а execve?

Еще раз повторюсь, для баш - это не сигнатура, а комментарий

Это делается в контексте процесса оболочки - новый процесс еще не создан. Оболочка запускает код из внешней либы. Поэтому в некотором роде да.

Нет, она вызывает системную библиотеку. Также это делают и все остальные процессы - вызывают системную библиотеку.

Самый простой способ пояснить, что шебанг это не шелл конструкция - запустить скрипт так
bash /path/to/your/script

и вы сразу поймете, что он будет игнорировать шебанг, потому что это - комментарий.
Я не понимаю вашего желания юлить и доказать что 1+1=2 при помощи абстракций.

Забыли 0 пункт: Никогда не программируйте на bash

почему это вдруг? отличный универсальный язык автоматизации

Отличный универсальный язык автоматизации это Python

Отличный универсальный язык автоматизации это PowerShell )

Он нигде не работает )

Язык общего назначения — не универсальный? Какая универсальность вам нужна?

Напишите на python скрипт для автоматического переименования файлов по шаблону, например. На bash это 1 недлинная строка.

Ну да, пробелы, спецсимволы, экранирование.. ;)

Для автоматического переименования я лучше буду использовать утилиту с gui интерфейсом, чтобы сразу видеть исходный список файлов, конечный список файлов и возможные ошибки (может статься, что в конечном списке получатся 2+ файлов с одинаковыми именами, и по идее переименовалка должна это событие как-то обрабатывать)

Если бы эта форма генерировалась автоматически из описания в man - купил скачал бы ;)

После долгого использования openssl req/x509 обычно берешь cfssl для создания самоподписанных сертификатов и перестаёшь мучаться)) Ну плюс cert-manager в k8s

Я в том смысле, что к некоторым cli командам gui не помешало бы. Но актуальное, а не сделанное 10 лет just for fun и с тех пор заброшенное.

С одной стороны соглашусь, но для комбайнов типа openssl или mencoder часто всё равно получится либо космический корабль, которым почти невозможно пользоваться, либо что-то почти бесполезное.
Хотя с теми же видеоэнкодерами народ делал полезные гуёвые фронты для нормальных пользователей

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

Странно, документация вроде на уровне, и отладчик встроенный.

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

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

Тут что-то по программистски, не смог разобрать ;) В документации как раз возвращаемые значения указаны. Если код писать не в блокноте, а в IDE - с типами будет проще.

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

Really? Навскидку:

"os.environ

A mapping object representing the string environment. For example, environ['HOME'] is the pathname of your home directory (on some platforms), and is equivalent to getenv("HOME") in C."

Вроде как вполне понятно, что именно возвращается. Некоторые модули, конечно, бывают с отвратительной документацией.. Но это к python не относится же? ;)

что согласно документации вернет type(os.times()) ?

Сеанс чтения манов вслух с выражением :) </s>

Обьект с 5 атрибутами?

Да

os.times()
Out[116]: posix.times_result(user=651.58, system=163.65, children_user=0.9, children_system=2.57, elapsed=17739271.21)

Или кортеж из 5 элементов?

Тоже да

user, system, children_user, children_system, elapsed = os.times()
user
Out[118]: 651.79

Да, это python, можно и так и так ;)

ну тут вы лукавите, мы оба знаем что times_result в доке не описан вообще, а ответ вы получили запустив код

https://docs.python.org/3/search.html?q=times_result

ну и если бы вы действительно читали то знали бы что type() всегда возвращает class, а не объект или кортеж

До сего момента я даже не подозревал о существовании этой функции ;) Открыл документацию, перевёл (как сумел). Да, то что она возвращает - выглядит немного неожиданно, но никакого несоответствия документации я не увидел.

Hidden text

А что это отдельный класс со специальным именем - не знал. Можно и так:

type(os.times())
Out[128]: posix.times_result
import posix
help(posix.times_result)

Python с собой тащит свою документацию - можно и так. Странно, конечно, что локальный help отличается от официального сайта.

Который нужно постоянно ставить
В котором нужно постоянно что-то доустанавливать
В котором случился питон2 vs питон3, а вот скрипт на посикс шелле заработает на машине 20летней давности и 20 лет спустя без проблем
Который совсем не так прост для манипулирования внешними утилитами

Который нужно постоянно ставить

Не более чем 1 раз ;)

В котором нужно постоянно что-то доустанавливать

Хм ;) Не чаще чем 1 раз

питон2 vs питон3

sh vs bash ;)

скрипт на посикс шелле заработает на машине 20летней давности и 20 лет спустя без проблем

Кроме самого sh, нужно чтобы утилиты, к которым он взывает, тоже за 20 лет не поменялись. Сможете это гарантировать?

Который совсем не так прост для манипулирования внешними утилитами

Но и сложностей особых нет.

На каждой машине нужно ставить по разу. А если машин много, а если продакшен, а потом подерживать одинаковую версионность.

скрипты на sh отлично работают в bash и наоборот, если соблюдать posix стандарт

Утилиты, которые вы вызываете из скриптов - это обычный набор gnu tools
И есть POSIX, если его соблюдать при написании, то да - 20 лет назад, 20 лет вперед - будет работать.

На каждой машине нужно ставить по разу. А если машин много, а если продакшен, а потом подерживать одинаковую версионность.

Для этого случая есть специальная сисадминская магия ;)

скрипты на sh отлично работают в bash и наоборот,

Если не использовать башизмов. Если не использовать башизмов - зачем нужен bash? ;)

Специальную сисадминскую магию нужно тоже настраивать и тратить время на поддержание. А баш обновлять не требуется.

" Если не использовать башизмов. Если не использовать башизмов - зачем нужен bash? ;) "
потому что он может работать быстрее, чем более древние оболочки. Например юзать треды.

баш обновлять не требуется.

https://ru.wikipedia.org/wiki/Bashdoor

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

20 лет назад - может не заработать. Да и 20 лет вперед под вопросом, тоже могут потерять обратную совместимость.

К чему это я? Ничем bash принципиально не отличается от python. Разве что каждые полгода не выходит новый релиз. Да, он на древний sh похож. Как python2 на python3 ;)

" 20 лет назад - может не заработать. Да и 20 лет вперед под вопросом, тоже могут потерять обратную совместимость. "

Что не заработать? Пишем posix-compatible конструкции и все работает и 20 лет назад и 30 лет назад.

Пишем posix-compatible конструкции

Не используем bash. Используем sh. ;)

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

разница не слишком велика.

Но она есть. Если хочется совместимости с sh - синтаксис bash не используем. Не используем синтаксис bash == не используем bash.

баш интерпретатор может выполнить тот же скрипт быстрее, например

Не думаю, что высоконагруженные сервисы пишут на bash ;)

PS так то я не против bash ;) Нормальный язык для использования здесь и сейчас. Умение выполнять скрипты sh - приятный бонус. Совместимость со всеми компьютерами +-20 лет вперёд и назад - миф. Ну, например, технически, init-скрипт от apache-1 из debian 1.0 можно запустить на ubuntu 22.04 - только он там бесполезен.

> Если хочется совместимости с sh - синтаксис bash не используем. Не используем синтаксис bash == не используем bash.

Совместимость нужна не с конкретным sh или ash/dash, а с POSIX

А в качестве интерпретатора, баш мне больше интересен в качестве интерактивной оболочки, с его bash completion

> Ну, например, технически, init-скрипт от apache-1 из debian 1.0 можно запустить на ubuntu 22.04 - только он там бесполезен.

А вот инсталляционній скрипт - запустить не проблема. Или какой-нить дефолтный start/stop, граббер и так далее. И в этом - прелесть.

инсталляционній скрипт

Даже если сильно захочется и удастся инсталляционный скрипт из debian 1.0 запустить на современном centos - ничего хорошего не получится.

питон2 vs питон3

уже давно прошел, остался только питон3

Основные команды bash, которые никак к нему не относятся? Даже не читал дальше, т.к. ls, pwd и т.д. из списка, это не команды оболочки, а отдельные программы.

Планирование сценариев с помощью Сron

Здесь критически важно проверить PATH, с которым работает cron. Это потому что PATH при интерактивном использовании bash и PATH при запуске из cron очень даже могут быть разными, что в комбинации с привычкой забивать на обработку ошибок может приводить к весьма плачевным результатам. Ещё имеет смысл убедиться в том, что вы получаете почту на которую cron будет потом присылать вам отчеты по этим вот скриптам.

Вы можете прочитать больше о работе с Cron здесь.

Здесь должна быть ссылка но её нет.

Пробелы, отделяющие открывающий и закрывающие скобки от окружающего текста, обязательны! Пожалуйста, укажите это в статье. Поскольку об эти грабли частенько бьются новички.

смотря где.
Если вы про оператор test - то нужно понимать почему.
если вы про определение массива или command substitution - то не обязательно.

Последнее время всё больше пишу скрипты под zsh (ибо там есть приятности типа zparseopts) когда могу себе позволить. Естественно на целевых машинах zsh в этом случае раскрывается ansible вместе со всякими полезными утилитами типа. htop/iotop/iostat/iftop/lsof/etc.

Когда не получается, то приходится целиться на ash/sh, т.к. в тех условиях bash может просто отсутствовать. А если можно ставить пакеты, то 7MiB на zsh не жалко.

И новичкам, и опытным собаководам крайне рекомендую взять на вооружение shellcheck, невнимательность и некоторое количество corner cases он ловит.

Ну и set -euo pipefail тоже хорошее подспорье, особенно против опечаток в именах переменных.

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

зато getopt тоже все парсит, и работает везде

Знаю, пользоваться, писал многостраничные case/esac)

Моя основная мысль была что для меня мир shell-скриптов спокойно разделился на две части:

  • универсальные переносимые скрипты (ориентируемся на sh, posix utils, без башизмов и опоры на GNU utils);

  • непереносимые скрипты непредназначенные для использования на других машинах (удобство zsh/rg/fd/flock).

И всё чаще я могу себе позволить не писать первые и просто контролировать окружение для вторых. Есть, конечно, исключения типа debian/ubuntu-based контейнеров, где целевая платформа -- ash, ибо тащить в образ лишнее нафиг не сдалось.

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

Sign up to leave a comment.

Articles