쉘 스크립트 - 기본편

Violet_Evgadn·2023년 4월 30일
0

Linux

목록 보기
25/34

쉘 스크립트 작성

쉘 스크립트 형식

# !/bin/bash
# 주석 문구 추가
[Body of Bash Shell Script[
exit 0
  • # : 주석 문구 생성을 위한 예약어. Java의 //와 동일한 역할
  • #!/bin/bash : 해당 파일이 쉘 스크립트임을 알리는 구문
    • 위에서 말했듯 #을 붙이면 주석 처리가 된다. 따라서 쉘 스크립트에서 사실 #!/bin/bash는 아무런 역할을 하지 않는다. 따라서 필수 구문은 아니긴 하지만 쉘 스크립트에 이 문구를 붙이면 스크립트의 첫 줄만 확인해도 쉘 스크립트인지 확인할 수 있으므로 관리가 용이해진다.
  • exit 0 : 쉘 스크립트 종료를 나타내는 예약어. C언어의 return 0와 동일한 역할

이렇게 만들어진 쉘 스크립트 파일은 주로 .sh라는 확장자명을 가지고 있다.

쉘 스크립트 실행 방법

# 1. 쉘 스크립트 파일에 실행 권한 부여
chmod +x [Shell Script 파일]

# 2. 실제 Script 실행
sh [Shell Script 파일]

위 과정을 통해 쉘 스크립트 파일을 작성하였지만 기본적으로 리눅스 측에서는 이 파일이 실행 파일인지 아닌지 알 수가 없다.

따라서 chmod 명령을 통해 일단 쉘 스크립트에 x 권한(실행 권한)을 부여해 실행 가능한 파일로 만드는 것이 선행되어야 한다.

이후 Script 실행은 sh 명령어를 통해 실행시킬 수 있다.

이때 sh 명령어 또한 Option을 부여할 수 있는데, 대표적으로 -v 옵션을 통해 Shell이 어디까지 제대로 수행되었는지 출력함으로써 오류 발생 여부를 확인할 수 있다.

PATH

이전에 말했듯 리눅스 콘솔 창은 Bash Shell이라는 쉘을 통해 명령어를 실행한다. 그렇다면 현재 실행 파일인 쉘 스크립트 이름만 입력하여 Bash Shell을 통해 쉘 스크립트를 실행할 수 있지 않을까?

정답은 “반만 맞다”이다.

윈도우에서 자바를 설치했다면 아마 자바를 설치한 뒤 환경 변수에 이를 추가해 줘야지만 정상적으로 자바 실행이 가능하다는 것을 알고 있을 것이다.

이처럼 환경 변수는 어떠한 프로그램이 실행될 수 있도록 실행 파일의 경로를 잡아주는 값을 의미한다.

말이 조금 어려울 수 있는데, 조금 더 풀어서 이야기해보자.

프로그램 한 개를 실행시킨다고 가정하자. 그런데 실행할 프로그램 경로가 상대 경로로 설정되어 있다면 문제가 발생할 것이다.

왜냐하면 상대 경로는 현재 디렉터리 기준으로 하는 경로인데 이러한 상대 경로로 프로그램을 실행시키려면 프로그램을 실행시킬 디렉터리가 매번 동일해야 하기 때문이다.

실제 컴퓨터 환경을 생각해 보면 바로가기 기능을 통해 실행 파일을 바탕화면에서 실행시킬 수도 있고 가끔은 다운로드 디렉터리 안에서 실행시킬 수도 있는데 이런 경우엔 프로그램 실행 환경이 매번 바뀔 것이므로 사용이 어려워질 것이다.

따라서 OS는 컴퓨터 프로그램을 실행시킬 때 절대 경로를 기준으로 실행시킨다.

그런데 여기에서 또 문제가 프로그램을 실행시킬 때마다 프로그램의 절대 경로를 외워 놓고 있다가 외운 경로를 그대로 입력해야 한다는 것인데 이는 귀찮기도 하고 에러도 많이 발생할 것이다.

이러한 문제 때문에 환경 변수라는 값이 생겨났으며 리눅스의 환경 변수 역할을 하는 것이 바로 PATH인 것이다.

리눅스의 PATH에는 실행하고자 하는 명령이나 프로그램의 상위 Directory들을 가지고 있다. 반대로 말하자면 PATH에 명시되지 않은 경로에 존재하는 실행 파일들은 (절대 경로를 입력하지 않는 이상) 실행시킬 수 없다는 것을 의미한다.

따라서 만약 PATH에 등록되어 있는 경로에 쉘 스크립트 파일이 존재한다면 이름만으로 실행시킬 수 있지만, 그렇지 않다면 이름만으로는 실행시킬 수 없는 것이다.

리눅스 PATH 추가 방법

PATH 추가 방법에는 2가지 방법이 존재한다.

# 방법 1 : 환경 변수 PATH에 직접 주입 
export PATH=$PATH:[추가하고자 할 디렉터리]

# 방법 2 : bashrc를 통한 설정
cd ~ 
  → vi .bashrc 
  → export PATH=$PATH:[추가하고자 할 디렉터리] 문구 추가
  → source .bashrc

.bashrc는 별칭(alias)나 Bash가 수행될 때 실행되는 함수를 제어하는 지역적인 시스템 설정과 관련된 파일이다.

export PATH 같은 경우 모든 User에게, .bashrc는 PATH 구문을 파일에 추가한 User에게만 적용되므로 상황을 고려하여 방법을 선택하자.

PATH를 추가하지 않고서도 쉘 스크립트를 실행시키는 방법

PATH에 쉘 스크립트가 존재하는 경로를 추가하지 않더라도 ./[Shell Script 파일] 형식으로 쉘 스크립트를 활용할 수 있다.

./은 "현재 디렉터리"를 의미하는데 리눅스 입장에서는 현재 디렉터리의 절대 경로를 이미 알고 있는 상태이다.
따라서 ./[Shell Script 파일]을 입력할 경우 Linux에서는 Shell Script 파일이 존재하는 절대 경로를 아는 것과 마찬가지인 상황이므로 쉘 스크립트를 실행시킬 수 있는 것이다.


변수 지정 및 특수 기호

변수 지정할 때 신경 써야 하는 점

모든 변수는 "문자열(String)"으로 취급한다.

리눅스에서 모든 변수는 문자열로 취급하기 때문에 Java나 C언어 같은 프로그래밍 언어와는 다르게 변수 선언시 자료형을 입력하지 않아도 된다.
어차피 모든 변수는 문자열으로 취급되기 때문이다.

변수에 값을 대입할 때 "=" 좌우에 공백이 없어야 함

프로그래밍 언어에선 a = 3처럼 공백이 존재하도록 변수 값을 지정해도 a에 3이 저장되지만 리눅스에서는 a=3처럼 띄어쓰기가 존재하지 않아야지만 변수 지정이 가능해진다.
이는 띄어쓰기가 있을 경우 리눅스에선 모든 값을 Parameter로 인지하여 “a”, “=”, “3” 총 3개 값이 입력된 것으로 인지하기 때문이다.

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

변수에 처음 값이 할당되면 자동으로 변수가 생성됨

Java에서 a라는 변수를 선언하지 않았는데 a=3을 입력하면 그런 변수가 없다는 에러가 발생할 것이다.
하지만 위에서 말했듯 리눅스는 변수의 모든 자료형을 String으로 생각하기 때문에 변수를 미리 선언해야 할 필요성이 적다.

따라서 리눅스에선 선언되지 않았던 변수더라도 처음 값을 할당하면 먼저 변수를 선언하여 공간을 만든 뒤 값을 주입하는 행위까지 모두 수행해 준다.

변수 선언과 사용

변수에 지정된 값은 $ 기호를 사용하여 $[변수명] 구문을 통해 저장된 값을 반환받을 수 있다.

실제 리눅스에서 어떻게 사용하는지 보며 이해하는 게 더 쉬우므로 아래 사진을 통해 이해하자.

추가로 read [변수명] 명령어를 입력하면 변수에 저장할 값을 입력할 수 있는 공간을 1라인 통째로 주기 때문에 더욱 편하게 값 입력이 가능해진다.
만약 붙여 넣기로 값을 입력하고 싶을 경우 이 방법이 더욱 편리할 수 있다.

알아둬야 할 리눅스 특수 문자

‘ ‘(작은따옴표)

작은따옴표로 문자열을 감쌀 경우 일반적인 문자열로 만들어주는 특수 문자이다.

중요한 점은 작은따옴표는 모든 특수 기호 또한 일반 문자로 간주하여 처리한다는 것이다.

“ “(큰 따옴표)

큰 따옴표로 문자열을 감쌀 경우 일반적인 문자열로 만들어주는 특수 문자이다.

위에서 설명한 ‘ ‘와 다른 점은 큰 따옴표의 경우 $, ₩(백 쿼터), \를 제외한 모든 특수 기호를 일반 문자로 간주하여 처리한다.
반대로 말하자면 위 3개의 특수 기호는 그 기능을 잃지 않는다는 것이다.

큰 따옴표는 여러 변수를 사용할 경우 변수를 구분할 때 사용하거나 변수를 1개만 불러올 때 사용한다.

위 사진을 보면 $variance에서 $는 그 역할을 다하지만 ?의 경우 문자 1개를 대체하는 와일드카드의 역할을 잃어버렸음을 알 수 있다.

\(역 슬래쉬)

특수문자 바로 앞에 사용될 경우 뒤에 나오는 특수 문자의 효과를 없애 일반 문자처럼 처리하는 특수 문자이다.
이는 다른 프로그래밍 언어나 심지어 벨로그에서도 이러한 역할을 수행하므로 따로 예시를 보이진 않겠다.

참고로 명령어 맨 뒤에 \가 나온다면 이는 무효화할 명령어가 "엔터"가 된다. 즉, 엔터를 무효화므로 아래 줄에 있는 명령어와 현재 줄의 명령어가 연결되는 결과로 이어진다.

;

명령어 2개를 한 줄에 표현하기 위해 사용하는 특수 문자이다.

|와는 다르게 ; 기준으로 앞 뒤에 있는 명령어는 서로 영향을 주지 않으며 단순히 왼쪽 명령어부터 차례대로 실행될 뿐이다.


쉘 스크립트에서 유용하게 활용하는 명령어

basename

basename [경로] [확장자]

입력한 경로 중 가장 하위에 존재하는 파일(혹은 디렉터리) 이름만 추출하는 명령어이다.

확장자는 선택 옵션으로 만약 확장자명을 입력할 경우 확장자까지 제거하여 파일 이름을 추출해 준다.
(사실 확장자라고는 써놨지만 문자열의 마지막 부분을 일치하는 범위까지 삭제하는 것이다)

cmp

cmp는 compare의 약자로써 2개 파일의 차이를 비교할 때 사용한다.

Linux 콘솔창에서 cmp를 사용하면 2개 파일의 동일 여부와 어떤 부분이 다른지까지 알려주는데 쉘 스크립트에서는 조건식으로도 사용할 수 있다.
(동일한 파일일 경우 True, 다른 파일일 경우 False)

실제 예시를 통해 확인해 보자.
a.txt와 b.txt는 동일한 파일이며 c.txt는 다른 파일이다.

일단 아래와 같은 쉘 스크립트 파일을 만든 뒤 실행시켜 보자.

#/bin/bash
if cmp a.txt b.txt
then
        echo "True"
else
        echo "False"
fi


동일한 파일이므로 True를 반환한다.

이젠 b.txt를 c.txt로 바뀐 뒤 다시 실행시켜 보자.

위 사진에서 보면 False 결과값이 정상적으로 출력되지만 동시에 cmp 명령어의 결과 또한 같이 출력됨을 볼 수 있다.
cmp 명령어의 출력문까지는 궁금하지 않으므로 if cmp a.txt b.txtif cmp a.txt b.txt 1>/dev/null로 바꿔주면 cmp 결과값이 출력되지 않을 것이다.

whoami

현재 접속한 사용자의 Username을 출력하는 명령문이다.
권한 등을 사용자 명을 통해 제한하고 싶으면 사용할 수 있다.

sleep

sleep [시간]

지정한 시간만큼 잠시 쉘 스크립트 실행을 중지하는 명령문이다.
기본적으로 시간에는 자연수뿐 아닌 소수 또한 사용할 수 있다.

기본적인 시간의 단위는 초(s)이고, s(초), m(분), h(시간), d(단위) 같은 단위를 붙여 단위 시간으로도 실행 중지 시간을 지정할 수 있다.

tr

tr [Option] [문자열1] [문자열2]

문자열1을 문자열2로 바꿔주는 명령문이다.

tr은 문자열1과 동일한 문자열만 바꾸는 것이 아니라 문자열1의 N번째 문자를 문자열2의 N번째 문자로 바꾸는 명령어이다.

예를 들어 tr abc def라는 명령어를 입력했다 가정하자.
이때 문자열에 대한 변환 전후는 아래와 같다.

  • abc \rarr def
  • ab \rarr de
  • b \rarr e

문자열1과 완전히 동일하지 않더라도 문자 사이에 치환이 되는 것을 확인할 수 있다.
만약 문자열 길이가 다를 경우 짧은 문자열의 마지막 문자로 짧은 문자열을 연장하여 문자열의 길이를 맞춰준 뒤 문자열 치환이 시작된다고 생각하면 된다.

Option

  • -c : 문자열1에 포함된 문자를 제외한 모든 문자를 문자열2의 마지막 문자로 치환
    • 문자열 마지막에 항상 존재하는 \n도 대체함
  • -n : \n은 치환되지 않도록 함
  • -d : 문자열1에 존재하는 문자들 삭제
    • 문자열2는 입력하지 않아도 됨

cut

cut [Option] [구문]

라인 구문을 정해진 기준에 따라 처리한 뒤 선택한 부분을 추출하는 명령어이다.

이렇게 설명하니 조금 어려운데, 예를 들어 i am peter라는 구문에서 띄어쓰기를 기준으로 자른 뒤 2번째 문자열(am)을 추출하고 싶을 때 활용하는 명령어가 cut이다.

Option

  • -d, --delimiter

옵션 뒤에 지정한 문자를 구분자로 하여 구문을 자르게 하는 옵션이다.
기본적인 구분자는 TAB으로 설정되어 있다.

만약 띄어쓰기를 구분자로 구문을 자르고 싶다면 cut -d ' '처럼 사용하면 된다.

  • -f, --fields

-d 옵션을 통해 잘라진 문자열들 중 사용할 필드를 지정하는 Option이다.

-f4처럼 -f 옵션 뒤에 숫자를 바로 붙여 사용하며 -f2,4처럼 여러 개의 문자열을 가져올 수도 있다.

expr

expr은 식을 계산하는 명령어로써 사용방법은 expr [연산]으로 매우 간단하다.

expr은 사용법보다는 입력할 연산에 대해 잘 알아둘 필요가 있다.

expr은 명령어이기 때문에 변수 지정과는 다르게 띄어쓰기를 통해 연산자와 피연산자를 구분해줘야 한다.
즉, expr 5 + 1처럼 연산에 띄어쓰기가 되어있어야 정상적으로 연산이 가능하다.

expr이 사용할 수 있는 연산자는 아래와 같다.

  • 산술 : +, -, *, /, %
  • 논리 : |(or), &(and)
  • 관계 : =, >, >=, <, <=, !=
  • 문자열 : :(일치 혹은 substitute)

추가로 알아둬야 할 것은 expr이 사용하는 문구 중 >, <, * 같은 것들은 역할을 가지고 있는 특수 문자로 사용되고 있다.

따라서 쉘 스크립트 측에서 명령을 실행할 때 헷갈리지 않게 \를 연산자 앞에 붙여주나 연산자 앞뒤를 따옴표(큰 따옴표, 작은따옴표 상관없음)로 감싸줘야 한다.

echo

echo [문자열]

문자열을 출력하는 간단한 명령어이다.

echo 단독으로 활용한다기보다는 echo [문자열] > a.txt와 같이 Redirection과 같이 활용하여 다른 파일에게 값을 전달해 주는 용도로 많이 활용한다.

sed

sed [Option] “s/바꿀패턴/바뀐 후 패턴/g” 파일이름`

스트림 에디터를 활용하여 문자열을 치환하는 용도로 많이 활용된다.

위에서 이미 문자열을 치환하는 tr이 있었는데 왜 sed라는 새로운 명령어가 필요할까?

위에서도 말했지만 tr은 사실 문자열을 치환한다기보다는 N번째 문자끼리 치환된다고 보는 것이 맞다.
하지만 리눅스를 사용하다 보면 완전히 일치하는 문자열들만 치환하고 싶을 경우가 있을 것이다.

이럴 때 활용하는 명령어가 sed인 것이다.

sed 중 /g 같은 경우 찾은 모든 문자열에 대하여 치환을 하라는 구문이다.
예를 들어 abc abc가 있을 때 abcdef로 치환한다면 /g가 없을 경우 def abc로, /g가 있을 경우 def def로 치환될 것이다.

Option

  • -i : 변경된 내용을 파일에 적용

    • 대부분 파일 내용을 치환하기 위하여 sed를 활용하므로 거의 필수 값이라 봐도 무방
  • -r : 확장 정규식 패턴 사용


알아두면 좋은 쉘 스크립트 문구

종료 상황을 알려주는 구문

exit 0

정상적으로 쉘 스크립트 실행이 끝났음을 의미한다.

exit 1

어떠한 이유로 비정상적으로 쉘 스크립트 실행이 끝났음을 의미한다.

띄어쓰기에 따른 값 변화

" "

큰 따옴표 사이에 띄어쓰기가 있을 경우 빈 공간(Space)의 의미를 가진다.

""

큰 따옴표 사이에 띄어쓰기가 없을 경우 Null 값을 의미한다.

$

$는 변수 값을 반환시키는 것 이외에도 다양한 역할을 수행할 수 있다.

$?

최근 실행한 명령이나 쉘 스크립트의 종료 스테이터스를 출력하는 구문이다.

정상적으로 끝났으면 0을, 비정상적으로 끝났으면 exit 1이 실행되었을 것이므로 `이 반환될 것이다.

$n

파라미터 변수를 의미하며 입력한 명령어 중 n번째로 기입된 파라미터를 의미한다.

여기에서 잘 알아둬야 하는 것이 명령어의 파라미터는 0번부터 시작한다는 점이다.

  • yum -y install net-tools
    • $0 : yum
    • $1 : -y
    • $2 : install
    • $3 : net-tools

$@

모든 Parameter를 별도의 문자로 취급한다.

$*

모든 Parameter를 하나의 단어로 취급한다.

사실 $@$*는 설명만 봐서는 이해가 쉽지 않다.
하지만 예시를 보면 바로 이해 가능하므로 예시를 통해 알아보자.

  • hello hi
    • $* : "hello hi"를 1개의 단어로 취급
    • $@ : "hello", "hi" 총 2개의 단어가 입력된 것으로 취급

$#

입력한 모든 Parameter의 개수를 의미한다.
이때 중요한 것이 $0, 즉 0번째 Parameter는 그 개수에서 제외한다는 점이다.

예를 들어 man을 입력했을 경우 $#은 0을 반환한다.

profile
혹시 틀린 내용이 있다면 언제든 말씀해주세요!

0개의 댓글