suyeonme

[Shell] Shell Script 작성하기_2 본문

프로그래밍👩🏻‍💻/Shell

[Shell] Shell Script 작성하기_2

suyeonme 2023. 2. 4. 17:47

셸 스크립트 기본 규칙


셸스크립트를 작성하기에 앞서 기본으로 지켜야할 규칙은 다음과 같다.

#!/bin/bash

echo "Root Directory"
cd /
ls -l

# 한행으로 입력하는 경우(위와 동일)
echo "Root Directory";cd /;ls -l
  • 셸스크립트는 명령어가 하나씩 실행되며 여러 명령어를 ;로 연결하면 한 행으로 입력할 수 있다.
  • 빈행은 무시된다.
  • #로 주석을 남긴다.

커맨드라인이 길어질 경우, 행 끝에 \를 입력하면 여러 행에 걸쳐 입력할 수 있다.

#!/bin/bash

echo \
      "Root Directory"

변수(Variable)


  • 변수명=값으로 변수를 저장하며 그 사이에는 공백이 없어야한다.
  • 변수명에 사용할 수 있는 문자는 알파벳, 숫자, 언더스코어(_)이다. 
  • 숫자는 첫글자로 사용할 수 없다.
  • 일반적으로 환경 변수는 대문자, 그 외는 소문자로 사용한다.
  • $변수명으로 변수의 값을 참조한다.
  • 로컬 변수는 local 변수명=값으로 변수를 지정한다.
#!/bin/bash

appdir=/home/ldk/suyeonme # 변수 지정
echo $appdir # 변수 사용

func1()
{
	local message="Hello World"
}

Quoting

배시는 공백을 기준으로 명령어 인자를 구분하므로, 공백을 포함하는 문자열을 인자로 지정하고 싶다면 따옴표("" 또는 '')를 사용한다.

이렇게 공백을 포함하기 위해서 따옴표를 사용하는 것을 quoting이라고 한다.

cat ‘my file’
cat “my file”

‘’사용시 변수 확장이 이루어지지 않는다. 반면에 “”사용시에는 변수 확장이 이루어진다.

또한 “”사용시 \를 $앞에 붙이면 문자 그대로 출력할 수 있다.

country=Korea
echo 'I came from $country' # I came from $country
echo "I came from $country" # I came from Korea
echo "I came from \$country" # I came from $country

명령어 치환

$(명령어) 또는 명령어 형식으로 명령어를 실행하고 출력된 결과를 취득할 수 있다.

  • 명령어 형식은 시작과 끝의 위치를 파악하기 어렵기 때문에 $(명령어)가 권장된다.
  • 또한 명령어 치환에서 명령어 치환을 다시 사용하는 경우 \를 붙여야한다.
#!/bin/bash

# 현재 일자를 YYYY-MM-DD 형식으로 출력
filename=$(date '+%Y-%m-%d') # $(명령어)
filename=`date '+%Y-%m-%d'`  # `명령어`

touch "$filename"

echo "Today is $(date '+%Y-%m-%d')" # “”안에서도 명령어 치환을 할 수 있음

파라미터(Parameter)

위치에 기반한 셸변수를 사용해서 전달받은 인자를 다룰 수 있다.

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

echo $# # 3
echo $@ # aaa bbb ccc

파라미터의 종류는 아래와 같다.

변수 내용
$0 셸 스크립트 파일이름, 특수 파라미터
$1, $2 … 커맨드라인 인자의 값(위치 파라미터)
$# 위치 파라미터 개수
#@ 모든 위치 파라미터. 큰따옴표로 감싸면 각각의 위치 파라미터가 큰따옴표로 감싸짐
$* 모든 위치 파라미터. 큰따옴표로 감싸면 전체가 하나의 문자열로 감싸짐

파라미터 확장

파라미터 확장 기능을 사용하여 패턴에 매치되는 부분을 제거할 수 있다.

  • 최장 일치: 패턴에 일치하는 문자열이 여러개일 때, 가장 긴 문자열
  • 최단 일치: 패턴에 일치하는 문자열이 여러개일 때, 가장 짧은 문자열
# 파라미터 확장을 사용하여 절대 경로에서 파일이름만 추출
filepath=/home/ldk/scripts/diary.sh
echo "${filepath##*/}" # diary.sh
표기 설명
${변수명#패턴} 전방 최단 매치로 패턴에 일치하는 부분 제거
${변수명##패턴} 전방 최장 매치로 패턴에 일치하는 부분 제거
${변수명%패턴} 후방 최단 매치로 패턴에 일치하는 부분 제거
${변수명%%패턴} 후방 최장 매치로 패턴에 일치하는 부분 제거

if 문과 명령어의 종료 상태


if문

  • fi를 만날때까지 차례로 명령어의 결과를 확인하며 분기한다. elif는 else if이다.
  • 조건식에 이어 세미콜론(;)을 붙여야한다. 세미콜론을 생략하면 에러가 발생한다. 하지만 then을 다음 행에 기재하면 세미콜론을 생략할 수 있다.
  • [ ]의 전후에 반드시 공백이 있어야한다.
#!/bin/bash

# 기본 형태
if [ 명령어 1 ]; then
	명령어1의 결과가 참일 때 실행
elif [ 명령어 2 ]; then 
	명령어2의 결과가 참일 때 실행
else 
	위 모든 명령어의 결과가 거짓일 때 실행
fi

# then을 다음행에 기재하면 에러가 발생하지 않음
if [ "$1" = "bin" ]
then

명령어의 종료 상태

모든 명령어는 종료 상태라고 부르는 정숫값을 반환한다. 명령어가 정상 종료하면 0, 에러가 발생하면 0 이외의 값을 반환한다.

이 값은 $? 셸 변수로 확인할 수 있다.

  • 특별히 지정하지 않으면 셸스크립트 중에서 마지막으로 실행한 명령어의 종료 상태가 해당 셸 스크립트의 종료 상태가 된다.
  • 명시적으로 종료 상태를 지정하고 싶다면 exit 명령어를 사용한다. (exit 종료상태)
  • 종료 상태를 적절히 반환하면 다른 프로그램에서 셸 스크립트를 호출했을 때 적절히 에러처리를 할 수 있다.
# 종료 상태 지정
echo "exit status = $?"

# if문에서 종료 상태 지정
if [ -z "$1" ]; then 
	# 인자를 지정하지 않으면 종료 상태 1로 종료
	exit 1
fi

ls "$1"

[ 명령어


  • if문 뒤에 오는 [는 조건식이 아니라 배시의 내장 명령어이다.
  • [는 단순한 괄호가 아니며 [안에 값은 전부 [명령어의 인자로 전달된다.
  • [명령어는 인자로 전달된 조건식을 판정하여 참이면 0, 그외에는 0이 아닌 종료 상태를 반환한다. if문은 지정된 명령어를 실행하여 종료 상태가 0이면 참, 그렇지 않으면 거짓으로 판단한다.

[ 명령어의 조건식의 연산자 종류

test 명령어

  • [ 명령어와 유사한 기능을 제공한다. test 명령어를 사용할 때는 마지막 인자로 ]를 지정하지 않아도 된다.
  • 가독성의 이유로 [ 가 더 많이 사용된다.
if [ "$1" = "bin" ]; then
if test "$1" = "bin"; then

문자열 비교 연산자

연산자 내용
str1 = str2 str1과 str2가 같음
str1 != str2 str1과 str2가 같지않음
-n str1 str1이 빈문자열이 아님
-z str1 str1이 빈문자열임

정수 비교 연산자

정수만 다루기때문에 소수점이 포함된 값에대한 연산을 할 수는 없다.

연산자 내용
int1 -eq int2 int1과 int2가 같음
int1 -ne int2 int1과 int2가 같지않음
int1 -lt int2 int1이 int2보다 작음
int1 -le int2 int1이 int2 이하임
int1 -gt int2 int1이 int2보다 큼
int1 -ge int2 int1이 int2 이상임
#!/bin/bash

num1=$1
num2=$2
max=$num1

if [ "$num1" -lt "$num2" ]; then
	max=$num2
fi

연산자 결합

  • 여러 조건식을 지정할 경우 연산자를 결합할 수 있다.
  • -o는 -a보다 우선 순위가 높다. 따라서 -a, -o, !를 조합할 때 우선순위를 지정하기 위해 사용한다.
연산자 내용
조건식1 -a 조건식2 조건식1과 조건식2가 모두 참이면 참 (AND)
조건식1 -o 조건식2 조건식1과 조건식2중 적어도 하나가 참이면 참 (OR)
! 조건식 NOT
() 조건식을 그룹화
if [ -d "$datadir" -a -r "$datadir" ]; then
#   대상이 디렉터리  AND 읽기 권한이 있는 경우

if [ ! -d "$datadir" ]; then
# 대상이 디렉터리가 아닌 경우

if [ -d "$datadir" -a \\( "$dir" = "/home" -o "$dir" = "/etc" \\) ]
# 셸 변수 $dir이 디렉터리이며 /home 또는 /etc인 경우

&&와 || 연산자

  • 명령어1 && 명령어2는 명령어 1이 정상 종료하여 0이 반환된 경우에만 명령어 2가 실행된다.
  • 명령어1 || 명령어2는 명렁어 1이 정상 종료하지 않아서 0이 반환되지 않은 경우에만 명령어 2가 실행된다.
[ -f file.txt ] && cat file.txt # 파일이 존재하면 cat 명령어 실행
[ -f file.txt ] || touch file.txt # 파일이 존재하지 않으면 touch 명령어 실행

# if문에서 사용
str=$1
if [ "$str" = 'home' ] || [ "$str" = 'usr' ]; then
	...
fi

반복 연산자


for loop

  • 공백이나 탭으로 구분된 단어 리스트에 대해 반복 처리를 수행하는 구문이다.
  • 셸스크립트에서는 for문과 $@를 사용하는 경우가 많아서 in <파일>을 생략하면 자동으로 $@가 리스트로 지정된다. (for parameter in "$0"는 for parameter와 동일하다.)

아래 예시의 seq <시작 숫자> <마지막 숫자>는 수열을 출력하는 명령어이다.

# 기본 문법
for 변수 in 리스트
do
	반복 처리
done

예시

for name in aaa bbb ccc
do
	echo $name
done

# 1) 경로 확장 사용(현재 디렉터리의 모든 html 파일에 대해 동일 처리 수행)
for filename in *.html
do
	head -n 1 "$filename"
done

# 2) 명령어 치환(1~5까지 숫자를 이용해 0001.txt~0005.txt 파일을 생성)
for i in $(seq 1 5)
do 
	touch "000${i}.txt"
done

# 3) 모든 커맨드라인 인자에 대해 수행
for parameter in "$0" 
do
	echo "$parameter"
done

case statement

esac는 case를 거꾸로 뒤집은 문자열이다.

case <문자열> in
	<패턴 1>)
		처리 1
		;;
	<패턴 2>)
		처리 2
		;;
...
esac

# 1) 와일드카드 사용
case "$1" in
	*.txt)
		less "$1"
		;;
	*.sh)
		vim "$1"
		;;
	*)                                 # 어떤 패턴에도 해당하지 않는 경우
		echo "not supported file : $1"
		;;
esac

# 2) |로 여러 패턴을 기술
case "$1" in
	start | stop)
		echo "OK"
		;;
	*)
		echo "NG"
esac

while loop

지정한 조건이 참일 동안에만 반복하여 처리를 실행하는 제어 구문이다.

while <명령어>
do 
	반복 처리
done

# 셸변수 i의 값이 10이하일 동안 i의 값을 출력하고 2를 더하는 예시
i=1
while [ "$i" -le 10 ]
do
	echo "$i"
	i=$((i+2))
done

산술연산자


배시에서는 $(())를 사용하여 산술 연산을 쓸 수 있다. 셸변수에 추가로 $를 붙이지 않아도 되며 가독성이 좋고 처리속도가 빠르다.

 

i=10
echo $(( i + 1 ))
echo $(( i / 2 ))
echo $(( i * 2 ))
echo $(( i ** 2 )) # i의 2제곱

sh기반 셸스크립트

  • sh 기반의 셸스크립트에서는 expr이라는 외부 명령어를 사용해서 산술 연산을 할 수 있다. 매번 외부 명령어를 호출하기 때문에 처리 속도가 느려지고 가독성이 떨어진다.
i=`expr $i + 2`

Function


  • function이나 ()중 하나는 생략할 수 있다.
  • 함수는 반드시 호출하기 전에 정의해야 한다.
  • 함수 안에서는 함수를 호출할 때 인자가 위치 파라미터에 대입된다.
  • 함수 안에서 마지막으로 실행한 명령어의 종료 상태가 셸 함수의 종료 상태가 된다. (명시적으로 종료 상태의 값을 반환하려면 return <종료 상태> 명령어를 사용해야한다.)

함수 선언은 아래와 같이 총 3가지 방식이 있다.

# 1
function <함수 이름>() 
{
	처리
}

# 2
function <함수 이름>
{
	처리
}

# 3. 이 방식이 가장 많이 사용됨
<함수 이름>
{
	처리
}

예시

homesize() 
{
	date
	du -h | tail -n 1
}

homesize  # 함수 호출

# 셸 함수에서 파라미터 사용
print_parameters()
{
	echo "\\$1 = $1"
	echo "\\$2 = $2"
}

print_parameters aa bb

 

'프로그래밍👩🏻‍💻 > Shell' 카테고리의 다른 글

[Shell] Shell Script 작성하기_1  (0) 2023.02.04
Comments