backend/ubuntu

The linux command line - 29. Flow control: looping with while/until

seul chan 2021. 5. 30. 21:07

29. Flow control: looping with while/until

이전 챕터까지 menu-deriven program을 만들었지만 여전히 많은 문제를 가지고 있음. 그 중 하나는 single choice만 받고 바로 종료되어버리는것.

잘못된 정보가 입력되면 다시 시도할 기회를 주지 않고 에러 코드와 함께 바로 종료된다.

사용자가 프로그램을 종료 할 때까지 메뉴 표시, 선택을 반본적으로 할 수 있으면 더 좋을것.

이는 많은 프로그래밍 언어에서 looping 개념으로 구현되어있는데 쉘에서도 looping을 위한 세 가지 명령어를 제공해줌.

looping

while

simple example for while in shell script

#!/bin/bash

# while-count: display a series of numbers

count=1

while [[ "$count" -le 5 ]]; do
    echo "$count"
    count=$((count + 1))
done

echo "Finished"

result of execution is below

$ while-count
1
2
3
4
5
Finished

The syntax of the while command is as follow

while commands; do commands; done

if 문과 마찬가지로 while문도 exit status를 평가함.
exits status가 0이면 loop안의 명령어를 실행시킴.

이전 예시에서 count 변수는 1로 설정됨. [[ ]] compond command 안의 명령어를 실행시켜 exit code가 0이기 때문에 loop 안의 명령어를 실행시킴.

while loop를 사용하여 이전 장의 프로그램을 개선

#!/bin/bash

# while-menu: a menu driven system information program

DELAY=3  # Number of seconds to display results

while [[ "REPLY" != 0 ]]; do
    clear
cat <<- _EOF_
    Please Select:

    1. Display System Information
    2. Display Disk Space
    3. Display Home Space Utilization
    0. Quit
_EOF_
    read -p "Enter selection [0-3] > "

    if [[ "$REPLY" =~ ^[0-3]$ ]]; then
      if [[ "$REPLY" == 0 ]]; then
        echo "Program terminated."
        sleep "$DELAY"
      fi
      if [[ "$REPLY" == 1 ]]; then
        echo "Hostname: $HOSTNAME"
        uptime
        sleep "$DELAY"
      fi
      if [[ "$REPLY" == 2 ]]; then
        df -h
        sleep "$DELAY"
      fi
      if [[ "$REPLY" == 3 ]]; then
        if [[ "$(id -u)" -eq 0 ]]; then
          echo "Home Space Utilization (All Users)"
          du -sh /home/*
        else
          echo "Home Space Utilization ($USER)"
          du -sh "$HOME"
        fi
        sleep "$DELAY"
      fi
    else
      echo "Invalid entry." >&2
      sleep "$DELAY"
    fi
done
echo "Program Terminated"

Breaking out of a loop

bash는 loop에서 벗어날 수 있는 두 가지 명령어를 제공해줌.

  • break 명령어: loop를 즉시 종료함
  • continue 명령어: 해당 loop의 남은 명령어를 스킵하고 다음 loop로

break, continue를 사용하여 이전 프로그램을 리팩토링

#!/bin/bash

# while-menu: a menu driven system information program

DELAY=3  # Number of seconds to display results

while true; do
    clear
cat <<- _EOF_
    Please Select:

    1. Display System Information
    2. Display Disk Space
    3. Display Home Space Utilization
    0. Quit
_EOF_
    read -p "Enter selection [0-3] > "

    if [[ "$REPLY" =~ ^[0-3]$ ]]; then
      if [[ "$REPLY" == 1 ]]; then
        echo "Hostname: $HOSTNAME"
        uptime
        sleep "$DELAY"
        continue
      fi
      if [[ "$REPLY" == 2 ]]; then
        df -h
        sleep "$DELAY"
        continue
      fi
      if [[ "$REPLY" == 3 ]]; then
        if [[ "$(id -u)" -eq 0 ]]; then
          echo "Home Space Utilization (All Users)"
          du -sh /home/*
        else
          echo "Home Space Utilization ($USER)"
          du -sh "$HOME"
        fi
        sleep "$DELAY"
        continue
      fi
      if [[ "$REPLY" == 0 ]]; then
          break
      fi
    else
      echo "Invalid entry." >&2
      sleep "$DELAY"
    fi
done
echo "Program Terminated"

이 버전에서는 while true를 사용하여 무한 루프를 만들고, 명시적인 break를 사용하였다.

Until

until compound command는 while과 흡사하지만, 반대로 작동한다.
until loop는 zero exit status를 만날 때까지 계속됨.

위에서 5까지 세는 count script를 만들었는데, 이를 until로 구현하면 다음과 같음.

#!/bin/bash

# while-count: display a series of numbers

count=1

until [[ "$count" -gt 5 ]]; do
    echo "$count"
    count=$((count + 1))
done

echo "Finished"

Reading files with loops

while과 until은 standard input을 처리할 수 있다.
이는 while, until loop를 사용해서 파일을 처리할 수 있게 해 준다.

#!/bin/bash

# while-read: read lines from a file

while read distro version release; do
  printf "Distro: %s\tVersion: %s\tReleased: %s\n" \
        "$distro" \
        "$version" \
        "$release"
done < distros.txt

테스트를 위해서는 아래 내용을 distros.txt로 저장하고 스크립트를 실행시키면 됨.

USE     10.2    12/07/2006
Fedora  10      11/25/2008
SUSE    11.0    06/19/2008
Ubuntu  8.04    04/24/2008
Fedora  8       11/08/2007
SUSE    10.3    10/04/2007
Ubuntu  6.10    10/26/2006
Fedora  7       05/31/2007
Ubuntu  7.10    10/18/2007
Ubuntu  7.04    04/19/2007
SUSE    10.1    05/11/2006
Fedora  6       10/24/2006
Fedora  9       05/13/2008
Ubuntu  6.06    06/01/2006
Ubuntu  8.10    10/30/2008
Fedora  5       03/20/2006

file의 내용을 loop로 redirect 하기 위해 redirection operator (<)를 done statement 뒤에 추가.

read command는 모든 라인이 종료되면 non-zero exit status를 반환하게 되고 이 때 루프가 종료되게 됨.

이를 pipeline을 사용하여 구현하여도 동일함.

#!/bin/bash

# while-read: read lines from a file

sort -k 1,1 -k 2n distros.txt | while read distro version release; do
  printf "Distro: %s\tVersion: %s\tReleased: %s\n" \
        "$distro" \
        "$version" \
        "$release"
done