The linux command line - 29. Flow control: looping with while/until
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