16.2. 코드 블럭 재지향

while, until, for 루프의 코드 블럭, 심지어는 if/then 테스트문 블럭도 표준입력의 재지향을 받아 들일 수 있습니다. 함수조차도 이런 형태의 재지향을 할 수 있습니다(예 23-7 참고). 이렇게 하려면, 해당 코드 블럭의 제일 끝에 < 연산자를 두면 됩니다.

예 16-2. 재지향된 while 루프

#!/bin/bash

if [ -z "$1" ]
then
  Filename=names.data  # 파일이름이 지정되지 않을 경우의 기본값.
else
  Filename=$1
fi  
# Filename=${1:-names.data}
# 라고 해도 됩니다(매개변수 치환).

count=0

echo

while [ "$name" != Smith ]  # $name 을 왜 쿼우트 했을까요?
do
  read name                 # 표준입력이 아니라 $Filename 에서 읽음.
  echo $name
  let "count += 1"
done <"$Filename"           # 표준입력을 $Filename 파일로 재지향.
#    ^^^^^^^^^^^^

echo; echo "$count 개의 이름을 읽었습니다."; echo

# 몇몇 오래된 쉘 스크립트 언어에서는 재지향된 루프가 서브쉘로 돕니다.
# 그렇기 때문에, $count 가 루프 밖에서 초기화되어 0 을 리턴합니다.
# Bash 와 ksh 은 가능한한 서브쉘을 안 띄우려고 하기 때문에
# 이 스크립트는 제대로 동작합니다.
# Heiner Steven 이 이 점을 지적해 주었습니다.

exit 0

예 16-3. 다른 형태의 재지향된 while 루프

#!/bin/bash

# 이 스크립트는 앞서 소개해 드렸던 스크립트의 다른 형태입니다.

# Heiner Steven 이 제공해 준 것으로, 
# 재지향 루프가 서브쉘로 돌 경우에 루프 안의 변수값이
# 루프 종료후 그 값을 잃어 버리는 것에 대한 해결책입니다.


if [ -z "$1" ]
then
  Filename=names.data     # 파일이름이 지정되지 않았을 경우의 기본값.
else
  Filename=$1
fi  


exec 3<&0                 # 표준입력을 3번 파일 디스크립터로 저장.
exec 0<"$Filename"        # 표준입력을 재지향.

count=0
echo


while [ "$name" != Smith ]
do
  read name               # 재지향된 표준입력($Filename)에서 읽음.
  echo $name
  let "count += 1"
done <"$Filename"         # 루프는 $Filename 파일에서 입력을 받음.
#    ^^^^^^^^^^^^


exec 0<&3                 # 원래 표준입력을 복구.
exec 3<&-                 # 임시 파일 디스크립터 3번을 닫음.

echo; echo "$count 개의 이름을 읽었습니다."; echo

exit 0

예 16-4. 재지향된 until 루프

#!/bin/bash
# 앞의 예제와 똑같으나 "until" 루트를 사용.

if [ -z "$1" ]
then
  Filename=names.data         # 파일이름이 지정되지 않았을 경우의 기본값.
else
  Filename=$1
fi  

# while [ "$name" != Smith ]
until [ "$name" = Smith ]     # != 를 = 로 바꿔주세요.
do
  read name                   # 표준입력이 아니라 $Filename 에서 읽어 들입니다.
  echo $name
done <"$Filename"             # 표준입력을 $Filename 파일로 재지향.
#    ^^^^^^^^^^^^

# 앞 예제의 "while" 루프와 똑같은 결과가 나옵니다.

exit 0

예 16-5. 재지향된 for 루프

#!/bin/bash

if [ -z "$1" ]
then
  Filename=names.data          # 파일이름이 지정되지 않을 경우의 기본값.
else
  Filename=$1
fi  

line_count=`wc $Filename | awk '{ print $1 }'`  # 대상 파일의 줄 수.
# "for" 루프에서 표준입력을 재지향 하는게 아주 부자연스러워 보이고
# 구현하기 까다롭겠지만, 여러분이 충분히 똑똑하다면 가능합니다.
#
# 좀 더 간단하게 하려면       line_count=$(wc < "$Filename")


for name in `seq $line_count`  # "seq"가 연속된 숫자를 출력한다는 거, 기억나시죠?
# while [ "$name" != Smith ]   --   "while" 루프보다 더 복잡합니다.   --
do
  read name                    # 표준입력이 아닌 $Filename 에서 읽어 들입니다.
  echo $name
  if [ "$name" = Smith ]       # 이런 작업을 추가적으로 덧붙여야 합니다.
  then
    break
  fi  
done <"$Filename"              # 표준입력이 $Filename 에서 재지향 됨.
#    ^^^^^^^^^^^^

exit 0

바로 앞의 예제를 조금 변경하면 루프의 출력도 재지향 할 수 있습니다.

예 16-6. 재지향된 for 루프(표준입력, 표준출력 모두 재지향됨)

#!/bin/bash

if [ -z "$1" ]
then
  Filename=names.data          # 파일이름이 지정되지 않을 경우의 기본값.
else
  Filename=$1
fi  

Savefile=$Filename.new         # 결과를 저장할 파일이름.
FinalName=Jonah                # "read" 시에 마지막 입력이 될 이름.

line_count=`wc $Filename | awk '{ print $1 }'`  # 대상 파일의 줄 수.


for name in `seq $line_count`
do
  read name
  echo "$name"
  if [ "$name" = "$FinalName" ]
  then
    break
  fi  
done < "$Filename" > "$Savefile"     # 표준입력을 $Filename으로 재지향하고,
#    ^^^^^^^^^^^^^^^^^^^^^^^^^^^       그 결과를 백업 파일로 저장.

exit 0

예 16-7. 재지향된 if/then 테스트

#!/bin/bash

if [ -z "$1" ]
then
  Filename=names.data   # 파일이름이 지정되지 않을 경우의 기본값.
else
  Filename=$1
fi  

TRUE=1

if [ "$TRUE" ]          # if true    와   if :   도 동작합니다.
then
 read name
 echo $name
fi <"$Filename"
#  ^^^^^^^^^^^^

# 파일의 첫번째 줄만 읽어 들임.
# "if/then" 테스트문은 루프안에서 쓰이지 않는 한, 반복해서 비교할 방법이 없습니다.

exit 0

코드 블럭의 표준출력을 재지향하는 것은 그 출력을 파일로 저장하는 효과를 가져옵니다. 예 4-2를 참고하세요.

참고: Here documents는 특별한 종류의 재지향된 코드 블럭입니다.