기본 논리연산 정리

논리연산 test, [ \[[ ]], (( ))
NOT ! !
AND -a &&
OR -o ||

condition evaluate

test

test 명령은 표현식이 성공하면(true이면) 종료 상태로 0을 갖는다. 실패하면 1을 갖는다.

따라서 간단하게 test 명령의 결과를 확인하려면 $?를 출력해보면 된다.

$ test -e /dev/tty; echo $?
0

$ test -e ~/not-exist-file; echo $?
1

한편, test 명령과 [는 같은 파일이다. [if와 함께 자주 사용하지만 if의 문법이 아니다.

$ # 두 파일의 inode 가 같다
$ test /bin/test -ef /bin/[ ; echo $?
0

$ # inode를 출력해서 확인해보자
$ ls -il /bin/test /bin/[
1152921500312434706 -rwxr-xr-x 2 root wheel 134224 2023-06-15 Thu 19:08:29 '/bin/['
1152921500312434706 -rwxr-xr-x 2 root wheel 134224 2023-06-15 Thu 19:08:29  /bin/test

기본 연산

  • ! expression : expression 이 false 이면 true.
  • expression_1 -a expression_2 : expression_1 과 expression_2 가 모두 true 이면 true.
  • expression_1 -o expression_2 : expression_1 과 expression_2 중 하나라도 true 이면 true.
    • -a의 우선순위가 -o보다 높다.
  • ( expression ) : expression 이 true 이면 true.

파일 test

  • -e file : 존재하는 파일이라면 true.
#!/usr/bin/env bash

if [ -e ~/.bashrc ]; then
    echo "the file exists."
else
    echo "the file does not exist."
fi
  • -d directory : 존재하는 디렉토리라면 true.
  • -x file : 쓸 수 있는 파일이면 true.
  • -f file : 일반 파일이면 true.
  • -L file : 심볼릭 링크라면 true.
  • -r file : 읽을 수 있는 파일이면 true.
  • -s file : 크기가 0보다 큰 파일이면 true.
  • -b file : block special 파일이면 true.
  • -c file : character special 파일이면 true.
  • -f file : regular 파일이면 true.
  • -p file : named pipe 파일이면 true.
  • -S file : 네트워크 socket 파일이면 true.

  • file_1 -nt file_2 : file_1 이 file_2 보다 최근에 수정된 파일이면 true.
  • file_1 -ot file_2 : file_1 이 file_2 보다 오래된 파일이면 true.
  • file_1 -ef file_2 : file_1 과 file_2 의 inode 번호가 같으면 true.

더 많은 옵션, 더 자세한 내용은 man test를 참고할 것.

문자열 test

  • 문자열이 비어있으면 false.
$ test "" ; echo $?
1

$ test "a" ; echo $?
0
  • -n string : 문자열 길이가 0 이 아니면 true.
  • -z string : 문자열 길이가 0 이면 true.
  • string_1 = string_2 : 두 문자열이 같으면 true.
    • sh 시절의 흔적.
  • string_1 == string_2 : 두 문자열이 같으면 true.
  • string_1 != string_2 : 두 문자열이 다르면 true.
  • string_1 \< string_2 : string_1 이 string_2 보다 먼저 정렬되면 true.
  • string_1 \> string_2 : string_1 이 string_2 보다 나중에 정렬되면 true.
    • \<, \> 비교에서 \를 사용하징 낳으면 리다이렉션에 사용되는 <, >로 셸이 착각하니 주의할 것.
$ # > 를 잘못 사용하는 사례
$ #           ↓ 그냥 사용하면 리다이렉션이 되어 버린다
$ if [ "aaaa" > "bbbb" ]; then echo "bad result"; else echo "good result"; fi
bad result

$ # if [ "aaaa" 의 출력이 bbbb 파일로 저장되어 있다
$ ls ./bbbb
./bbbb
$ #           ↓ 백슬래시를 붙이면 제대로 동작한다
$ if [ "aaaa" \> "bbbb" ]; then echo "bad result"; else echo "good result"; fi
good result

$ ls ./bbbb
ls: cannot access './bbbb': No such file or directory

정수 test

  • integer_1 -eq integer_2 : 두 정수가 같으면 true.
  • integer_1 -ne integer_2 : 두 정수가 다르면 true.
  • integer_1 -le integer_2 : integer_1 ≤ integer_2 이면 true.
  • integer_1 -lt integer_2 : integer_1 < integer_2 이면 true.
  • integer_1 -ge integer_2 : integer_1 ≥ integer_2 이면 true.
  • integer_1 -gt integer_2 : integer_1 > integer_2 이면 true.

\[[ ]] 를 사용한 테스트 표현식

  • string =~ regexp : string 이 regexp 에 매치되면 true.
  • string == pattern : string 이 pattern 과 일치하면 true.
$ # * 을 확장해 파일 이름 패턴을 인식한다
$ if [[ "sample.txt" == sample.* ]]; then echo "ok"; fi
ok

$ # ""로 감싸면 확장을 하지 않는다
$ if [[ "sample.txt" == "sample.*" ]]; then echo "ok"; fi

(( )) 를 사용한 테스트 표현식

  • 0 이 아니면 true.
$ (( 0 )) ; echo $?
1

$ (( 1 )) ; echo $?
0

$ (( 23 )) ; echo $?
0

if

if 를 사용하는 패턴은 보통 이런 식이다.

if [ -e "dir/file" ]; then
    echo "file exists"
fi
if [ -d "dir" ]; then
    echo "dir exists"
elif [ -f "file" ]; then
    echo "file exists"
else
    echo "not matched"
fi
a=5
b=10

if [ "$a" -eq 5 ] && [ "$b" -eq 10 ]; then
    echo "true"
fi
a=5
b=20

if [ "$a" -eq 5 ] || [ "$b" -eq 10 ]; then
    echo "true"
fi

다음과 같이 복잡하게 사용하는 것도 가능하지만, 이 정도로 복잡해지면 그냥 다른 프로그래밍 언어를 사용하는 것이 낫다.

a=5
b=20
c=30

if [ "$a" -eq 5 ] && ([ "$b" -eq 10 ] || [ "$c" -eq 30 ]); then
    echo "true"
fi

case

#!/usr/bin/env bash

read -p "좋아하는 색깔은? " answer

case $answer in
    red)
        echo "빨간색을 좋아하시는군요"
        ;;
    blue)
        echo "파란색을 좋아하시는군요"
        ;;
    white|black|gray)
        echo "무채색을 좋아하시는군요"
        ;;
    *)
        echo "준비된 답변이 없습니다"
        ;;
esac

for

for color in red blue white; do
    echo "이번 색깔은 $color 입니다"
done

확장 규칙을 사용해 이렇게 할 수도 있다.

for color in {1..3}; do
    echo "이번 숫자는 $color 입니다"
done

배열을 사용하려 한다면 이렇게 하면 된다.

colors=("red" "blue" "white")
for color in "${colors[@]}"; do
    echo "이번 색깔은 $color 입니다"
done

C 계열 프로그래밍 언어에서 사용하는 for 문과 비슷하게 사용할 수도 있다.

for ((i=0; i<4; i++)); do
    echo "인덱스: $i"
done

while

i=0
while [ $i -lt 10 ]; do
    echo "인덱스: $i"
    i=$((i+1))
    sleep 1
done
  • 매 루프별로 1초씩 대기하며 인덱스: 0부터 인덱스: 9까지 출력한다.

아래와 같이 한 줄로 작성할 수도 있다.

i=0; while [ $i -lt 10 ]; do echo "인덱스: $i"; i=$((i+1)); sleep 1; done