1 조건문(if)

if문은 조건을 판별하여 조건에 부합되는 경우와 그렇지 않는 경우를 나눠서 프로그램을 진행시킨다. 쉘 프로그램에서 if문을 작성할 때 iffi와 같이 시작과 끝을 명기한다. case문도 동일하게 caseesac와 같이 단어를 전치시킨다.

country="China"

if [ $country ==  "China" ]; then
    echo "Serious"
else
    echo "$country is not serious"
fi    
Serious

단어나 문자를 비교하는 조건식을 작성할 때는 부울 표현식을 작성하면 되지만 숫자의 경우 조금 차이가 있다. 이를 보완하기 위해서 조건식 표현에 다음과 같이 사용하다.

  • -eq: 같다(equal to)
  • -ne: 같지 않다(not equal to)
  • -lt: 적다(less than)
  • -le: 같거나 적다(less than or equal to)
  • -gt: 크다(greater than)
  • -ge: 크거나 같다(greater than or equal to)

숫자로 작성된 조건문

a=3
if (($a > 2)); then
    echo "$a is greater than 2"
fi    
3 is greater than 2

산술 기호(arithmetic flag)가 사용된 조건문

a=3
if [ $a -gt 2 ]; then
    echo "$a is greater than 2"
fi
3 is greater than 2

따라서, 숫자의 경우도 또다른 구문에 따라 작성하는 대신 if [ ... ]; then 방식을 하나 익혀 이를 문자조건식 판별이나 숫자조건식 판별에 모두 사용하는 것도 인지부하를 줄이는 방식 중 하나가 될 것이다.

조건식을 다수를 대표적인 부울 연산자(&&, ||)를 동원하는 것도 가능하고 다수 조건식을 조합하여 조건 판별식을 작성할 경우 다음과 같이 구문을 작성한다.

  • [ $a -gt 2 ] && [ $a -lt 10 ]
  • [[ $a -gt 2 && $a -lt 10 ]]
a=7
#if [ $a -gt 2 ] && [ $a -lt 10 ]; then
if [[ $a -gt 2 && $a -lt 10 ]]; then
    echo "$a is greater than 2 and less than 10"
fi
7 is greater than 2 and less than 10

1.1 파일 내부 단어 찾기

파일 내부에 특정 단어가 있는지도 조건문과 쉘 명령어를 결합시켜 찾아낼 수 있다.

echo "Korea is the best country ever!!" > data/shell/korea.txt
if $(grep -q 'Korea' data/shell/korea.txt); then
    echo "Korea 단어 있음"
fi    
Korea 단어 있음

2 for 반복

쉘에서 반복작업을 수행하는 대표자는 for 반복문으로 for ... in ... 반복횟수를 지정하고 do ... done 사이에 반복작업을 지정한다. 반복하는 성격에 따라 몇가지 방식이 있다. 하나씩 증가시키는 경우가 있는가 하면 2만큼 증가 시키는 경우도 있다.

순차 반복 사례

for i in 1 2 3 4 5
do
    echo "1부터 5까지 1만큼 증가 $i"
done    
1부터 5까지 1만큼 증가 1
1부터 5까지 1만큼 증가 2
1부터 5까지 1만큼 증가 3
1부터 5까지 1만큼 증가 4
1부터 5까지 1만큼 증가 5

2만큼 반복 증가 사례

for ((i=1; i<=5; i+=2))
do
  echo "1부터 5까지 2만큼 증가: $i"
done    
1부터 5까지 2만큼 증가: 1
1부터 5까지 2만큼 증가: 3
1부터 5까지 2만큼 증가: 5

이외에 glob 표현식을 사용하는 것이 대표적인 활용사례가 된다. 먼저 특정 디렉토리에 포함된 모든 .txt 파일을 찾아보자. data/shell/model_results/ 디렉토리에는 예측모형 결과가 담긴 파일이 4개 존재한다. 쉘 스크립트에 glob 표현식을 사용해서 모든 텍스트 파일에 접근이 가능하다.

그 다음으로 grep 명령어를 사용해서 각 파일 내부 특정 단어가 들어간 파일만 추출시킬 수가 있다. 이를 위해서 정규표현식 '[Rr]andom [Ff]orest' 을 동원해서 텍스트 파일을 특정할 수 있다.

모든 디렉토리 파일

for file in data/shell/model_results/*.txt
do
  echo 'Model result files: ' $file
done
Model result files:  data/shell/model_results/model_1.txt
Model result files:  data/shell/model_results/model_2.txt
Model result files:  data/shell/model_results/model_3.txt
Model result files:  data/shell/model_results/model_4.txt

파일에 포함된 파일 찾기

for file in data/shell/model_results/*.txt
do
  echo "Random Forest 포함된 파일: "$(grep -e '[Rr]andom [Ff]orest' $file)
done
Random Forest 포함된 파일: 
Random Forest 포함된 파일: Model Name: Random forest
Random Forest 포함된 파일: 
Random Forest 포함된 파일: 

2.1 sed 사례

이번에는 특정 디렉토리 파일 전체를 뒤져서 특정 단어가 들어간 파일을 찾아 해당 단어를 다른 다른어로 치환시키는 사례를 살펴보자. 1

  • 맥OS: sed -i '' 's/old-text/new-text/g' input
  • 리눅스: sed -i 's/old-text/new-text/g' input

특정단어가 포함된 파일을 찾게되면 sed로 단어를 치환시킨다… 그리고 나서 다시 치환된 결과를 확인한다.

for file in data/shell/model_results/*.txt
do
  echo "Random Forest 포함된 파일: "$(grep -e '[Rr]andom [Ff]orest' $file)
  sed -i '' 's/Forest/forest/g' $file
  echo "Random Forest 포함된 파일: "$(grep -e '[Rr]andom [Ff]orest' $file)
done
Random Forest 포함된 파일: 
Random Forest 포함된 파일: 
Random Forest 포함된 파일: Model Name: Random forest
Random Forest 포함된 파일: Model Name: Random forest
Random Forest 포함된 파일: 
Random Forest 포함된 파일: 
Random Forest 포함된 파일: 
Random Forest 포함된 파일: 

2.2 while 반복

for문과 유사한데 조건이 만족되면 반복을 중지하고 빠져나온다는 점에서 차이가 있다. 다음과 같이 로켓 발사를 위한 카운트다운의 사례를 보면 처음 10초부터 시작하여 매번 while루프가 돌 때마다 1초씩 감소하여 마지막 0이 되면 반복을 중지하고 빠져나오게 된다.

countdown=10
while [ $countdown -ge 1 ]; 
do
  echo '현재값:' $countdown
  ((countdown-=1))
done
현재값: 10
현재값: 9
현재값: 8
현재값: 7
현재값: 6
현재값: 5
현재값: 4
현재값: 3
현재값: 2
현재값: 1

3 case 조건문

if문을 중첩하여 사용할 경우 코드가 길어지고 해독하기 어려운 사례가 종종 발생된다. 이런 경우 case 문을 사용하면 좋다. robs_files/ 디렉토리에는 확장자가 .py, .csv, .txt로 끝나는 파일이 있다. 이를 각자 디렉토리에 맞춰 복사하여 저장하는 사례를 생각해보자.

3.1 현황 파악

data/shell/robs_files/ 디렉토리에 있는 파일 확장자만 뽑아내는 쉘 명령어는 다음과 같다. 즉, cut 명령어로 앞서 받은 결과물을 . 구분자로 쪼개고 두번째 필드만 추출해서 이를 정렬한 후에 유일무이한 값만 추출한다.

ls data/shell/robs_files/ | cut -d '.' -f2 | sort | uniq
csv
py
python
txt

3.2 목표 디렉토리 생성

data/shell/robs_files/ 디렉토리에 담긴 파일을 csv/, python/, txt/ 디렉토리에 각가 정리할 수 있도록 mkdir 명령어로 디렉토리 3개를 생성한다.

mkdir data/shell/robs_files/csv
mkdir data/shell/robs_files/python
mkdir data/shell/robs_files/txt

3.3 파일 → 디렉토리 복사 2

data/shell/robs_files/ 디렉토리에 담긴 파일을 csv/, python/, txt/ 디렉토리로 case문을 사용해서 복사한다.

직관적으로 다음과 같이 작성할 수 있다.

  1. glob을 사용해서 모든 파일을 가져온다.
  2. 파일 확장자를 추출해낸다.
  3. if문으로 파일 확장자를 비교하여 앞서 생성한 디렉토리에 복사한다.
for path_file in data/shell/robs_files/*
do
  FILENAME=$path_file
  EXT=${FILENAME##*.}
  if [ $EXT == "py" ]; then
    cp $path_file data/shell/robs_files/python/
  fi
  if [ $EXT == "csv" ]; then
    cp $path_file data/shell/robs_files/csv/
  fi
  if [ $EXT == "txt" ]; then
    cp $path_file data/shell/robs_files/txt/
  fi
done

3.4 case

case 문으로 제작하면 다음과 같다.

4 함수(function)

함수는 R, 파이썬 등 대부분의 언어에서 제공하는 기능으로 재사용성을 증진시키고 입력과 출력만 기억하면 되기 때문에 단순한 레고 모듈을 조합하여 복잡하고 유용한 무언가를 제작할 수 있게 하는 가장 중요한 요소로 쉘에도 이런 기능이 제공된다.

4.1 헬로 월드

function 키워드로 쉘 스크립트 함수를 제작하고 함수명을 그 다음에 넣고 함수 몸통에 쉡 명령어를 작성한다. 마지막에 return 키워드로 함수출력을 반환시킨다.

function say_hello() {
  echo "안녕하세요"
}

say_hello
안녕하세요

원의 면적을 S로 두면 \(S=\pi r^2\) 공식을 사용해서 면적을 계산할 수 있다. 즉 반지름(r)을 알면 원의 면적을 구할 수 있다.

r=10
function calculate_area {
  S=$(echo "scale=2; 3.14*$r*$r" | bc)
  echo $S
}

calculate_area
314.00

4.2 인자를 받는 함수

인자를 받는 함수는 앞서와 마찬가지로 $1, $2, … 을 사용한다. 총인자갯수는 #1으로 하여 계산을 할 수 있다.


function calculate_area {
  AREA=$(echo "scale=2; 3.14*$1*$1" | bc)
  echo "총인자갯수: " $#
  echo "반지름: " $1  "원면적: " $AREA
}

calculate_area 100
총인자갯수:  1
반지름:  100 원면적:  31400.00
 

데이터 과학자 이광춘 저작

kwangchun.lee.7@gmail.com