Bash는 산술 연산을 할 수 있지만, 정수만 다룰 수 있어요. 실수 연산이 필요하다면 awk나 bc 같은 외부 프로그램을 불러와야 해요.
산술 연산자 목록은 매뉴얼의 Shell Arithmetic에 (우선순위 순으로) 나와 있어요.
게다가 Bash는 특정한 곳에서만 산술 연산을 수행해요. 이런 곳을 산술 "컨텍스트"라고 불러요.
산술 확장은 명령어 치환과 비슷하게 생겼지만, 괄호가 두 개예요.
$(( expression ))
이 문법은 표현식을 평가한 결과로 확장돼요. 보통은 결과를 변수에 담을 때 사용해요:
x=$(( 3 + 4 ))
괄호 안에 공백을 얼마나 넣을지는 원하는 대로 정하면 돼요. 스타일과 가독성의 문제예요.
x=$(( 3 + 4 ))
y=$((5+6))
이 글의 친절한 저자는 괄호 안에서 산술 표현식이 더 잘 보이도록 공백을 넣는 걸 좋아해요.
산술 조건문은 앞에 $ 없이 이중 괄호 안에서 수행해요:
(( expression ))
이 문법은 결과로 확장되지는 않아요. 대신 _종료 상태_를 만들어요.
(( 안에서 Bash는 C 언어와 같은 방식으로 참/거짓을 판단해요. 0은 거짓, 0이 아닌 값은 참이에요.
이건 Bash가 종료 상태를 판단하는 방식과 정반대예요!
이 문법은 보통 두 곳에서 사용해요:
if문이나 while 루프의 COMMAND 부분으로 사용할 때.
if (( x == y - 1 )); then ...
while (( y > 0 )); do ...
변수의 값을 바꾸는 독립적인 명령어로 사용할 때.
(( x = x + 1 ))
echo "new value for x is $x"
이런 식의 산술 연산을 set -e와 함께 사용할 때는 주의해야 해요.
표현식이 0으로 평가되면 종료 상태가 0이 아니게 되고, Bash가 종료돼요.
x=0
(( x++ ))
echo "you will see this"
set -e
x=0
(( x++ ))
echo "shell exits on the previous line; you won't see this"
++ 연산자에 대한 더 자세한 설명은 Postincrement vs Preincrement 글을 참고해요.
산술 컨텍스트인 곳이 몇 군데 더 있어요:
for (( i = 0; i < 10; i++ )); do
echo "$i"
done
숫자로 인덱싱된 배열의 인덱스 부분.
echo "The next element is ${array[x + 1]}"
# ................................^^^^^
배열에 대해 더 알고 싶다면 배열 개념 문서를 읽어봐요.
${var:offset:length} 확장의 오프셋과 길이 부분.
(( expression ))가 let "expresson"보다 선호된다는 점을 기억해요.
산술 조건문과 달리 let 표현식은 파일명 확장과 단어 분리의 영향을 받고, 따옴표로 감싸야 해요.변수에 "정수 속성"이 있을 때의 변수 할당.
# variable x does not have the integer attribute
x="3 + 4"
declare -p x # => declare -- x="3 + 4"
# variable y has the integer attribute
declare -i y
y="4 + 5"
declare -p y # => declare -i y="9"
이 글을 쓴 저자의 생각에는 declare -i의 쓰임새는 제한적이에요.
가장 큰 장점은 "그냥" 산술 연산을 (산술 표현식 문법 없이) 할 수 있다는 점이에요.
다만 이 때문에 코드를 읽는 사람이 헷갈릴 수 있어요. 예상치 못한 곳에서 산술 표현식이 평가되니까요.
산술 컨텍스트의 한 가지 특징은 가독성을 높여줘요.
height=10
width=20
area=$(( height * width ))
산술 표현식 안의 변수에는 $ 기호가 빠져 있다는 걸 눈치챘나요?
Bash에서는 이렇게 써도 돼요.
덕분에 표현식이 훨씬 읽기 쉬워져요.
매뉴얼에는 이렇게 나와 있어요,
표현식 안에서는 매개변수 확장 문법을 사용하지 않고도 이름으로 셸 변수를 참조할 수 있어요.
배열 원소에도 똑같이 쓸 수 있어요:
point1=(10 15) # these are (x, y) coordinates
point2=(20 7)
distance_squared=$(( (point1[0] - point2[0])**2 + (point1[1] - point2[1])**2 ))
하지만 다른 매개변수 표현식은 각자의 특별한 문법을 써야 해요. 예를 들어 문자열의 길이를 두 배로 구하려면:
s="hello world"
len=$(( ${#s} * 2 ))
위치 매개변수도 마찬가지예요. 처음 두 위치 매개변수가 너비와 높이를 나타낸다면:
area=$(( $1 * $2 ))