BASH Tips&Tricks #0005: О том, что остаётся за скобками. За двойными.

BASH
BASH, сам по себе весьма мощный интерпретатор команд с очень широкими возможностями (в т.ч. и для написания кода, который не понимает даже его автор сразу после сохранения файла скрипта), становится просто кладезем интересных возможностей тогда, когда вы открываете для себя круглые, но в то же время такие многогранные, — (( двойные+скобки ))!

Дело в том, что выражения, заключенные в круглые двойные скобки, ведут себя так, словно их написали на одном из многочисленных диалектов языка Си (Perl-программисты, цыц, это не про вас!).
Вот, например, как преображаются циклы с использованием этой «super cow power» из мира си-подобных языков:

max_i=12
for ((i=1; i<=max_i; i++)); do
 date -d "2012-$i-01" +%B
done

Выражения, заключенные в «тёплые ладони» двойных круглых скобок помогают избавиться от уродливой конструкции ${} вокруг элементов массива. И вообще как только мы попадаем в чудесный мир Си-подобных кострукций за «двойным ограждением», у нас пропадает всякая необходимость постоянно писать $VAR или уж тем более ${VAR}. Узрите истину!

a=()
b=(14 3 72)
c=5
for ((i=0; i<${#a[@]}; i++ )); do
 (( a[$i]=b[$i]+c ))
done

Двойные скобки — это такая своеобразная пограничная зона между талайским наречием BASH и марсианским языком Си. Как вам, например, проверка арифметических условий?

 a=7
 b=3
 c=12
 if (( (a+b)<c )); then
  echo $c
 fi

А для тех, кто хорошо учился в школе или написал миллион строк на ассемблере, не составит труда вот этот пример решить и без BASH (но после непременно проверьте себя):

 a=45
 if (( ( (a&28)>>2 ) + 3 > 0xC )); then
  doSomething
 fi

Все мы знаем, что BASH умеет очень элегантно прибавлять что-либо в конец строки:

a=45
a+=2
echo $a
--
Output: 452

Но не все знают, что при помощи двойных скобок можно столь же легко и непринуждённо изменить значение «на месте» или даже сделать пост- или пред- инкремент/декремент:

a=45
echo "a=$a"
(( a+=2 ))
echo "1) a=a+2=$a"
(( a-=7 ))
echo "2) a=a-7=$a"
echo "3) (postincr) a=$(( a++ ))"
echo "4) (after postincr) a=$a"
echo "5) (preddecr) a=$(( --a ))"
echo "6) (after preddecr) a=$a"


Интересным применением двойных скобок является тестирование флаговых переменных:

flAllDoneWell=1
(( flAllDoneWell )) && sayThankYouMrJohns

… в том числе и в неявной форме:

getCash=1000
(( getCash )) && go2Cinema

А еще это самый «короткий» (по количеству символов, которые необходимо ввести на клавиатуре :)) способ проверки кода возврата на «неуспешность»:

doStopSomeDaemon
chkDaemonStatus
(( $? )) && \
  echo 'Daemon stopped, good has won again!'
---
If chkDaemonStatus return code is greater than zero
("bad", not running), then daemon not running and the world are in safe


P.S. OK, мы сегодня многое поняли, давайте выполним вот этот кусочек кода и поразмышляем в тишине:

a=3
cat <<STOP
a=$((++a))
STOP
echo "a=$a"

Вы не заметили ничего странного? :)

7 комментариев

avatar
Хорошие у Вас заметки…
хотелось бы добавить, что еще есть возможность использования тернарного оператора
например:
z=20
(( n = z<15 ? 33 : 55 ))
echo $n
avatar
Ого! taha, спасибо, не знал о такой возможности. Похоже, в (( кроется ещё много интересного родом из «больших» языков семейства Си…
avatar
Это нормально… у меня вообще иногда складывается впечатление, что я не знаю bash… причём чем больше узнаю, тем больще оно складывается))
avatar
BASH пишется Just for fun, то есть ради наслаждения процессом и результатом, поэтому его разработчики часто забывают «рекламировать» крутые фичи языка. С другой стороны, если бы они доказали всем, что BASH — это самобытный и весьма оригинальный язык программирования, то затем им пришлось бы долго и нудно объясняться в связи с тем, что это, пожалуй, ещё и один из самых медлительных ЯП. Например, обработка файлов циклами на BASH может быть разумна вообще только в случае ну очень маленьких ones, да и в целом производительность циклов на BASH оставляет желать примерно в 10^4 степени раз лучшего :)
avatar
ещё хочу добавить, что с математическими операциями также хорошо справляется $[] и как не странно тоже поддерживает тернарный оператор)))
z=$[2+5*10]
n=$[z<5?10:20]
echo $n

также мощь (()) не должна затмивать старого доброго [[]]
МОЩЬ [[]]:
z="mystring"
[[ "$z" =~ (str.*) ]] && echo yes
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.