Вопрос или проблема
Я где-то читал, что конструкция [..]
в shell-скрипте, такая как [ -e $HOME/temp ]
, соответствует стандарту POSIX. В то время как конструкция [[..]]
, например [[ -e $HOME/temp ]]
, не соответствует ему.
Это правда?
Помимо соответствия POSIX, есть ли что-то, что делает [..]
более выгодным по сравнению с [[..]]
? Или наоборот?
Да, утилита [
, также известная как test
, является стандартной. Вы можете найти HTML-версию ее спецификации POSIX 2024 на сайте OpenGroup.
Существовало предложение специфицировать [[...]]
из ksh в языке POSIX sh
, но оно было в конечном итоге отклонено (переработано в добавление всего лишь нескольких дополнительных операторов к [
, которые являются общими для большинства реализаций).
В разделе обоснование вы можете увидеть следующее:
Условная команда, основанная на KornShell (двойные квадратные скобки
[[]]
), была удалена из описания языка команд оболочки в раннем предложении. Были выдвинуты возражения, что настоящая проблема заключается в неправильном использовании команды теста ([
), и вводить ее в оболочку — неправильный способ решения проблемы. Вместо этого, надлежащая документация и новое резервированное слово оболочки (!) достаточно. Позже предложение добавить[[]]
в Вопросе 8 также было отклонено, потому что существующие реализации оказались подверженными ошибкам так же, как и исторические версии теста, и также было слишком много вариаций в поведении между оболочками, которые поддерживают его.
Некоторые из соответствующих различий между [[...]]
и [
:
- Комбинировать более одного теста в одной инвокации надежнее с помощью
[[...]]
, чем с[...]
(где операторы-a
/-o
фактически устарели, поскольку их нельзя использовать надежно), но использование[ ... ] && [ ... ] || [ ... ]
вполне допустимо и не несет штрафов, так как[
всегда встроен, что делает[[...]]
собственные&&
/||
избыточными (и потенциально запутанными, так как правила приоритета отличаются от&&
/||
в оболочке). [[ $string = $pattern ]]
действительно выполняет сопоставление с образцом, а не сравнение на равенство. Это избыточно, так как стандартная конструкцияcase
может сделать то же самое, и это является источником ошибок, поскольку люди склонны считать, что оставлять переменные без кавычек внутри[[...]]
нормально (что часто так), и забывать использовать[[ $string1 = "$string2" ]]
при сравнении строк на равенство.- Ситуация ухудшается с
[[ $string =~ $pattern ]]
, что реализуется по-разному в зависимости от оболочки. bash и несколько других оболочек сделали неудачное решение, дав кавычкам влиять на операцию regex, что неверно, так как синтаксис regex несовместим с обычной токенизацией оболочки (детали можно найти в Как хранение регулярного выражения в переменной оболочки избегает проблем с кавычками, которые являются специальными для оболочки? и в упомянутой выше ошибке группы Остина). У встроенного[
в zsh и yash также есть оператор=~
. - В большинстве оболочек арифметические операторы
[[...]]
интерпретируют свои операнды как арифметические выражения (что создает уязвимость для инъекций команд для[[ $1 -eq $2 ]]
в большинстве оболочек (и вызывает сюрпризы, когда числа начинаются с 0, что делает их интерпретируемыми как восьмеричные в некоторых оболочках). В то время как POSIX требует, чтобы операнды для[
рассматривались как десятичные целые числа. - Правила разбора в
[[...]]
специфичны для конструкции и варьируются от оболочки к оболочке, в то время как[
является просто простой командой и обрабатывается как любая другая команда.
Таким образом, [
обычно нормально, и, чтобы процитировать POSIX, настоящая проблема заключается в неправильном использовании команды test
([
).
Пока вы помните, что нужно заключать переменные и другие расширения в кавычки (как это требуется для всех команд, а не только для [
) и не используете устаревшие бинарные операторы -a
(и), -o
(или) (и (...)
, которые не нужны, once you can’t combine more than one test), [
вполне безопасен (и надежнее при сравнению чисел).
[[...]]
может быть удобнее, так как позволяет сократить код, может позволить пропустить заключение некоторых расширений в кавычки, но в основном это синтаксический сахар, но за счет необходимости изучения дополнительного синтаксиса и его особенностей.
Ответ или решение
Да, это правда, что конструкция [ ... ]
(также известная как test
) является совместимой с POSIX, тогда как конструкция [[ ... ]]
не является таковой. Как вы правильно заметили, утилита [
входит в стандарт POSIX, что можно подтвердить, ознакомившись с публикацией на сайте The Open Group.
Были предложения добавить конструкцию [[ ... ]]
, происходящую из KornShell, в описание языка POSIX, однако они были отклонены. В принципе, возражения касались того, что реальная проблема заключалась в неправильном использовании команды test
, и вместо этого предлагалось обеспечить адекватную документацию и ввести новое зарезервированное слово в оболочку.
Вот некоторые из основных различий между [[ ... ]]
и [ ... ]
:
-
Объединение условий: С конструкцией
[[ ... ]]
проще и надежнее комбинировать несколько условий в одной команде, в то время как для[ ... ]
использование операторов-a
(AND) и-o
(OR) считается устаревшим и ненадежным, что приводит к путанице. -
Сравнение строк и паттернов: В конструкции
[[ ... ]]
оператор=
выполняет сопоставление с шаблоном, а не стандартное сравнение. Это может привести к ошибкам, если забыть про кавычки около переменных. -
Регулярные выражения: Оператор
=~
в конструкции[[ ... ]]
выполняет сопоставление с регулярным выражением, но реализация может отличаться в разных оболочках, что создает дополнительные сложности. -
Арифметические операторы: В
[[ ... ]]
операторы арифметики интерпретируют свои операнды как арифметические выражения, что может создать уязвимости. В отличие от этого, POSIX требует обработки операндов в[ ... ]
как десятичных целых чисел. -
Правила парсинга: Правила парсинга для
[[ ... ]]
специфичны и могут варьироваться между оболочками, в то время как[ ... ]
обрабатывается как обычная команда.
В отношении преимуществ, у конструкции [ ... ]
есть определенные плюсы. Она является стандартной и поддерживается во всех POSIX-совместимых средах, что делает ваши скрипты более переносимыми. При правильном использовании (т.е. с кавычками и избеганием устаревших операторов) [ ... ]
может быть безопасным и эффективно.
Конструкция [[ ... ]]
, в свою очередь, может облегчить написание кода и уменьшить количество необходимых кавычек, однако она требует изучения дополнительных синтаксических правил и может привести к ошибкам из-за неясностей в реализации.
В заключение, выбор между этими конструкциями должен основываться на ваших требованиях к совместимости, безопасности и удобству написания кода. Правильное использование [ ... ]
является вполне достаточно, если вы будете внимательны к возможным подводным камням, в то время как [[ ... ]]
может предложить больше гибкости, но с возможными последствиями, если не знать его особенностей.