Comments 59
Краткая форма это не костыль, потому что это не краткая форма, а совершенно отдельный conditional оператор.
bash это отличный скриптовый язык. Логичный. Простой. Понятный. Берущий очень много из архитектуры и идеологии posix. Он совершенно не анахронизм, и если вам что-то кажется нелогичным, так это потому что по своему опыту я вижу, что мало людей пытается именно изучить баш. Подавляющее большинство считают его недоязыком, недостойным даже прочтения стандартного мануала, поэтому и пишут наощупь, потом ругаются.
Кроме того, если у других языков есть библиотеки, то в баше принято активно пользоваться консольными утилитами как gnu так и другими, а у них могут быть совершенно разные стандарты на опции и подход. Многие популярные были созданы и написаны еще до POSIX стандартизации.
Менять его не нужно - это системный скриптовый язык, созданный как шелл, для автоматизации простых рутинных действий в консоли.
Надо что-то сложнее - в линуксе из коробки доступны перл и питон. А баш удобен из-за своей глубокой совместимости. Скрипт написанный 30 лет назад будет работать и сейчас. Сможете так с питоном?
Если условие истинно, то выполняется действие_1. Если же ложно, то выполняется действие_2.
Да в том-то и дело, что это объяснение неверное. Просто неверное, вот и всё. О чем в самом конце (почему-то) и рассказывается.
Предлагаемые же решения работают, но с точки зрения нормального программиста это костыли на костылях, потому что сама краткая форма это уже костыль. Костыль, потому что тысячи и тысячи начинающих bash-пронраммистов погорели на этом, нанеся огромные убытки своим проектам и фирмам. И сколько ещё погорит. А костыль на костыле, потому что вставка лишнего эха может восприняться, как нужная когда-то команда, и быть удалена другим программистом за ненадобностью.
Рискуя нарваться на холивар и кучу минусов, все же замечу. Я писал довольно много скриптов на бэше, но как не любил, так и не люблю этот язык. Язык явно для "жрецов" от компов, какими и были в 60-80-е годы программисты *nix. Сейчас это жуткий анахронизм и надо бы давно заменить его на нормальный, легко читаемый и поддерживаемый язык (только не на такой, как PowerShell в Винде).
Почему неверное? Абсолютно верное) Оно корректно описывает реальное поведение. Но не полностью, что и стало темой данной заметки.
И нет, предлагаемые решения отнюдь не костыли. Писать return в функциях вдруг стало костылём, или что?)
shell-script на самом деле и есть легко читаемый и легко поддерживаемый, а еще, кстати, с потрясающей обратной совместимостью. Просто если что-то не любишь, то хорошо ты писать на этом не будешь.
А холивары приветствую)
Корректно не полностью = некорректно. Странно, что программист не понимает этого. А про легко читаемый, спросите любого программиста, который раньше не писал на бэше. Ответ будет очевидный - ни хрена не понятно. А вот Питон, например, понятен на 90% сразу.
А обратная совместимость для нового языка вообще не нужна.
спросите любого программиста, который раньше не писал на бэше. Ответ будет очевидный - ни хрена не понятно. А вот Питон, например, понятен на 90% сразу.
Простите не соглашусь =)
Синтаксис питона просто похож на современные популярные языки, поэтому кажется что он более-менее очевидный.
Если взять, например, программиста на перле, то думаю ему баш будет понятен сразу =)
Описывает корректно. Описывает не все возможные случаи. Странно, что программист не понимает разницы.
Неверное. && говорит что следующая команда будет выполнена, если предыдущая выполнилась успешно. || говорит что следующая команда будет выполнена, если предыдущая выполнилась неуспешно.
Они не исключают друг друга. Они позволяют перечислить цепочку команд с условием. И в цепочке может быть не action1, action2 а просто любой длины список условий.
command1 && command2 || command3 && command4 || command 5 && command6 && command7 || command8
не очень похоже на if/else? тут будет просто поочередное выполнение команд с пропуском некоторых согласно результатам выполнения и условию цепочки.
Вспомнить, что функция вообще-то всегда возвращает код ошибки, даже когда return явным образом не прописан. И тогда это код последней выполненной команды. Можно просто писать echo в качестве последнего оператора функции. Немного непрофессионально. Чревато тем, что другой программист может удалить или закомментировать этот оператор, попросту не зная о том, что на оператор неявным образом навешано дополнительное действие.
Если уж хочется что то подобное сделать (хотя не советую), то есть такой сахарок как обычное двоеточие ":" которое эквивалентно "true". Пример в интерактивном режиме, для наглядности
[user@host:~]$ false ;:
[user@host:~]$ echo $?
0
[user@host:~]$
но ведь : это не эквивалент тру. Это просто пустая команда. Используется в тех случаях, когда синтаксис требует команду, а вы ничего не хотите делать. Аналог nop в ассемблере.
То что : ничего не делает и результат команды true - это уже следствие того, что команда выполнилась успешно.
То есть это не "сахарок", а конкретная команда со своим значением.
Хотите прям сложностей, гляньте тут https://habr.com/ru/articles/340544/
То что : ничего не делает и результат команды true - это уже следствие того, что команда выполнилась успешно.
Нет такого понятия как результат команды true. Есть понятие нулевого и ненулевого кода выхода. То что я обозначил "true" это тоже встроенная команда, а не значение. Обе команды ":" и "true" (builtin, встроенные) - дают выходной код 0, что и можно использовать в некоторых случаях. Критерий "сахарка" это то, что можно писать один символ вместо четырех.
Краткая форма это не костыль, потому что это не краткая форма, а совершенно отдельный conditional оператор.
bash это отличный скриптовый язык. Логичный. Простой. Понятный. Берущий очень много из архитектуры и идеологии posix. Он совершенно не анахронизм, и если вам что-то кажется нелогичным, так это потому что по своему опыту я вижу, что мало людей пытается именно изучить баш. Подавляющее большинство считают его недоязыком, недостойным даже прочтения стандартного мануала, поэтому и пишут наощупь, потом ругаются.
Кроме того, если у других языков есть библиотеки, то в баше принято активно пользоваться консольными утилитами как gnu так и другими, а у них могут быть совершенно разные стандарты на опции и подход. Многие популярные были созданы и написаны еще до POSIX стандартизации.
Менять его не нужно - это системный скриптовый язык, созданный как шелл, для автоматизации простых рутинных действий в консоли.
Надо что-то сложнее - в линуксе из коробки доступны перл и питон. А баш удобен из-за своей глубокой совместимости. Скрипт написанный 30 лет назад будет работать и сейчас. Сможете так с питоном?
Это, все же, скорее не язык, а "клей" для автоматизации выполнения утилит операционной системы. Использую с удовольствием, но не стал бы сравнивать с питоном
Это язык, который часто используется как клей при интеграции разных систем. Но это полноценный язык.
Согласен. Но я сравниваю его с питоном только в вопросе читаемости и понимания новичком в языке. И кстати, на этом клее некоторые целые поэмы пишут. К счастью, хотя бы, не новый VS Code )
Понимание, что это не классический язык высокого уровня помогает в изучении шел программирования, например в выражении [ тест ] первая скобка - это имя программы /usr/bin/[ , тест - список ее аргументов а ] нужна только для красоты, поэтому запись [тест] без пробелов будет ошибочна, тогда как в обычных ЯП пробелы в похожем выражении не играют никакой роли
Все я понимаю. Но это не делает бэш более читабельным и более свободным от непреднамеренных ошибок.
Не совсем.
[ это внутренняя команда шелла. То, что существует внешняя, это дублирование на случай если у вас какой-то нестандартный шелл, а скрипт должен работать.
ну и не для красоты, а нормальный синтаксис.
тогда как в обычных ЯП пробелы в похожем выражении не играют никакой роли
На питоне пробовали писать?
Да, с не играющими роли пробелами в питоне я погорячился), но, все же имел ввиду использование пробелов как разделителей лексем, в похожих выражениях, например в С, можно написать if(x>y) слитно и это будет распознано синтаксическим анализатором, а скрипт на шел - обертка над командной строкой и пробелы в выражении [ тест ] обязательны. Поэтому я назвал его "клеем", пусть не обижается, он такой же классный как linux и unix, пишу на нем 90 процентов всего, что мне нужно и всем рекомендую для простых задач. Для сложных использую perl, и мечтаю найти время изучить python
А костыль на костыле, потому что вставка лишнего эха может восприняться, как нужная когда-то команда, и быть удалена другим программистом за ненадобностью.
А теперь, коллега, предлагаю на этот раз внимательно прочитать комментируемую статью, и увидеть в ней именно ту же мысль.
Чревато тем, что другой программист может удалить или закомментировать этот оператор, попросту не зная о том, что на оператор неявным образом навешано дополнительное действие.
Самому не странно писать мне в комментах то же самое, что я написал в статье?
Вообще из-за возможного отстрела ног, конструкцию
[ тест ] && действие_1 || действие_2
судя по моему опыту лучше не использовать даже в виде с выше предложенными фиксами. А вот конструкции типа
[ тест ] || return 1
действие_1
действие_2
...
(внутри функции)
либо
[ тест ] && {
действие_1
действие_2
...
}
я лично использую постоянно и вам советую, если вы конечно не любите писать if then ... fi
конструкции.
С какой стати логичные операторы || и && нужно сравнивать с конструкцией if else, если это разные конструкции?
То, что это conditions это одно, но это не сокращенный if else.
Интересно начинается когда делаете так:
[ тест ] && [ тест ] || [ тест ] || [ тест ] && [ тест ]
или так
[ тест ] && ([ тест ] || [ тест ]) && [ тест ]
Как бы совершенно очевидно, что на if оно уже не похоже.
Опять же, совершенно непонятно почему вы, сделав функцию с exit code 1, удивляетесь что она возвращает false для операторов проверяющих exit code?
думаете if будет работать иначе?
function return1() {
echo все ок
return 1
}
if return1; then
echo все хорошо
else
echo все плохо
fi
Можете убедиться, что код вернет "все ок - все плохо" и в if.
В данном случае нелогичность связана с тем, что вы видимо не знаете, что функции и скрипты возвращают код возврата последней выполненной команды, в результате ваша функция с такими строками в конце:
A=нежелательное_значение
[[ $A == 'желаемое_значение' ]] && echo делаем действие
будет возвращать exit code 1 от команды [[ ]] , у которой желаемое значение не совпало, все совершенно очевидно.
Если это ответ к предыдущему комментарию, то вы очевидно не поняли что я хочу сказать. Я не предлагаю заменять if then else конструкцию. Если где-то в коде например нужно без else cделать условную ветку, проще использовать ||, && и операторные скобки. То есть вместо
if [ test ] ; then
...
fi
пишем
[ test ] && {
...
}
Вас ввели в заблуждение действие_1, действие_2, я не должен был их вообще писать. Это просто любой код в операторных скобках.
Не, это я пишу автору статьти, который почему-то считает, что && и || это короткая форма if/else
В шелле && и || нужно ставить в один ряд с ;
То есть команды выполняются по порядку с условием или без условия, их в цепочке может быть сколько угодно, и выполниться может больше одного action
Это не автор статьи так считает, в статье буквально сказано:
Её часто называют сокращенной версией конструкции if-then-else.
Даже навскидку беглый поиск выдаёт множество руководств, где подобная запись именно так и называется.
Потому, что эта конструкция, не являясь буквально if statement, действует схожим образом.
А в статье автор как раз говорит, что с этим надо быть осторожным.
так говорить в принципе можно, если при этом знать и помнить особенности работы
И рассказывает почему.
То, что множество руководств ошибается, не значит что стоит повторять эту ошибку.
Действует она не схожим образом, поскольку конструкция просто связывает две команды (или два блока команд) условием.
И осторожничать нужно тем, кто не хочет этого понимать, и продолжает искать подтверждения своей теории "схожести с if"
ну да, множество руководств ошибается, потому что я с ними не согласен. )) весело
и нет никакой "моей теории", оттого, что вы это повторите хоть сколь угодно большое количество раз, это фактом не станет.
То есть команды выполняются по порядку с условием или без условия, их в
цепочке может быть сколько угодно, и выполниться может больше одного
action
в курсе. Я вообще очень много чего про баш в курсе. Но статья не об этом.
В шелле && и || нужно ставить в один ряд с ;
это интересная версия, но вот как раз не соглашусь. Ничего общего у них нет, кроме того, что 2 команды записываются в одну строку.
А вот команды, соединённыё операторами && и || связаны именно логически (и также, обычно, записываются в одну строку).
Общего именно то, что все три оператора ";", "||" ,"&&" позволяют последовательно выполнить две или более команд. Последние два делают это еще и с условием.
Ну вот вы же любите ссылаться на "множество руководств". Потрудитесь поискать "выполнение цепочки команд в шелле" или "command sequence in shell". Как вообще пишут однострочники.
Я уверен в 7 из 10 результатах будет именно эта последовательность из трех операторов.
В том-то и дело, что ; - это не "оператор, который позволяет последовательно выполнить несколько команд".
Это оператор, который позволяет ЗАПИСАТЬ последовательно несколько команд. Это буквально enter, не более.
А вот && и || - как раз именно вполне подпадают под определение.
С какой стати логичные операторы || и && нужно сравнивать с конструкцией if else, если это разные конструкции?
Потому что они делают одно и то же.
Интересно начинается когда делаете так:
[ тест ] && [ тест ] || [ тест ] || [ тест ] && [ тест ]
Ну а не надо так делать. Причём сразу по нескольким причинам.
Во-первых, это в принципе пиздец. Это не просто поле для ошибок, это практически гарантированная ошибка. Тут в некоторых комментариях предлагают вообще однострочники не использовать, а вы тут такое...
Во вторых -- подобная конструкция и не обсуждается в статье. Что за отдельный род занятий сначала извратить вопрос, а потом самому же с ним спорить?
Как бы совершенно очевидно, что на if оно уже не похоже.
Разумеется не похоже. Я где-то утверждал, что такая конструкция похожа?
Опять же, совершенно непонятно почему вы, сделав функцию с exit code 1,
удивляетесь что она возвращает false для операторов проверяющих exit
code?
Потому что ничего подобного не было.
В данном случае нелогичность связана с тем, что вы видимо не знаете, что
функции и скрипты возвращают код возврата последней выполненной команды
ага, настолько не знаю, что вообще-то сам и написал это в статье.
функция вообще-то всегда возвращает код ошибки, даже когда return явным
образом не прописан. И тогда это код последней выполненной команды
В общем, понятно.
В принципе, есть и третье решение.
есть и четвертое решение. не использовать однострочники вообще, писать как завещал Н.Вирт (if [] ; then else fi), не жлобиться на комментарии, помнить что за [ ] скрывается /bin/test, в пределе забить на башизмы и пользоваться совместимым /bin/sh (в современных линуксах это dash).
согласен насчёт комментариев, но не насчёт всего остального. Особенно насчёт однострочников.
"Не надо использовать" -- это не решение. Это декларация своей неспособности найти решение. Окей, наверное, для некоторых случаев это справедливо, но не для основополагающей и мощной фичи языка.
Не надо использовать, если не умеешь. Это да. Но, быть может, лучше научиться и использовать?
Однострочники не стоит использовать в скриптах
Но опять таки, это просто рекомендация, а не правило, ибо многие вещи легче читаются в одну строку. Цель ведь не юзать однострочники и не избавляться от них, а сделать код более читабельным.
за [] не скрывается /bin/test, там скрывается test. Не путайте внутренние и внешние команды
ни [] ни || ни && не является башизмом, это стандартные команды posix шелла. С другой стороны, bash уже является стандартом де-факто во современных линуксах и за 5-10 лет возможно им станет zsh
И на posix shell скорее следует равняться если работаешь с контейнерами или ембеддед системами, где каждый лишний килобайт на учете.
В bash нет логических выражений. Но есть конструкции, сильно похожие на логические выражения, и это сбивает с толку.
Честно говоря, предложенная конструкция [ условие ] && действие_1 || действие_2
выглядит изначально стремно. Я всегда использовал короткие вариант с одним действием, что понятно и почти всегда оправдано.
почему стремно? В других языках есть тернарный оператор, который выглядит немного похоже. И не является стремным.
Ну, скажем так, он не выглядит очевидным именно для bash
Ну и обычно его применяют в сокращенном варианте
------------
Вы же понимаете, что указанная конструкция в реальности не равна if ... then .... else ... .fi? И поэтому у меня вообще большой вопрос, с чего вообще такая идея возникла
Это одна из первых вещей, которую учишь, начиная заниматься любым программированием. Называется operator precedence. Так вот в шелле, в отличие от, сажем, С, && и || имеют одинаковый приоритет и исполняются слева направо. TRUE && FALSE || TRUE == TRUE, TRUE || FALSE && TRUE == TRUE, TRUE || FALSE && FALSE == FALSE, etc.
Это не "записки баш скриптера", а заметки джуна на полях.
Уж простите, но всё о чем вы говорите - называется "принципы чистого кода". В данном случае хотелось бы особо подчеркнуть читаемость кода.
Как вы правильно заметили, когда результат выражения очевиден, и все последующая логика укладываются в строку 60-80 символов - да, можно использовать тернарное выражение. В остальных случаях - это лишь затрудняет понимание происходящего, усложняет рефакторинг и увеличивает шансы на ошибку.
Не нужно лениться написать несколько дополнительных строчек, если поиск последующей ошибки может занять несколько часов.
А для языков без статической типизации это в несколько раз более актуально.
Я делаю так:
/bin/true && (/bin/false || /bin/true) || echo false
Чтобы что?
Чтобы что?
Ребус, сэр.
Чтобы поведение конструкции
[ условие ] && действие_1 || действие_2
совпадало с поведением if-then-else.
отлично, тема оказалась горячей, как я и хотел. Особо горячие головы меня же и минусуют)
Значит, очередной статье -- быть.
Записки bash-скриптера. Листок первый. Сокращённый if