backend/ubuntu

33 - flow control: for loop (The Linux command line book)

seul chan 2021. 7. 4. 16:54

33. Flow control: looping with for

for loop는 이전에 다룬 while과 until과는 다르게 processing sequence를 제공한다.

for: traditional shell form

전통적인 형태의 for문은 다음과 같이 사용.

for variable [in words]; do
  commands
done

for문은 command line에서도 유용하게 사용될 수 있다.

[me@linuxbox ~]$ for i in A B C D; do echo $i; done
A
B
C
D

for문의 강력한 기능 중 하나는 word list를 동적으로 만들 수 있다는 점. brace expansion을 사용 가능.

[me@linuxbox ~]$ for i in {A..D}; do echo $i; done
A
B
C
D

혹은 pathname expansion을 사용할 수 있다.

[me@linuxbox ~]$ for i in distros*.txt; do echo "$i"; done
distros-by-date.txt
distros-dates.txt
distros-key-names.txt
distros-key-vernums.txt
distros-names.txt  
distros.txt
distros-vernums.txt
distros-versions.txt

다른 흔한 예시는 command substitution이다.

#!/bin/bash

# longest-word: find longest string in a file

# if "$1" is not null string
while [[ -n "$1" ]]; do
    # if "$1" has read permission
    if [[ -r "$1" ]]; then
        max_word=
        max_len=0
        for i in $(strings "$1"); do
            # count the letters of the filename
            len="$(echo -n "$i" | wc -c)"
            if (( len > max_len )); then
                max_len="$len"
                max_word="$i"
            fi
        done
        echo "$1: '$max_word' ($max_len characters)"
    fi
    shift
done

위 예시는 특정 파일에서 가장 긴 단어를 찾는 스크립트이다.

GUN builtin package인 strings 프로그램을 사용해서 읽을 수 있는 파일의 단어 리스트를 생성하고, for문에서 각 단어를 보고 가장 긴 단어인지 판단한다.

bash operator examples: https://linuxhint.com/bash_operator_examples/#

while 대신 for를 사용하여 받은 인자를 처리하는것도 가능.

#!/bin/bash

# longest-word2: find longest string in a file

for i; do
    if [[ -r "$1" ]]; then
        max_word=
        max_len=0
        for j in $(strings "$i"); do
            len="$(echo -n "$j" | wc -c)"
            if (( len > max_len )); then
                max_len="$len"
                max_word="$j"
            fi
        done
        echo "$i: '$max_word' ($max_len characters)"
    fi
done

for: C Language form

최근 버전의 bash는 C언어에서 사용하는 형태의 두번째 for 명령어 문법을 지원한다.

for (( expression1; expression2; expression3 )); do
  commands
done

이는 다음 코드와 동일한 의미.

(( expression1 ))
while (( expression2 )); do
      commands
      (( expression3 ))
done

expression1은 for loop의 조건을 initialize하기 위해 쓰이고, expression2는 for loop를 종료시킬 때 쓰인다. expression3은 각 loop의 종료에 실행된다.

아래는 간단한 예시

#!/bin/bash

# simple_counter: demo of C style for command

for (( i=0; i<5; i=i+1 )); do
    echo $i
done

Summing up

앞에서 다룬 for 명령어를 통해 기존의 sys_info_page 스크립트를 업그레이드 시킬 수 있다.

기존에 아래의 코드로 되어있는데,

report_home_space () {
      if [[ "$(id -u)" -eq 0 ]]; then
            cat <<- _EOF_
                  <h2>Home Space Utilization (All Users)</h2>
                  <pre>$(du -sh /home/*)</pre>
                  _EOF_
      else
            cat <<- _EOF_
                  <h2>Home Space Utilization ($USER)</h2>
                  <pre>$(du -sh "$HOME")</pre>
                  _EOF_
      fi
      return
}

이를 각 유저의 홈 디렉토리를 순회하면서 각각이 가진 디렉토리와 파일들의 숫자를 제공하게 수정해보자.

report_home_space () {
      local format="%8s%10s%10s\n"
      local i dir_list total_files total_dirs total_size user_name

      if [[ "$(id -u)" -eq 0 ]]; then
            dir_list=/home/*
            user_name="All Users"
      else
            dir_list="$HOME"
            user_name="$USER"
      fi

      echo "<h2>Home Space Utilization ($user_name)</h2>"

      for i in $dir_list; do
            total_files="$(find "$i" -type f | wc -l)"
            total_dirs="$(find "$i" -type d | wc -l)"
            total_size="$(du -sh "$i" | cut -f 1)"

            echo "<h3>$i</h3>"
            echo "<pre>"
            printf "$format" "Dirs" "Files" "Size"
            printf "$format" "----" "-----" "----"
            printf "$format" "$total_dirs" "$total_files" "$total_size"
            echo "</pre>"
      done
      return
}