쉘 스크립트에서 계산하기 [expr]

2022. 5. 30. 10:10IT/S . e . v . e . r . I . n . f . r . a

expr

기본적으로 expr은 커맨드로 별도로 존재하고 있습니다.

/usr/bin/expr

하지만, bash 쉘은 expr를 built-in으로 내장시켜 쉘스크립트에서 expr 명령어를 사용하면,
/usr/bin/expr를 사용하지 않고 내장된 build-in 명령어를 실행합니다.
실행속도도 build-in 명령이 외부 명령보다 빠릅니다.

expr로 연산할 수 있는 내용은 아래 4가지 입니다.

산술

+ : 더하기
- : 빼기
* : 곱하기('\*'로 사용)
/ : 나누기를 한 후의 몫의 값
% : 나누기를 한 후의 나머지 값

논리

| : 어느 한쪽이 조건에 만족하면 참(or)
& : 양쪽 조건이 모두 만족하면 참(and)

관계

= : 같음
> : 왼쪽이 오른쪽보다 큼
>= : 왼쪽이 오른쪽보다 크거나 같음
< : 왼쪽이 오른쪽보다 작음
<= : 왼쪽이 오른쪽보다 작거나 같음
!= : 왼쪽과 오른쪽이 같지 않음

문자열

: : 문자열 일치
:(찾을 문자열) : 괄호안의 문자열만 출력

예제

or연산

양쪽에 모두 null이나 0이 있으면 출력 값은 false를 의미하는 0을 출력합니다.

$ expr "" \| ""
0
$ echo $?
1

$ expr "" \| 0
0
$ echo $?
1

반면 피연산자 중 하나가 1이거나 문자라면 결과는 true가 됩니다.

출력값은 1 혹은 문자가 나옵니다.

$ expr "" \| 1
1
$ echo $?
0

$ expr "" \| "a"
a
$ echo $?
0

and 연산

and 연산은 피연산자 중에 하나가 null 혹은 0이면 출력은 언제나 '0'이 됩니다.

$ expr '' \& ''
0
$ echo $?
1

$ expr "" \& ""
0
$ echo $?
1

$ expr "" \& 1
0
$ echo $?
1

$ expr "" \& "a"
0
$ echo $?
1

양쪽에 값이 있으면 앞쪽에 나온 값이 출력값이 됩니다.
리턴값은 당연히 성공을 의미하는 0입니다.

$ expr 1 \& "a"
1
$ expr "a" \& 1
a

'=' 연산은 좌항과 우항의 값이 같으면 1(true)을 출력하고 다르면 0(false)을 출력합니다.
여기서 '출력'의 의미는 '리턴값'과 다릅니다.
'출력'의 경우, true가 1이고, false가 0입니다.

반대로 리턴값의 경우는 처리가 정상적으로 완료되면 0을 출력하고
명령의 처리가 에러를 내면 1이나 2를 출력합니다.

이렇게 된 연유는 '리턴값'에 대한 유닉스 고유의 정책때문입니다.
유닉스에서는 에러없이 성공적으로 프로그램이 종료되면 '0'을 리턴합니다.
문제가 있으면 '0' 외에 다른 값을 출력하기 때문입니다.

따라서 쉘스크립트에서도 '0'인 경우를 '성공'으로 판단합니다.

":" 연산은,
string : [정규표현식]
위와 같은 형태로 사용합니다.
string 문자열 내에서 정규표현식에 부합하는 문자열의 길이를 출력합니다.

주의사항

expr을 사용할 때 주의해야 할 사항이 있는데
아래 조건을 만족하지 않으면 원하지 않는 결과가 나올 수 있다.

  1. 식의 전체를 역따옴표(``)나 $()로 감싸주어야 한다.
    쉘 스크립트 작성시 역따옴표는 보안문제를 일으킬 수 있으므로 $()를 사용하도록 하자.
  2. *연산자와 괄호'()' 앞에는 (역슬래시)를 붙인다.
    연산자로 쓰이는 기호 중 일부는 이미 특수문자로 사용되고 있습니다.
    위의 문자를 연산에 사용하려면 쉘스크립트가 낚아채서 다른 액션을 하지 않고 해당 항목 문자 그대로 해석하기 위해서이다.
  3. 모든 연산자와 숫자, 변수, 기호 사이에는 space가 있어야 한다. 공백을 주지 않으면 하나의 문자열로 인식한다.
  4. 반대로 "=" 사이에는 space가 있으면 안된다.
#!/bin/sh

VAR_NUM=$(expr 3 + 4 - 5)
echo ${VAR_NUM}

VAR_NUM2=$(expr \( 3 \* 5 \) / 4 + 7)
echo ${VAR_NUM2}

여태까지 살펴본 expr의 수많은 연산자들 사이에는 우선순위가 있습니다.
총 7단계로 나눌 수 있습니다.

  1. 괄호
    괄호는 다른 모든 우선순위를 능가합니다. 어느 언어에서나 마찬가지이죠.
  2. string : 정규식
    문자열에서 정규표현식을 부분이 괄호를 제외하고 가장 높습니다.
  3. *, /, %
    곰셈, 나눗셈, 나머지 연산
  4. +, -
    덧셈, 뺄셈
  5. =, >, >=, <, <=, !=
    관계
  6. &
    and 연산
  7. |
    or 연산

이상과 같습니다.
우선순위가 확실히 기억나지 않는다면 괄호로 감싸주면 되겠지요.