DevOps/Shell

Shell Script, 셸 스크립팅하기

게임이 더 좋아 2022. 9. 5. 22:27
반응형
728x170

 

나는 Ubuntu 환경에서의 bash Shell 이다.

알아보자

 

 


 

 

셀 스크립팅의 기본은 간단하다

아래 포맷만 지키면 된다.

(prompt) command { option } { arg.. }

 

여러가지 환경변수들은 미리 지정되어 있다.

실제 값의 확인은  echo ${name} 으로 확인하며

미리 지정된 여러가지 값들은 아래와 같다.

변수 설명 변수 설명
HOME 현재 사용자 홈 디렉터리 PATH 실행 파일을 찾는 디렉터리
LANG 기본 지원 언어 PWD 사용자 현재 디렉터리
TERM 로그인 터미널 타입 SHELL 로그인해서 사용하는 셸
USER 현재 사용자의 이름 DISPLAY X 디스플레이 이름
COLUMNS 현재 터미널의 컬럼 수 LINES 현재 터미널의 라인 수
PS1 1차 명령 프롬프트 변수 PS2 2차 명령 프롬프트(대개 '>')
BASH bash 셸의 경로 BASH_VERSION bash 버전
HISTFILE 히스토리 파일 경로 HISTSIZE 히스토리 파일의 저장 개수
HOSTNAME 호스트이름 USERNAME 현재 사용자 이름
LOGNAME 로그인 이름 LS_COLORS ls 명령의 확장자 색상 옵션
MAIL 메일을 보관하는 경로 OSTYPE OS 타입

더 있지만.. 나중에 더 알아보도록 하자.

 

환경변수의 변경은 export {name}={value}로 실행하면 바뀐다.

printenv 커맨드로 출력할 수 있다.

**일부 환경변수는 출력되지 않는다.

 


 

셸 스크립트 작성 어렵지 않다.

sample.sh

#!/bin/sh             => bash를 사용하고 셸 스크립트를 시작하겠다는 의미다. 무조건 써준다.(#! 무조건 써준다)
echo "name: " $USER
echo "home: " $HOME
exit 0    => 종료 코드를 반화하는 것이 올바른 결과가 나왔는지 알 수 있는 방법이다.

 


 

 

위 파일을 만들었다면 이제 실행해야 한다.

스크립트 파일은 크게 2가지 방법으로 실행된다.

1. sh 명령으로 실행

$ sh sample.sh

 

2. 실행 가능 속성으로 바꾸어 실행

** 해당 파일은 chmod를 바꾸어서 x (execute)를 추가한 뒤에 사용 가능하다.

./sample.sh

./{script_file_name}으로 알면 되겠다.

 

여기서 ./ 가 무엇을 의미하는 지 알아보자

우선 . 은 현재 디렉터리를 의미한다.

다시 말해서 ./은 현재 디렉터리에서의 파일인 sample.sh를 실행하라는 것이다.

아니.. 굳이 써야하나..?라고 할 수 있는데

일반적으로 경로를 설정하지 않으면 셸에서는 $PATH에 있는 디렉터리만 찾아보기 때문에 

파일을 찾을 수 없는 경우가 있다.

즉, 제대로 파일을 실행하기 위해서는 경로를 지정해주는 것이 필요하다.

 


 

그럼 셸 스크립트에서 사용하는 변수에 대해서 알아보자

변수는 다른 프로그래밍언어와 같다.

계속 쓸려면 그대로 리터럴 값을 쓰느니, 저장해서 쓴다.

셸 스크립트에서도 자주 쓰면 변수로 선언해서 쓴다.

 

하지만 다른 프로그래밍 언어와 다른 기본 규칙이 있다.

 

1. 셸 스크립트에서는 변수를 사용하기 전 미리 선언하지 않는다.

2. 변수에 값이 할당되면 자동 생성된다.

3. 변수에 넣는 모든 값은 문자열로 취급한다.

4. 변수 이름은 대소문자를 구분한다.

5. 변수를 대입할 때는 = 좌우에 공백이 없어야 한다. => 이것이 진짜 특이하다.

test1 = Hello => 공백이 있으면 안됨
test2=Hello => 정상
test3=heloo threr => 띄어쓰기를 넣으려면 ""로 선언
test4="heloo there" => 정상
test5=7+5 => 12가 아니라 7+5가 저장됨

 

변수를 저렇게 선언하는 것은 배웠고

변수를 출력하는 것은 $를 붙여서 한다.

** $ 자체를 출력하고 싶을 때는 이스케이프 문자인 ' \ ' 를 넣어서 출력한다.

 

**일반적으로 ${name} 과 "${name}" 은 똑같이 취급되는데

공백으로 인한 오류를 막기 위해서는 ""를 사용해서 변수를 쓰는 것도 나쁘지 않다.

 

변수의 모든 것은 문자열로 인식한다고 했는데 그럼 변수의 연산은 없는걸까??

그렇지는 않다.

사칙연산? 할 수 있다. 다만 몇가지 규칙이 존재한다.

expr 키워드를 사용하고 `라는 역따옴표를 사용하는 것이다.

또한 연산식에서 괄호를 쓰고싶거나 문자를 쓸 때는 이스케이프 코드가 필요하다. [ * 과 ( ) 에 필요하다. ]

#!/bin/sh
num1=100
num2=$num1+200
echo $num2
num3=`expr $num1 + 200 ` => 이건 숫자 1 옆에 있는 역따옴표를 쓴다.
echo $num3
num4=`expr \( $num1 + 200 \) / 10 \* 2`  => (num1 + 100)/10*2

 

파라미터 변수라는 것도 존재한다.

$0, $1, $2 .. 등이 그 예이다.

??? 그게 뭐야???

예를 들어서 

apt -y install gftp 를 실행한다고 해보자.

파라미터 변수는 이렇다.

$0 $1 $2 $3
apt -y install gftp

 

??? 그냥 커맨드 키워드 순서대로 파라미터 변수가 되는건가??

맞다. 명령어에 포함된 모든 키워드들을 $변수 형식으로 지정하는 것이다.

어떤 식으로 쓰냐하면..?

 

아래 파일을 실행시킬 때 조금 다르다.

paravar.sh 

#!/bin/sh
echo $0
echo $1
echo $2
echo $*
exit 0

 

아래처럼 실행하면 결과가 나온다

sh paravar.sh 값1 값2 값3 => 실핼할 때 이렇게 한다.

값1
값2
값1 값2 값3

 


 

 

그다음에는 if 문과 case 문에 대해서 알아보자

역시 분기문이 없는 프로그래밍을 상상할 수는 없다.

하지만 형식이 조금 다르다.

if [ condition ]
then
	참일 때 실행
fi

 

??? 뭐가 다르긴 허다..?

예를 통해 알아보자

 

아래 파일을 실행하면 결과는 어떻게 나올까?

#!/bin/sh
if [ "abc" = "abc" ]
then
	echo "True"
fi
exit 0

True라는 결과를 얻을 수 있다.

**그리고 if문 안에 조건에 들어가는 모든 단어들은 띄어쓰기가 되어야 한다.

[띄고"abc" 띄고 = 띄고 "abc" 띄고] 이렇게다.

 

?? 근데 else 문은 없어??

있다. 당연히 있다.

아래가 끝이다. 뭐 특별한 것은 없으니 넘어가자

if [ condition ]
then
	참일 때 실행
else
	거짓일 때 실행
fi

 

 

**참고로 조건문에 들어가는 비교연산자와 논리연산자는 조금 다르게 생겨서 외워야 한다.

"abc" = "abc"  두 문자열이 같으면 참
"abc" != "abc" 두 문자열이 다르면 참
-n "abc" 문자열이 NULL이 아니면 참
-z "abc" 문자열이 NULL 이면 참
수식1 -eq 수식2 두 수식의 값이 같으면 참
수식1 -ne 수식2 두 수식의 값이 다르면 참
수식1 -gt 수식2 수식1이 크면 참
수식1 -ge 수식2 수식1이 크거나 같으면 참
수식1 -lt 수식2 수식1이 작으면 참
수식1 -le 수식2 수식1이 작거나 같으면 참
!수식 수식이 거짓이면 참

 

또한 if 문에서 다른 조건들도 집어넣을 수가 있는데 바로 파일과 관련된 조건이다.

-d 파일이름 파일이 디렉터리면 참
-e 파일이름 파일이 존재하면 참
-f 파일이름 파일이 일반 파일이면 참
-g 파일이름 파일에 set-group-id 가 설정되면 참
-r 파일이름 파일이 읽기 가능이면 참
-s 파일이름 파일 크기가 0이 아니면 참
-u 파일이름 파일에 set-user-id가 설정되면 참
-w 파일이름 파일이 쓰기 가능이면 참
-x 파일이름 파일이 실행 가능이면 참

 

또한 조건문에 빠지지 않는 case문도 존재한다.

if를 계속 쓰는 것은 비효율적이니까

당연히 case도 있어야 한다.

근데 case문도 형식이 완전 달라서 조금 외워야 하는 부분이 존재한다.

 

이것도 예를 들면 바로 이해가 된다.

#!/bin/bash
case "$1" in
    start)
    	echo "start";;
    stop)
    	echo "stop";;
    restart)
    	echo "restart";;
    *)
    	echo "***";;
esac
exit 0

 

위의 예시는 $1 (파라미터 변수) 에 따라 4가지 분기가 있는 것이다.

 

**case 문의 실행부분에서는 세미콜론 2개를 붙여써야 한다는 특징이 있다.

 

우리가 case 문 종료할 때는 그냥 괄호만 닫지만 여기서는

esac 라는 case를 거꾸로 쓴 키워드를 사용해서 case 문이 끝난 것을 알 수 있다.

 

다른 예도 한 번 보자

#!/bin/sh
echo "yes or no"
read answer
case $answer in
    yes | y | Yes | YES)
        echo "YEAH"
        echo "GOOD";;
    [nN]*)
    	echo "Noooh";;
    *)
        echo "what?";;
    	exit 1
esac
exit 0

 

조건에 들어가는 ' ) ' 왼쪽을 보면 알다시피 정규표현식도 있고 뭐 논리연산자도 있고 그렇다.

이렇게 유용하게 쓸 수 있다.

 

AND나 OR 같은 연산자는 다르게 사용할 수도 있다.

and는 -a 나 &&로 대신 사용할 수 있고

or는 -o나 || 로 대신 사용할 수 있다.

다만 -a 나 -o 같은 경우는 [ ] 안에서도 사용할 수 있는데

이 때 과호 등의 특수 문자 앞에서는 이스케이프 코드( \ )를 붙여서 써야 한다.

#!/bin/sh
echo "whatever"
read fname    => 파일이름을 입력받아서 아래 문장을 실행
if [ -f $fname ] && [ -s  $fname  ] ; then   =>  일반 파일이면서 크기가 0이 아니라면  then  실행
    head -5 $fname
else
    echo "NO FILE"
fi
exit0

 

 

 


 

그다음 반복문에 대해서도 알아보자

알다시피 for문을 쓴다.다만 for문에 들어가는 조건문이 조금 다르다.

for {var} in {value...}
do
    {expression}
done

 

그니까 변수와 값을 넣고 변수를 expression 에서 이용하게 된다.

예를 들어보자

#!/bin/sh
hap=0
for i in 1 2 3 4 5 6 7 8 9 10
do
    hap=`expr $hap + $i`
done
echo $hap
exit 0

 

보다시피 1부터 10까지의 합이 출력된다.

사칙연산을 하기 위해 expr 키워드를 이용한 것을 알 수 있다.

이거 말고도 다른 반복문이 있었는데..?

바로 while 문이다.

역시 있다.

조건식이 참이라면 계속 반복하는 것? 셸에도 있다.

#!/bin/sh
while [ 1 ]
do
    echo "good"
done
exit 0

 

꺼지지 않는 반복문과 비슷해보이는데??

맞다.

 

어 근데.. shell에는 우리가 안쓰는 다른 문장이 하나 더 있다.

바로 until 문이다.

~ 할 때까지 라는 영어 해석과 알맞다.

until은 조건식이 참이 될 때까지 반복하는 것이다.

즉, 거짓일 때 계속 반복한다.

굳이 왜 있냐 싶지만 그냥 알면 편리한 기능과 같다.

**이건 그냥 while 에서 until만 집어넣으면 되어서 예는 알아보지 않도록 하겠다.

 

또한 반복문을 탈출하거나 제어하는 키워드 역시 존재한다.

break, continue, exit, return 이 그 예다.

exit와 return만 셸스크립트에서 조금 다르다.

exit는 해당 프로그램 자체를 종료하는 것이고

return 은 함수 안에서 함수를 호출한 곳으로 돌아가게 하는 것이다.

사실 같은데.. 우리가 맨날 main에서 return 하면 프로그램이 종료되니까 return이 종료되는 줄 안다.

 


 

마지막으로 추가적으로 몇가지는 알아두자

1. 함수만들기

역시나 스크립트는 사용자 정의 함수가 있어야 한다.

셸 스크립트도 갖고 있다.

형식은 뭐 비슷하다.

{function name} ()  {
    ...
    ...
    ...
}

그냥 같다.

 

함수에 파라미터를 넣고 싶다??

{function name} ()  {
    $1, $2 .....
    ...
    ...
}

//호출 시에만 아래에 파라미터를 넣어서 호출하면 된다.
{function name} {param1, param2, ...}

 

 

유용한 pre-defiend function이 많다.

1. eval => 문자열을 명령문으로 인식한다.

2. export => 외부 변수로 선언한다. (다른 프로그램에서도 이용가능)

3. printf => C의 printf랑 비슷하다. 포맷 맞추어서 출력 가능하다.

4. shift => 파라미터 변수를 왼쪽으로 한 단계씩 아래로 시프트 한다.

$2 였던게 $1이 되고 그런 것을 말하는 것이다.

 

또 참고해야 할 것이 있는데

**커맨드의 결과 자체를 사용하고 싶다면 $ 을 잘 이용해야 한다.

**결과를 다시 파라미터로 이용하고 싶다면 set을 잘 이용해야 한다.

예를 들어보자

 

#!/bin/sh
echo "오늘은 $(date)"   => date 라는 커맨드가 실행된 것이 쓰이기 위해 $를 같이 씀
set $(date)     => date의 결과를 파라미터로 $1,$2...로 저장함.
echo "오늘은 $4 요일"   => 즉 $4가 date의 4번째 파라미터인 것을 알 수 있음
exit 0

 

 


이 정도면 Shell을 다룰 수 있다.

남은 것은 반복뿐이다.

 


그냥 끝내면 섭하니까

예를 들어 하나 예시를 들어볼까

서버 A의 파일을 서버 B에 보내는 스크립트를 만들어보자

필수적으로

1. SSH 설치가 되어 있어야함(#apt -y install openssh-server) 

2. 서버 B의 계정과 비밀번호, IP를 알고 있어야함 (포트는 대개 22)

3. 해당 계정에 대해 파일 쓰기 권한이 있어야함

 

서버 A의 파일을 처리해서 B로 보내는 것을 해보자.

 

결론은 이거다.

#!/bin/sh

sed 's/10.1.2.3/10.1.2.4/g' before.txt | sed 's/test_world/svc_world/g' | sed 's/test_user/svc_user/g' |sed 's/mypassword/mypassword!@#/g' > after.txt


scp /root/test/after.txt ubuntu@192.168.111.200:/home/ubuntu/after.txt

sed 라는 것으로 해당 파일을 편집할 수 있고

scp라는 것으로 파일을 전송할 수 있다.

 

저 스크립트를 sh {name}하면 된다.

 

728x90
반응형
그리드형

'DevOps > Shell' 카테고리의 다른 글

Shell Script - 4, escape character  (0) 2022.11.23
Shell Script - 3, Wildcard  (0) 2022.11.23
Shell script 11, Tip  (0) 2022.11.22
Shell Script - 2, Variable(1)  (0) 2022.11.21
Shell Script - 1, 작성하기  (1) 2022.11.21