[Linux] 16장 셸 스크립트의 기초 지식

aramjs·2023년 12월 11일

Linux

목록 보기
13/14
post-thumbnail

16.1 셸 스크립트의 기본

  • 셸 스크립트 : 실행하고 싶은 명령어를 파일에 입력한 것

  • echo "hello";cd /;ls -l : 명령어를 ;로 연결하여 한 행으로 입력할 수 있다.

  • 셸 스크립트에서 빈 행은 무시된다. 셔뱅 아래에 넣는다 보통.

  • \로 명령어를 여러 행으로 나눠서 입력할 수 있다.

echo \
	"root"
  • 셸에서도 \ 를 입력하고 enter 누르면 프롬프트가 > 로 변한다.

    • 세컨더리 프롬프트 : 아직 커맨드 입력이 끝나지 않은 프롬프트. 이때 표시되는 프롬프트의 문자를 PS2로 설정할 수 있다.
    • 세컨 프롬프트에서도 \를 사용할 수 있다. 마지막에 enter를 누르면 전체 명령어가 실행된다.
  • 파이프라인으로 명령어 연결 시 | 뒤에 enter를 누르면 이어서 작성할 수 있다.

    • 여러 행에 나눠 입력하면 가독성이 좋아져 오타가 줄어든다.
  • 주석 : #주석 일시적으로 명령어 실행하지 않도록 하는 것을 코멘트 아웃이라 한다.


16.2 변수

  • 대입할 때는 $를 붙이지 않는다. 변숫값을 참조할 경우 변수명 앞에 붙인다.

$appdir=/home/jisu/myapp : 에러 발생.

  • 변수값을 참조할 때만 변수명 앞에 $를 붙여야 한다.

    • 양옆에 공백이 없어야 한다. appdir = /home 공백 사용 시 에러 발생.
  • 셸 스크립트는 대입 시 공백이 허용되지 않는다.

  • 변수명에 사용할 수 있는 문자

    • 알파벳, 숫자, 언더바

    • 숫자는 첫 글자로 사용할 수 없다.

    • 첫 글자에는 알파벳이나 언더스코어만 사용할 수 있다.

    • 알파벳은 대소문자 모두 사용가능

    • 환경 변수에는 대문자를 사용하고 그 외 일반 변수에는 소문자를 사용한다.

  • 변수 사용 시 주의점

    • 변수를 명확히 구분한다.

    • 예로 변수_backupecho하고 싶은 경우, ${변수}_backup 으로 출력한다.

    • 변숫값에 문자열을 연결하고 싶을 때는 문자열까지 변수 이름에 포함되어 해석되지 않도록 주의해야 한다.

    • 변수명을 {}로 감싸주면 된다.

#!/bin/bash

filename=residue.dat

echo ${filename}_backup

→ residue.dat_backup 이 출력된다.


16.3 쿼팅

  • 배시에서는 공백을 기준으로 명령어 인자를 구분한다.

  • cat file1 file2 : cat 명령어에 파일 두 개를 인자로 지정하면 각각 내용이 출력된다.

  • my file과 같이 공백을 포함하는 문자열을 인자 하나로 지정하고 싶은 경우 작은 따옴표나 큰 따옴표로 감싼다. 공백이 있어도 한 단어로 인식한다.

    • cat ‘my file’
  • *도 쿼팅하면 셸의 와일드카드로 인식되지 않는다.

    • 쿼팅하면 공백이나 메타 문자를 의도한 대로 사용할 수 있다.

    • grep ‘Be*r’ drink.txt : drink.txt에서 Be*r에 해당하는 행을 출력

#!/bin/bash
country=Korea
echo ‘I came from $countryecho “I came from $country
  • $ : 변수값을 꺼내온다는 의미이다.

  • 작은 따옴표로 감싸면 $가 그대로 출력되고,

  • 큰 따옴표로 감싸면 변수값을 꺼내온다.

  • vi에서 한 글자 교체하는 경우 r 를 이용한다.

    • .명령어로 반복 실행 가능하다.
  • 큰 따옴표 사용 시 $ 앞에 \를 붙이면 문자 그대로 출력할 수 있다.

    • echo “I came from \$country”

16.4 명령어 치환

  • 명령어의 출력 결과를 이용하고 싶을 때 명령어 치환을 사용하면
    명령어를 실행하고 출력된 결과를 취득할 수 있다.

  • $() 또는 ` : 백쿼트를 이용하여 괄호 안에 실행하려는 명령어를 작성한다.

  • 작성한 명령어를 실행하고 출력되는 표준 출력으로 치환된다.

  • 백쿼트는 시작과 끝 위치를 파악하기 어렵다. 명령어 치환에서 명령어 치환을 사용하려면 /를 붙여야 한다.

man date를 사용하여 옵션을 확인할 수 있다.

  • `date %m` : 오늘 month 출력

  • `date +%Y` : 년도 출력

  • `date ‘+%Y-%m-%d’` : 년-월-일 출력. 작은 따옴표로 묶어주면 좋다.

  • `echo “Today is $(date ‘+%Y-%m-%d’)”` : Today is 2023-11-14

  • 작은 따옴표는 $를 그대로 출력한다.

  • 큰따옴표는 변수값을 불러온다.

  • $ 대신에 ` 로 묶으면 결과가 동일하다.

  • echo “Today is date ‘+%Y-%m-%d’” : Today is 2023-11-14

  • 셸 스크립트에서도 $() 대신에 쓸 수 있다.

  • echo “Today is $(echo date)” : Today is date - $()로 치환

  • echo “Today is $(echo `date`)” : Today is 2023.11.14(화) - 백쿼트로 치환

  • echo “Today is `echo date`” : Today is date - 백쿼트로 치환

  • echo "Today is `echo `date`` " : Today is 2023.11.14(화) - 백쿼트로 치환

  • echo "Today is $(echo $(date))" : Today is 2023.11.14(화) - $()로 치환

백쿼트를 중첩으로 사용할 경우는 앞에 \를 붙여야 한다.


16.5 셸 위치 파라미터

  • 셸 스크립트에서 위치 파라미터는 셸 변수를 사용해 전달받은 인자를 다룰 수 있다.

  • $ ./parameters.sh aaa bbb ccc
    ------$0---------------$1---$2--$3

  • $0 : 숫자를 이름으로하는 변수로 셸 스크립트를실행할 때 지정한 인자가 각각 할당된다.


  • ./parameters.sh * : 인자에 와일드카드로 지정하면 모든 값이 셸 스크립트에 전달한다.

    • 현재 디렉터리의 파일들이 모두 인자로 들어간다.
  • $# : 인자 개수를 나타낸다.

    • ./parameters.sh aa bb c : $# 란에는 3이 출력된다.

$@, $*

  • 인자를 분할하지 않은 채 전체를 참조하고 싶은 경우 자동으로 분할된 인자를 참조할 수 있다.

  • 전체 인자가 $@$*에 담긴다.

  • $@ : 각각 독립적인 문자열로 취급한다.

  • $* : 하나의 문자열로 취급

  • 인자 전체를 출력하는 것은 같다. 보통은 $@로 한꺼번에 얻어와서 사용한다.

  • 배시에서도 일반 프로그래밍 언어처럼 값에 의한 조건 분기, 반복처리를 기술할 수 있다.

  • wrapper : 입력 받은 인자를 그대로 지정하여 다른 명령어를 실행하는 셸 스크립트

    • 환경 변수 LANG을 C로 설정하여 영문 환경으로 다른 명령어를 실행하고 있다.

커맨드 라인 인자와 관련된 셸 변수

  • $0 : 셸 스크립트 파일 이름
    #* : 모든 위치 파라미터. 큰따옴표로 감싸면 전체가 하나의 문자열이 된다.
  • $1, $2.. : 커맨드 라인 인자의 값
  • $# : 위치 파라미터의 개수
  • #@ : 모든 위치 파라미터. 큰따옴표로 감싸면 각 문자열이 감싸진다.
  • $0 : 셸 스크립트 파일 이름
    #* : 모든 위치 파라미터. 큰따옴표로 감싸면 전체가 하나의 문자열이 된다.

16.6 제어 구조

if

#!/bin/bash

if [ "$1" = "bin" ]; then
	echo "OK"
else
	echo "NG"
fi
  • 들여쓰기 필수 아니다.

  • 조건식에 if []; then 세미콜론을 붙어야 한다.

    • 다음 행에 then을 기재하면 세미콜론 생략이 가능하다.
  • [] 의 전후에 반드시 공백이 있어야 한다.

    • [ : 배시의 내장 명령어이므로 공백이 필수이다.

    • [ : 조건식이 참이면 0, 아니면 0 이외의 수를 반환한다.

  • if 뒤에 오는 것은 조건식이 아니라 명령어이다. 명령어의 결과가 참일 때 실행된다.

  • elif를 사용할 수 있다.

  • 명령어의 종료 상태

    • 정상 종료 시 0, 에러 발생 시 0 이외의 값을 반환한다.

    • $? 명령어로 확인할 수 있다.

  • if문에 [ 가 아닌 다른 명령어를 입력할 수 있다.

    • grep -q 'bash' /etc/passwd 의 종료 상태가 0인 경우 실행된다.

    • -q : 검색 결과를 출력하지 않는 옵션이다. 종료 상태만 필요한 경우 사용한다.

#!/bin/bash

if grep -q 'bash' /etc/passwd;then
	echo 'bash found'
fi

itest 명령어와 연산자

  • 리눅스의 test 명령어는 [ 명령어와 유사한 기능을 제공한다.

  • 유일한 차이점은 test 명령어를 사용할 때는 마지막 인자로 ] 를 지정하지 않아도 된다는 점이다.

  • if [ "$1" = "bin" ];then

  • if test "$1" = "bin";then
    두 표현은 동일한 의미를 가진다.

[ == test

문자열에 관한 평가 연산자

  • str1 = str2
  • str1 != str2
  • -n str1 : str1이 빈 문자열이 아님
  • -z str1 : str1이 빈 문자열임
filename=$1
if [ -z "$filename" ];then  # 변수 filename이 빈 문자열이면 실행한다.
	filename="default.dat"
fi

정수에 관한 비교 연산자

  • int1 -eq int2 : 같음
  • int1 -ne int2 : 다름
  • int1 -lt int2 : 1이 2보다 작음
  • int1 -le int2 : 1이 2 이하임
  • int1 -gt int2 : 1이 2보다 큼
  • int1 -ge int2 : 1이 2 이상임
  • 정수를 비교하는 연산자는 정수만 다룬다. 소수점이 포함된 값은 에러 발생

파일 속성에 관한 비교 연산자
-e file : exist
-d file : exist, 디렉토리
-h file : 존재, 심볼릭 링크임
-L file : 존재, 심볼릭 링크임
-f file : 존재, 일반 파일임
-r file : 존재, 읽기 권한 부여됨
-w file : 존재, 쓰기
-x file : 존재, 실행
file1 -nt file2 : 1의 변경 시간이 2보다 최근임
file1 -ot file2 : 1의 변경 시간이 2보다 오래됨

  • 셸 변수 logdir에 지정된 디렉토리가 존재하는지 -d 연산자로 확인하여 메시지를 출력한다.
#!/bin/bash

logir=/home/ldk/myapp/logs

if [ -d "$logdir" ]; then
	echo "로그 디렉터리: $logdir"
else
	echo "error: 로그 디렉터리가 존재하지 않음"
fi

결합 연산자

  • 조건식1 -a 조건식2 : and
  • 조건식1 -o 조건식2 : or
  • ! 조건식 : 반대로. not
  • () : 조건식을 그룹화

-o-a 보다 우선순위가 낮다.

  • if [ -d "$dir" -a \( "$dir" = "/home" -o "$dir" = "/etc" \) ]
    =if [ (-d "$dir") -a \( "$dir" = "/home" -o "$dir" = "/etc" \) ]
    (dir이 디렉터리이고,) (home 또는 etc인 경우) 를 and연산 하기 위해 괄호 사용했다. 메타문자이므로 이스케이프 문자를 붙여줘야 한다.

  • && : 처음 인자가 false일 때 할 필요가 없다. 처음 인자가 true일때만 실행한다.

  • || : 하나만 true여도 true이다. 처음 인자가 false일 때만 두 번째 인자 실행한다.

[ -f file.txt ] && cat file.txt : file.txt가 없는 경우 실행되지 않는다.

[ -f file.txt ] || touch file.txt : file.txt 가 없는 경우 두 번째 인자를 실행한다. 있는 경우 두 번째 인자를 실행하지 않는다.

하이픈 a로 바꾸기 시험에 출제.

  • -a(and)는 &&와 같다.

  • &&는 명령어 2개를 나열해야 한다.

  • || : 앞에꺼가 참이면 뒤에껄 검사하지 않는다.

  • 셸 스크립트의 종료 상태

    • 셸 스크립트도 종료 상태를 반환할 수 있다.

    • 특별히 지정하지 않으면 마지막으로 실행한 명령어의 종료 상태가 해당 셸 스크립트의 종료 상태가 된다.

    • 명시적으로 종료 상태를 지정하는 방법

    • exit <종료 상태>

    • exit 명령어를 실행하면 지정한 종료 상태가 return 된다.

    • 다른 프로그램에서 이 셸 스크립트를 호출했을 때 적절히 에러 처리를 할 수 있다.

    • exit 명령어에 아무것도 지정하지 않으면 셸 스크립트에서 마지막으로 실행된 명령어의 종료 상태가 반환된다.

for

  • 사용법
for 변수 in 리스트
do
	반복 처리
done
  • aaa, bbb, ccc 요소 세 개를 차례로 출력한다.
    for.sh
#!/bin/bash

for name in aaa bbb ccc
do 
	echo $ name
done
  • ./for.sh 를 실행하면
    Aaa
    Bbb
    Ccc
    이렇게 출력된다.

  • 경로 확장

    • 현재 디렉터리 안의 모든 html파일의 첫 번째 행을 출력한다.
#!/bin/bash

for filename in *.html
do
	head -n 1 "$filename"  # 파일의 앞 부분을 보여줌.
done
  • 명령어 치환을 사용할 수 있다.
    • seq 시작 숫자 마지막 숫자 : 수열 출력 명령어
#!/bin/bash

for i in $(seq 1 5)  # 1,2,3,4,5 

do
	touch "000${i}.txt"  # 0001, 0002, 0003.. txt파일을 생성한다.
done
  • 커맨드 라인 인자와 for

    • ./parameters.sh readme.txt 38 "hello"

    • readme.txt
      38
      hello

  • parameters.sh : 모든 커맨드 라인 인자가 차례로 출력된다.

#!/bin/bash
for parameter in "$@"  # 문자 단위로 인자 구분
do
	echo "$parameter"
done

case

  • 패턴을 차례로 확인하다 일치하는 패턴이 있으면 처리를 실행한다.

  • 어느 패턴에도 일치하지 않으면 아무것도 실행하지 않고 case문을 벗어난다.

    • 패턴과 세미콜론 사이에 실행하고자 하는 처리를 작성한다.

    • 패턴은 )로 끝나야 한다.

    • case문의 마지막은 esac로 끝난다.

    • 패턴 개수에는 제한이 없다.

  • 사용법

case "$1" in
	*.txt)
    	less "$1"
        ;;
    *.sh)
    	vim "$1"
        ;;
    *)
    	echo "not supported file : $1"
        ;;
    esac
  • 확장자가 sh이면 vim “$1”을 실행한다.

  • 해당되는게 없으면 echo “not supported file : $1”

  • case에서 패턴을 지정할 때 와일드카드를 지정할 수 있다.

  • *는 임의의 문자열을 의미하며 ? 는임의의 한 문자를 의미한다.

  • 마지막 패턴으로 *를 지정했다.

  • *는 빈 문자열을 포함하여 모든 문자열 패턴을 의미한다.

  • 어떤 패턴에도 해당하지 않는 경우를 지정하기 위해 사용한다.

#!/bin/bash

case "$1" in
	start | stop) #start 혹은 stop인 경우
		echo "OK"
		;;
	*) # 그 외 경우
		echo "NG"
		;;
esac
  • |로 기술한 여러 패턴 중 한 패턴에 일치하면 블록의 내용이 실행된다.

  • start과 stop 둘 중 하나가 지정되면 OK가 출력된다.

while

  • if문과 마찬가지로 명령어의 종료 상태가 0이면 do와 done 사이에 입력된 처리를 실행한다.
#!/bin/bash

i=1
while [ "$i" -le 10 ]  #i가 less than equal to 10일 때 실행한다.
do
	echo "$i"  #i 출력
	i=$((i+2)) #i=i+2
done
  • $(()) : 배시 셸의 산술 연산자이다.

  • $((i+2)) : `expr $i + 2` 와 동일하다.

    • `expr $i + 2` 사용 시 역따옴표를 사용해야 하고, + 양쪽에 빈칸이 있어야 한다.
  • sh기반에서는 expr 외부 명령어를 사용해야 한다. 이 경우 처리 속도가 느리고 가독성이 떨어진다.


16.7 셸 함수

  • 셸 스크립트 작성 시 똑같은 처리를 여러 번 중복하여 입력하는 경우 발생.

  • 중복되는 처리를 함수로 정의하면 코드가 깔끔해진다.

  • 함수 이름만으로 처리를 실행할 수 있다.

  1. 함수 정의
  2. 함수 호출
  • homesize라는 셸 함수를 호출하고 있다.

    • du -h | tail -n 1 : 각 디렉토리의 디스크 사용량이 나온다.

    • -h 로 사용량 단위를 human-readable하게 출력한다.

    • tail로 현재 디렉토리 사용량만 출력한다.

#!/bin/bash

homesize() # 함수 정의
{
	date
	du -h | tail -n 1
}

homesize # 함수 호출
  • 정의 전 호출할 경우 에러가 발생한다. 반드시 호출 전에 정의해야 한다.

  • 셸 함수 안에서도 위치 파라미터 $1, $2 를 사용할 수 있음

  • 위치 파라미터에는 커맨드 라인의 인자가 아니라 함수의 인자값이 대입된다.

  • 커맨드 라인에 아무것도 지정하지 않았지만 함수를 호출할 때 지정한 인자 값이 위치 파라미터에 대입되어 있는 것을 알 수 있다.

  • 함수를 호출할 때 인자가 위치 파라미터에 대입된다.

  • $0에는 함수 이름이 아닌 셸 스크립트의 이름이 들어있다.

  • $0은 위치 파라미터가 아닌 특수 파라미터이다.

셸 함수의 종료 상태

  • 마지막으로 실행한 명령어의 종료 상태가 셸 함수의 종료 상태가 된다.

  • 명시적으로 값을 반환하려면 return을 사용한다.

  • return이 실행되면 셸 함수의 처리가 종료되고 호출한 곳으로 실행 흐름이 돌아간다

#!/bin/bash

chechparam()
{
	if [ -z "$1" ]; then # 인자가 안들어오면 return 1
		return 1
	fi

	ls "$1" # 인자가 있으면 ls 실행
}

chechparam /home
echo "$?"
  • <chechparam /home의 실행 결과>
1
ls:aaa에 접근할 수 없습니다.
2
user1 user2
0
  • checkparam의 종료 상태는 0이다. 마지막 실행 명령어이다.
profile
안녕하세요.

0개의 댓글