[Linux] shell script (1)

한호성·2023년 6월 13일

shell script

목록 보기
1/2

Introduction

Shell Script를 활용하여서, 간단한 작업을 자동화할 필요가 있었다.

자동화를 시키기 위해 파이썬을 사용할 수 도 있었지만, Java 기반으로 개발을 하고 있기도 하고, 개발팀원 모두가 설치없이 사용할 수 있도록 Shell Script로 쓰기로 결정했다.

이전에 개인 프로젝트에서 자동배포할 때, Shell Script를 사용했는데, 자세히 공부를 하지 못하고 넘어간 것이 걸려서, 요번에 한번 공부한 내용을 작성해보도록 하겠다.

[목차]

  • Shell Scirpt란?
  • 기본 문법 정리
  • 내가 사용한 코드 전문에서 사용된 문법 공부해보기
  • 내가 사용한 코드에서의 유의사항 및 개선점에 대해 고민해보기

Shell Script


Shell 이란 무엇일까?

shell은 운영체제 상에서, 사용자가 입력하는 명령을 읽고 해석하여 대신 실행해주는 프로그램입니다. 운영체제의 커널과, 사용자 사이를 이어주는 역할을 하며, 사용자의 명령어를 해석하고 운영체제가 알아들을 수 있도록 도와주는 명령입니다.

Linux에서 사용하는 shell의 종류는 여러가지가 존재합니다.

  • bash : GNU운영체제, 리눅스, 맥 등 다양한 운영체제에서 사용중이며, 현재 리눅스의 표준 쉘이다.

  • sh Bourne shell

  • csh C shell : C언어를 기반으로 만들어졌으며, 강력한 프로그램 작성기능을 가지고 있다.

  • ksh Kron shell : 콘 쉘이라고 읽으며 C쉘을 기반으로 업그레이드 한 쉘 중 하나이다.

  • tcsh TENEX C Shell : C쉘의 기능을 강화 시킨 쉘이다.


그렇다면 Shell Script란 무엇일까?

Shell Script


Shell Script란 Shell 이나 Command Line 인터프리터에서 구동되도록 작성된 스크립트입니다. 여러가지 명령어 조합을 수행하거나, 반복적인 명령어를 단일 명령으로 쉽게 사용할 수 있습니다.

(윈도우에서 .bat 파일을 들어보셨다면, 리눅스에서 사용되는 같은 개념이라고 생각하면 됩니다)

Shell Script 기본 문법


1 쉘의 첫번째 행 -Shebang

Unix 계열 Shell Script의 필수적인 구문이다. Script 파일의 최상단에 해당 파일을 해석해줄 인터프리터의 절대경로를 지정해주는 것이다. ( bash shell 인터프리터로 지정한 것을 의미)

#! /bin/bash

name="inpa" # 변수 선언 및 대입
pass=123123 # 따옴표로 감싸든 말든 문자열로 저장됨

echo $name 
# {}가 있으나 없으나 $만으로 변수의 값을 넣어줄 수 있으나, 
#문자열을 붙여서 쓸려면 ${} 를 사용해야 한다.
echo "my name is mr.${name}"
printf "%s" $pass

2 변수 타입 지정

기본적으로 Bash 변수는 "문자열"만 저장한다.
하지만, 자료형 타입을 미리 지정해주는 문법도 존재한다.
(혼자서, 문법공부 먼저 안하고, 구현에 집중하느라.. 이게 무슨말이야 하면서 찾아보면서 개발했는데, 미리 개념을 한번 싹 잡고 시작했으면 좀 더 편했을 수도 있을거 같다.)

# -r 읽기 전용 타입 (상수 const라고 보면 된다)
declare -r var1
readonly var1

# -i 정수형 타입
declare -i number
number=3
echo "number = $number"     # number = 3

# -a 배열 타입
declare -a indices

# -A 연관배열(MAP) 타입
declare -A map

# -f 함수 타입
declare -f

# -x 환경변수(export) 지정
declare -x var3 # 스크립트 외부 환경에서도 이 변수를 쓸 수 있게 해준다.

이 부분에서 제일 답답했던 것은 정수형 타입이였다.
shell Script에서 산술 연산을 어떻게 하는지 알아봅시다.

3 shell Script에서 산술 연산

shell Script에서 산술연산의 방법은 총 3가지가 존재합니다.

  • let
  • expr
  • $(())

사용법만 알면 간단하니, 예제만 올려두겠습니다.

number1=10
number2=20

plus=`expr $number1 + $number2` 
minus=`expr $number1 - $number2`
mul=`expr $number1 \* $number2` # 곱셈에는 \* 를 이용한다.
div=`expr $number1 / $number2`
rem=`expr $number1 % $number2`


# 우선순위 산술 연산을 할때는 괄호를 문자 처리해야 한다.
# 연산자 *와 괄호() 앞에는 역슬래시와 같이 사용
num=`expr \( 3 \* 5 \) / 4 + 7`
echo $num

num1=42
num2=9
 
let re=num1+num2 #Add
echo "add:$re"

let re=num1-num2 #Sub
echo "sub:$re"

let re=num1*num2 #Mul
echo "mul:$re"

let re=num1/num2 #Div
echo "div:$re"

let re=num1%num2 #Mod
echo "mod:$re"

echo add:$((num1+num2))
echo sub:$((num1-num2))
echo mul:$((num1*num2))
echo div:$((num1/num2))
echo mod:$((num1%num2))

내가 봤을 때, let 방식이 가장 간단한 방식인거 같다..

다음으로, 조건문이다.

if 문

if 문의 특징은 조건식을 대괄호로 감싸고 조건식과 대괄호 사이에 공백이 존재해야한다는 점과, 조건문의 끝을 알리는 fi 가 필요하다는 점이다. 기본 틀을 보도록 하자

if [(공백필요) (조건식) (공백필요) ]
then
	수행
else
	수행
fi

# 가독성 좋기 위해 then을 if [] 와 붙여쓰려면 반드시 세미콜론 ; 을 써야한다.
if [ 값1 조건식 값2 ]; then
    수행1
else
    수행2
fi

if 문 사용시, 이중괄호를 사용하면, 비교표현식이 들어갈 수 있다.
( 우리가 자주 사용하는 ! ~ << >> & | && || num++ 등등)

for 문

기본적인 for문 작성하는 것도 문법이 조금 난해하다 보니 고생했다. 잘 살펴보도록 하자

# 초기값; 조건값; 증가값을 사용한 정통적인 for문
for ((i=1; i<=4; i++)); do
    echo $i
done

다른 예시
declare -a test

test[0]=123
# 배열 길이 구하고,
length=${#test[@]}

# 배열길이만큼 돌면서 index에 해당하는 값 찍기.
for ((i=0; i<${#test[@]}; i++)); do
    echo "value : ${test[i]}"
done

프로그램은 조건문, 반복문으로 구성된 명령들의 집합이라고 봐도 무방하다고 생각한다. 기본을 알았으니, 우선 이렇게만 정리해두고, 이제 내가 짠 코드에서 사용된 부분들중 모르는 것을 정리해보도록 하겠다.

내가 CSV로 제공되는 ErrorMessage를 자동화 하여 Java 코드에서 사용하도록 간단하게 만들어본 shell script 이다.

input_file="./error_message.csv"
output_file="./src/main/java/kr/co/wizcore/wcm/web/rest/errors/constant/ErrorMessage.java"
userInput="public class ErrorMessage{

}"
messageKeyColumnIndex=5
messageValueColumnIndex=6
csvKeyArray=()
csvValueArray=()
javaFileKeyArray=()
javaKeyPrefix="public static final String"


echo "==================="
echo "ErrorMessage.java 파일 읽는중..."


if [ -f "$output_file" ];
  then
    while IFS= read -r line
    do

        line=$(echo -n "$line" | tr -d '[:cntrl:]')
        if [ -z "$line" ]; then
                continue
        fi
        if [[ $line == *"${javaKeyPrefix}"* ]];
        then
          substring="${line#*$javaKeyPrefix }"
          IFS='='
          read -ra substrings <<< "$substring"
          javaFileKey=$(echo -n "${substrings[0]}"|tr -d '[:space:]')
          javaFileKeyArray+=("$javaFileKey")
        fi
    done < "$output_file"

    echo "java key length : ${#javaFileKeyArray[@]}"
fi

# output 파일 없으면 생성
if [ -f "$output_file" ]; then
  echo "File $output_file exists."
else
  touch "$output_file"
  echo "$userInput" > "$output_file"
fi


echo "==================="
echo "error_message.csv 파일 읽는중..."
if [ ! -f "$input_file" ];
  then
    echo "ErrorMessage INPUT CSV does not exist, Check it plz"
    exit
fi

# Read the CSV file line by line 구분자
while IFS= read -r line
do
    line=$(echo -n "$line" | tr -d '[:cntrl:]' )
    if [ -z "$line" ]; then
        continue
    fi
    IFS=','
    read -ra values  <<< "$line"
    # Split 한 내용 key,value 로대입
    key=${values[$messageKeyColumnIndex]}
    value=${values[$messageValueColumnIndex]}
    csvKey=$(echo -n "$key" | tr -d '[:space:]')

    #중복체크 로직
    if [ -z "$csvKey" ] || echo "${javaFileKeyArray[@]}" | grep -qw "$csvKey" || echo "${csvKeyArray[@]}" | grep -qw "$csvKey"
      then
      continue
    fi

    csvKeyArray+=("$key")
    csvValueArray+=("$value")
done < "$input_file"
echo "error_message.csv 파일 완료"
echo "==================="



echo "==================="
echo "ErrorMessage 파일에 작성하는중 ..."

file_end_pattern='}'
index=1
arrayLength=${#csvKeyArray[@]}

echo "추가되는 항목 수 : $(($arrayLength-1))"
for ((i=index; i<${arrayLength}; i++)) do
  echo "추가되는 KEY : ${csvKeyArray[i]}"
done

while [ "$index" -lt "$arrayLength" ]
do
  content_to_insert="${javaKeyPrefix} ${csvKeyArray[$index]}=\"${csvValueArray[$index]}\";"
  content_to_insert=$(echo -n "$content_to_insert" | tr -d '[:cntrl:]' )
  sed -i "/$file_end_pattern/i $content_to_insert" "$output_file"
  index=$((index+1))
done

echo "ErrorMessage 파일에 완료"
echo "==================="

코드를 보면서 헷갈렸던 부분들을 추려보자

1 IFS ?
2 (ECHOn"(ECHO -n "line" | tr -d '["cntrl:]')
3 이중대괄호 [[ ]]
4 sed -i "/file_end_pattern/i $content_to_insert" "output_file"
5 csvKeyArray+=("$key")

이 다음글에서는, 위의 헷갈리는 5가지 문법에 대해 정리하는 글과, 내가 만든 스크립트 사용시 유의사항과 개선점에 대해 고민해보도록 하겠다.

Reference

https://inpa.tistory.com/entry/LINUX-%EC%89%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%ED%95%B5%EC%8B%AC-%EB%AC%B8%EB%B2%95-%EC%B4%9D%EC%A0%95%EB%A6%AC

profile
개발자 지망생입니다.

0개의 댓글