Profile picture

[Shell Script] 셸 스크립트 문법

JaehyoJJAng2023년 04월 10일

Shell Script ?

Shell Script는 Shell이나 command line 인터프리터에서 구동되도록 작성된 스크립트이다.

여러 명령어들 하나하나를 하나의 스크립트 파일에 나열하여, 스크립트 파일을 자동으로 실행 함으로 효율적이고 간편하게 작업 처리를 할 수 있게 된다.


Shebang

쉘 스크립트 작성 전 항상 기억해야 할 것이 있다.

바로 첫 번째 행을 다음과 같이 작성 해주어야 한다.

#!/usr/bin/bash

echo $(which bash) # bash 디렉토리 위치 출력

스크립트 첫 번째 줄에 작성한 내용은 쉘 스크립트가 실행될 때 어떤 쉘로 스크립트를 실행할지정의하는 구문이다.


위와 같이 리눅스의 표준 shell인 bash로 실행하겠다는 선언을 하겠다는 뜻이다.

이러한 쉘 선언문을 Shebang이라고 한다


스크립트를 실행하려면 아래와 같이 실행 권한을 주어야 한다.

$ chmod u+x ./test.sh

쉘 변수 선언

변수의 타입에는 로컬변수와 전역변수, 환경변수, 예약변수, 매개변수 등 다양하게 존재한다.

  • 변수는 대,소문자를 구분해야 함
  • 변수의 이름은 숫자를 포함할 수 있지만, 숫자로 시작할 수는 없음
  • 변수는 모든 값을 문자열로 저장됨
  • 값을 사용할 때는 변수명 앞에 특수문자 $를 사용한다 (Ex. echo "${name}")
  • 값을 대입할 때는 특수문자 $를 사용하지 않는다. (Ex name="맛나")
  • 변수를 생성할 때는 = 대입문자 앞뒤로 공백이 없어야 한다.

#!/usr/bin/bash

MY_NAME="WTT"
PASSWORD=4567 # 따옴표로 감싸든 감싸지 않든 문자열로 저장됨

echo "나의 이름은 ${MY_NAME} 이다!"

전역 변수 & 지역 변수

쉘에서 선언된 변수는 기본적으로 **전역 변수(global variable)**이다.

단, 함수 안에서만 **지역 변수(local variable)**를 사용할 수 있는데 사용하기 위해서는 변수명 앞에 local을 붙여주면 된다

# 기본적으로 전역 변수로 지정됨
TEXT="Hello World"

function string_test() {
  # local을 붙여야 지역변수로 인식. 만일 local을 빼면 전역변수 TEXT를 덮어쓰게 됨.
  local TEXT="Hello Fucking World"
  echo "Local Variable: ${TEXT}"
}

# 함수 호출
string_test
echo ${TEXT}

# 변수 초기화
unset TEXT

변수 타입 지정

기본적으로 Bash 변수는 문자열만 저장한다.

다만, 다른 프로그래밍 언어같이 변수 자료형 타입을 미리 지정해주는 문법도 존재한다

# -r 읽기 전용 타입 (상수 선언과 같음)
declare -r VAR1
readonly VAR1

# -i 정수형 타입
declare -i num
num=5
echo "num: ${num}"

# -a 배열 타입
declare -a arr
arr=(Hello Im WTT)

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

환경 변수

쉘 스크립트에서 변수 명 앞에 export을 붙여주면 **환경 변수(environment variable)**로 설정되어 자식 스크립트에서 사용 가능하다.
다만 환경 변수 사용시 시스템에 미리 정의된 **예약 변수(reserved variable)**와 변수명이 겹치지 않게 주의해야 한다.

# /home/user/export_test.sh 파일을 만들고 작성
#!/usr/bin/bash

echo "${my_name}"
# 환경 변수 선언
export my_name="WTT"

# 자식 스크립트 호출은 스크립트 경로를 쓰면 된다
/home/user/export_test.sh
# 자식스크립트의 코드에서 부모스크립트에서 정의한 my_name 변수 값이 출력됨

매개 변수

프로그램에서도 실행할 때 인자를 주듯 쉘 스크립트도 역시 그렇게 할 수가 있다.
실행한 스크립트 이름은 ${0}, 그 이후는 전달받은 인자 값 들이다(${0},${1},${2}, ..)

종류 설명
$0 실행된 셸 스크립트명
$1 스크립트에 넘겨진 첫 번째 인자
$2 스크립트에 넘겨진 두 번째 인자
$# 스크립트에 넘겨진 인자 갯수
$* 스크립트에 전달된 인자 전체를 하나의 변수에 저장하면 IFS 변수의 첫 번째 문자로 구분
$@ $*와 동일하지만 IFS 환경 변수를 사용하지 않음
$! 실행을 위해 백그라운드로 보내진 마지막 프로그램 프로세스 번호
$$ 쉘 스크립트의 PID
$? 실행한 뒤의 반환 값 (백그라운드로 실행된 것 제외)
#!/usr/bin/bash

echo "스크립트 이름: ${0}"
echo "매개변수 개수: ${#}"
echo "전체 매개변수 값1: ${*}"
echo "전체 매개변수 값2: ${@}"
$ bash script.sh 1 2 3 4 5 6 7

스크립트 이름: test.sh
매개변수 개수: 7
전체 매개변수 값1: 1 2 3 4 5 6 7
전체 매개변수 값2: 1 2 3 4 5 6 7

예약 변수

쉘 스크립트에서 사용자가 정해서 만들 수 없는 이미 정의된 변수가 존재한다

변수 설명
HOME 사용자 홈 디렉토리
PATH 실행 파일의 경로
chmod, mkdir 등의 명령어들은 /bin 이나 /usr/bin, /sbin에 위치하는데 이 경로들을 PATH에 지정하면 굳이 /bin/chmod를 입력하지 않고, chmod만 입력해주면 된다
LANG 프로그램 실행 시 지원되는 언어
UID 사용자의 UID
SHELL 사용자가 로그인시 실행되는 쉘
USER 사용자의 계정 이름
FUNCNAME 현재 실행되고 있는 함수 이름
TERM 로그인 터미널

이외의 변수 명령어

  • set: 셸 변수 출력 명령어
  • env: 환경 변수 출력 명령어
  • export: 특정 변수의 범위를 환경 변수의 데이터 공간으로 전송하여 자식 프로세스에서도 특정 변수를 사용할 수 있도록 함 (전역변수 개념)
  • unsert: 선언된 변수 제거

쉘 이스케이프 문자

  • \f: 앞 문자열만큼 열을 밀어서 이동
  • \n: 새로운 줄로 바꿈
  • \r: 앞 문자열의 앞부분부터 뒷문자열 만큼 대체하고 반환
  • \t: 탭 만큼 띄움

쉘 산술 연산

쉘 스크립트의 변수 산술연산은 다른 언어에 비해 간단하지 않다.

Bash 변수는 본질적으로 문자열로 저장되기에 별도의 특수한 문법을 사용해 연산해야 한다.
그렇게 하면 Bash가 알아서 형변환하여 연산 또는 변수 비교가 가능하도록 해준다.


Bash에서는 계산을 처리할 수 있는 3가지 문법을 제공하고 있다.

  • expr
  • let
  • $(( ))

expr 연산자

  • expr는 역따옴표를 반드시 감싸준다. 역따옴표 대신 $( ) 로 감싸줘도 동작한다.
  • expr을 사용할 때 피연산자와 연산자 사이에 공백이 필요하다
  • 산술 연산할 때 우선순위를 지정하기 위해 괄호를 사용하려면 \처리를 해줘야 한다
  • 곱셈 문자 *는 \처리를 해줘야 한다
#!/usr/bin/bash

num1=10
num2=20

plus=$(expr ${num1} + ${num2})
minus=$(expr ${num1} - ${num2})
mul=$(expr ${num1} \* ${num2})
div=$(expr ${num1} / ${num2})
rem=$(expr ${num1} % ${num2})

echo "plus:     ${plus}"
echo "minus:    ${minus}"
echo "mul:      ${mul}"
echo "div:      ${div}"
echo "rem:      ${rem}"
$ bash ./script.sh

plus:     30
minus:    -10
mul:      200
div:      0
rem:      10

$(( )) 연산자

#!/usr/bin/bash

num1=42
num2=9

echo add:$((num1+num2))
echo sub:$((num1-num2))
echo mul:$((num1*num2))
echo div:$((num1/num2))
echo mod:$((num1%num2))
$ bash ./script.sh
add:51
sub:33
mul:378
div:4
mod:6

쉘 조건문

if문

배쉬 if문의 특이한 점은 fi와 **대괄호 []**이다.

여타 언어와 달리 중괄호를 안쓰기 때문에 fi로 if문의 끝을 알려주어야 하며,
주의해야할 점은 if문 뒤에 나오는 대괄호 []와 조건식 사이에는 반드시 공백이 존재해야 한다

if [ 값1 조건식 값2 ]
then
  수행1
else
  수행2
fi

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

if [] : deprecated 되었음.

이제 if [ 값1 조건식 값2 ] 가 아닌

if [[ 값1 조건식 값2 ]] 로 if문을 작성하는걸 추천함


if - elif - else 문

#!/bin/bash

num1="10"
num2="10"

if [ ${num1} -lt ${num2} ]; then # "-lt", A가 B보다 작으면 True
    echo "yes"
elif [ ${num1} -eq ${num2} ]; then # "-eq", A와 B가 서로 같으면 True
    echo "bbb"
else
    echo "no"
fi


# 이중 소괄호를 쓰면 논리연산자 기호 사용 가능
if (( ${num1} < ${num2} )); then
    echo "yes"
elif (( ${num1} == ${num2} )); then
    echo "bbb"
else
    echo "no"
fi

# 한줄 작성
if [ ${num1} -lt ${num2} ]; then echo "yes"; elif [ ${num1} -eq ${num2} ]; then echo "bbb";  else echo "no"; fi

(( ... ))와 [[ ... ]]의 차이점은 무엇인가?

  • (( ... ))는 산술 연산을 평가하는 데 사용됩니다. 이것은 산술 연산의 결과가 0이 아니면 참으로 간주됩니다.

  • [[ ... ]]는 조건부 표현식의 일반적인 형식으로, 문자열 비교, 파일 테스트 등 다양한 유형의 조건을 검사할 수 있습니다. 여기에는 산술 연산도 포함될 수 있습니다.


비교 연산

문자1 = 문자2             # 문자1 과 문자2가 일치 (sql같이 = 하나만 써도 일치로 인식)
문자1 == 문자2            # 문자1 과 문자2가 일치
문자1 != 문자2            # 문자1 과 문자2가 일치하지 않음
-z 문자                   # 문자가 null 이면 참
-n 문자                   # 문자가 null 이 아니면 참
문자 == 패턴              # 문자열이 패턴과 일치
문자 != 패턴              # 문자열이 패턴과 일치하지 않음
값1 -eq 값2             # 값이 같음(equal)
값1 -ne 값2             # 값이 같지 않음(not equal)
값1 -lt 값2             # 값1이 값2보다 작음(less than)
값1 -le 값2             # 값1이 값2보다 작거나 같음(less or equal)
값1 -gt 값2             # 값1이 값2보다 큼(greater than)
값1 -ge 값2             # 값1이 값2보다 크거나 같음(greater or equal)
if [ ${a} -eq ${b} ]; then
    echo "a와 b는 같다."
fi

if [ ${a} -ne ${b} ]; then
    echo "a와 b는 같지 않다."
fi

if [ ${a} -gt ${b} ]; then
    echo "a가 b보다 크다."
fi

if [ ${a} -ge ${b} ]; then
    echo "a가 b보다 크거나 같다."
fi

if [ ${a} -lt ${b} ]; then
    echo "a가 b보다 작다."
fi

if [ ${a} -le ${b} ]; then
    echo "a가 b보다 작거나 같다."
fi

# 한줄로 작성
if [ ${num1} -lt ${num2} ]; then echo "yes"; fi
num1=35
num2=48

# 이중 소괄호를 쓰면 조건문을 문자 대신 기호로 표현 가능하다. 단, 소괄호 안에 따옴표 쓰면 안된다.
if (( ${num1} < ${num2} )); then
    echo "yes"
fi

if (( ($num1 * $num2) - $num2 > 200 )); then
	echo ">200"
else
	echo "<200"
fi

이중 괄호 (( expression ))

expression에는 수식이나 비교 표현식이 들어갈 수 있다!.

! 논리 부정

~ 비트 부정

** 지수화

<< 비트 왼쪽 쉬프트

>> 비트 오른쪽 쉬프트

& 비트 단위 AND

| 비트 단위 OR

&& 논리 AND

|| 논리 OR

num++ 후위 증가

num-- 후위 감소


논리 연산

조건1 -a 조건2         # AND
조건1 -o 조건2         # OR
조건1 && 조건2         # 양쪽 다 성립
조건1 || 조건2         # 한쪽 또는 양쪽다 성립
!조건                  # 조건이 성립하지 않음
true                   # 조건이 언제나 성립
false                  # 조건이 언제나 성립하지 않음
if [ -f ${file1} -a -f ${file2} ]; then
    echo 'file1과 file2는 모두 파일입니다.'
else
    echo 'file1과 file2가 모두 파일인 것은 아닙니다.'
fi


if [ -f ${a} -a -d ${b} ]; then
    echo "a는 파일이고 b는 디렉토리"
fi
VALUE=10

if [ ${VALUE} -gt 5 -a ${VALUE} -lt 15 ] ; then
	echo "VALUE is greater than 5 and less than 15!"
fi

# 둘이 같은 문장이다.
if [ ${VALUE} -gt 5 ] && [ ${VALUE} -lt 15 ] ; then
	echo "VALUE is greater than 5 and less than 15!"
fi

# 대괄호 두개를 써서 표현할 수 도 있다.
if [[ ${VALUE} -gt 5 && ${VALUE} -lt 15 ]] ; then
	echo "VALUE is greater than 5 and less than 15!"
fi
# AND 
if [ ${string1} == ${string2} ] && [ ${string3} == ${string4} ] ;
then

# OR 
if [ ${string1} == ${string2} ] || [ ${string3} == ${string4} ];
then 

# 다중 조건 
if [[ ${string1} == ${string2} || ${string3} == ${string4} ]] && [ ${string5} == ${string6} ];
then

이중 대괄호

[[ "1.8.3" == 1.7.* ]]

&& , || , =~*(정규식매칭) 과 같은 확장 expression test 기능을 대괄호 내에 사용할 수 있도록 함


파일 검사

image

if [ -d ${변수} ]; then     # ${변수}의 디렉토리가 존재하면 참
if [ ! -d ${변수} ]; then	  # ${변수}의 디렉토리가 존재하지 않으면 참


if [ -e ${변수} ]; then     # ${변수}라는 파일이 존재하면 참
if [ ! -e ${변수} ]; then   # ${변수}라는 파일이 존재하지 않으면 참


if [ -r ${변수} ]; then     # 파일을 읽을 수 있으면 참
if [ -w ${변수} ]; then     # 파일을 쓸 수 있으면 참
if [ -x ${변수} ]; then     # 파일을 실행할 수 있으면 참


if [ -s ${변수} ]; then     # 파일의 크기가 0보다 크면 참
if [ -L ${변수} ]; then     # 파일이 symbolic link이면 참
if [ -S ${변수} ]; then     # 파일 타입이 소켓이면 참
if [ -f ${변수} ]; then     # 파일이 정규 파일이면 참
if [ -c ${변수} ]; then     # 파일이 문자 장치이면 참


if [ ${변수1} -nt ${변수2}]; then # 변수1의 파일이 변수2의 파일보다 최신 파일이면 참
if [ ${변수1} -ot ${변수2}]; then # 변수1의 파일이 변수2의 파일보다 최신이 아니면 참
if [ ${변수1} -ef ${변수2}]; then # 변수1의 파일과 변수2의 파일이 동일하면 참

반복문

for in 문

#!/usr/bin/bash

# 루프 돌 데이터에 띄어쓰기가 있으면 각각 돌음
for x in 1 2 3 4 5
do
	echo "${x}"
done

# 변수를 사용한 반복문
data="1 2 3 4 5"
for x in $data
do
	echo ${x}
done

# 배열을 사용한 반복문
arr=(1 2 3 4 5)
for i in "${arr[@]}" # arr[@] : 배열 전체 출력
do
	echo "${i}"
done

# sequence를 통한 for문. seq라는 프로세스가 순서대로 숫자를 출력해 주는 역할을 bash에 사용한 것이다.
for num in `seq 1 5`
do
  echo $num
done

# range를 사용한 반복문. {..} 중괄호와 점 두개를 쓰면 range처리가 된다.
for x in {1..5}
do
	echo ${x}
done
# 파일 리스트 출력
for line in `ls` # 역따옴표 써서 ls를 문자가 아닌 하나의 명령어로 실행
do
 echo $line # 해당 위치에 파일이나 디렉토리들이 출력
done

# 한줄 문법 (한줄로 쓰면 터미널에서 직접 스크립트를 실행 할 수 있다)
for line in `ls`; do echo $line; done

while 문

count=0
while [ ${count} -le 5 ]; 
do
    echo ${count}
    count=$(( ${count}+1 ))
done
count=0
while (( ${count} <= 5 ));  # 이중괄호 사용하면 논리기호 사용 가능
do
    echo ${count}
    count=$(( ${count}+1 ))
done

쉘 배열문

#!/bin/bash

# 배열의 크기 지정없이 배열 변수 선언
# 굳이 'declare -a' 명령으로 선언하지 않아도 바로 배열 변수 사용 가능함
declare -a array

arr=("test1" "test2" "test3") # 배열 선언 및 지정

echo ${arr[0]}  # test1


# 기존 배열에 1개의 배열 값 추가 3가지 방법
arr[3]="test4" 
arr+=("test5")
arr[${#arr[@]}]="test6" # 배열 길이를 인덱스로 사용해 push


echo ${arr[@]}  # arr의 모든 데이터 출력
echo ${arr[*]}  # arr의 모든 데이터 출력
echo ${#arr[@]} # arr 배열 길이 출력

echo ${arr[@]:2:3} # 2부터 3개의 요소

❗️ TIP

리눅스 쉘은 1차원 배열만 지원한다


배열 원소 삭제

  • /를 사용해 해당 문자열 부분이 있으면 삭제 할 수 있다.
  • 다만 unset을 이용해 삭제를 권고하는 편임.
arr=(1 2 3)
remove_element=(3)

arr=( "${arr[@]/$remove_element}" ) # 배열 1 2 3 에서 / 3을 없앰

echo ${arr[@]} # > 1 2
arr=("abc" "def" "defghi")

unset arr[1] # 배열 특정 인덱스 요소 삭제

echo ${arr[@]} > # abc defghi

unset array # 배열 전체 지우기

연관배열 (MAP)

key와 value 타입으로 저장된 배열을 말한다.

프로그래밍 고급언어에서 자주 등장하는 자료형 타입인데,파이썬의 딕셔너리 이라고 봐도 무방하다

# 연관배열 생성
declare -A map=([hello]='world' [long]='long long long string' [what is it]=123)

declare -p map # 연관배열 정보 출력
# > declare -A map=([long]="long long long string" ["what is it"]="123" [hello]="world" )

echo "map[hello]=${map[hello]}" 
# > map[hello]=world

key=hello # 변수를 인덱스로 넣어줘도 된다. (MAP의 특성)
echo "map[key]=${map[${key}]}" 
# > map[key]=world
# 연관배열 value 값 모두 출력 (MAP은 순서를 보장하지않는다)
echo "all=${map[@]}" # > long long long string 123 world

# 연관배열 key 인덱스 모두 출력 (MAP은 순서를 보장하지않는다)
echo "keys=${!map[@]}" # > long what is it hello

# 연관배열 길이 출력 
echo "length=${#map[@]}" # > length=3
# 원소 추가
map+=([key1]=value)
map+=([key2]=value2 [key3]=value3)
map+=(['long key']='long long long value')
map['like a C++']='value!!!!!'

# 원소 삭제 (키로 삭제)
unset 'map[intput1]'
# 루프1: 순차적으로 접근
for i in "${map[@]}"; do
  echo "${i}"
done
# long long long string
# 123
# world


# 루프2: 키로 접근
for key in "${!map[@]}"; do
  echo "map[${key}]=${map[${key}]}"
done
# map[long]=long long long string
# map[what is it]=123
# map[hello]=world

쉘 함수

다른 프로그래밍 언어와 달리 쉘 스크립트에서는 함수명 앞 function은 써주지 않아도 알아서 인식된다. 또한, 함수를 호출할때는 괄호를 써주지 않고 호출해야한다는 점이 다르다. 그리고 함수 호출 코드는 함수 코드보다 반드시 뒤에 있어야 된다. 함수 코드 보다 앞에서 호출 시 오류가 발생하기 때문이다.

#!/bin/bash

func(){
	echo "func()"
}

function string_test() {
    echo "string test"
    echo "인자값: ${@}"
}

#함수 호출
func

# 함수에 인자값 전달하기(공백의로 뛰어서 2개의 인자값을 넘김)
string_test "hello" "world"

**함수 인자(Argument) **

함수에 인자를 전달하는 방법은 함수를 호출하면서 공백으로 구분하여 이후 인자들을 하나씩 넣어주면 된다.

이렇게 넣어준 인자들은 함수 내부에서 다양한 방식으로 사용할 수 있는데,

대표적으로는 $1, $2처럼 인자가 들어온 순서대로 입력을 받아 사용하게 된다.

function test3()
{
    param1=$1
    param2=$2
    echo $param1 # a
    echo $param2 # b
    echo $@ # 파라미터 전체 출력
}

test3 "a" "b"

Shell command 실행

$() : command substitution 이라고 한다. 괄호 안에 쓰여진 문자를 명령어로 인식하여 실행하고 결과값을 반환해준다. 즉 명령어를 수행하고, output을 $() 안에 넣어준다.

그리고 위에서 expert 할때 배웠던 백틱(역따옴표)도, expert뿐만 아니라 명령문을 그대로 써주면 $()와 같이 쉘 명령어로 인식해서 명령어 결과를 반환해 준다.

#!/bin/bash

# 그냥 date 문자열 출력
echo date

# 백틱으로 감싸주면 date 명령어가 실행되게 된다.
echo `date`

# $()도 마찬가지
echo $(date)

# shell execution
echo "I'm in `pwd`"
echo "I'm in $(pwd)"

연습문제

구구단

#!/usr/bin/bash

for i in {2..9}
do
  for j in {1..9}
  do
    echo "${i} * ${j} = $(expr ${i} \* ${j})"
  done
done

현재 디렉토리에 있는 txt 파일들 실행권한 주기

#!/usr/bin/bash

for file in $(ls -lh *.txt | awk '{print $9}')
do
  chmod u+x ${file}
  echo "${file} 실행권한 추가 완료."
done

로그파일 정리하기

로그 파일 중에 2일 이상 지난 파일들은 압축해서 보관하도록 함.
압축된 로그 파일 중에서 3일 이상 경과한 것들은 삭제

#!/usr/bin/bash

LOGDIR="/var/log"
GZIPDAY=1
DELDAY=2

cd ${LOGDIR} # 로그가 저장되어있는 디렉토리로 이동
echo "cd ${LOGDIR}"

# 파일이 일반파일이고 파리명이 log.1, log.2 , .. 로 끝나고 -mtime +1 (수정된지 1일이 지난) 파일들을 검색하여 gzip 압축
# 2> : 중간에 오류가 나도 코드가 죽지 않고 오류 리다이렉션을 통해 넘기고 진행 (try .. catch 개념)
sudo find . -type f -name "*log.?" -mtime +${GZIPDAY} -exec bash -c "gzip {}" \; 2>

# 파일이 일반파일이고 파일확장자가 .gz이고 -mtime +2(수정된지 3일이 지난) 파일들 검색하여 삭제
sudo find . -type f -name "*log.?" -mtime +${DELDAY} -exec bash -c "rm -rf {}" \; 2>

cd 명령은 종료 상태 값을 확인해야 한다

아래와 같이 없는 디렉토리에 접근하려다 실패할 경우 현재 디렉토리에 있는 파일들을 날려버릴 수가 있다.

#!/usr/bin/bash

cd ~/git/testdir # 디렉토리 이동
rm -rf *
# 본 스크립트의 목적은 ~/git/testdir 디렉토리에 존재하는 모든 파일들을 지우는 것이지만
# 해당 경로가 존재하지 않는 경우 현재 내가 있는 디렉토리에 모든 파일들을 날려버릴 위험이 있다

따라서 반드시 아래와 같이 cd 명령의 성공여부를 체크해야 한다

#!/usr/bin/bash

DIR="~/git/testdir"

if [[ -d ${DIR} ]]
then
  rm -rf *
fi

또는 아래와 같이 체크할 수 있다.

#!/usr/bin/bash

# 1. and 연산자를 써서 앞의 명령어가 성공해야 뒤의 명령어가 실행되도록
cd ~/git/testdir && rm -rf *

# 2. 앞의 명령어가 실패하면, 뒤의 명령어를 실행하여 에러메시지를 출력하고 exit 1 종료함
# 1>&2는 모든 출력을 강제로 쉘 스크립트의 표준 에러로 출력
cd ~/git/testdir || { echo 1>&2 "cd ~/git/testdir failed"; exit 1 ;}
rm -rf *

자동 백업하기

#!/usr/bin/bash

# -z는 null 검사
if [[ -z ${1} ]] || [[ -z ${2} ]]; then
  echo "Usage : ${0} sourcedir targetdir"
else
  SRCDIR=${1} # Source DIR
  DSTDIR=${2} # Destination DIR

  BACKUPFILE="backup.$(date +%y%m%d%H%M%S).tar.gz"

  if [[ -d ${DSTDIR} ]]; then # 해당 디렉토리가 존재하면 아래 코드 실행
    tar -cvzf "${DSTDIR}/${BACKUPFILE}" ${SRCDIR}
  else
    mkdir ${DSTDIR}
    tar -cvzf "${DSTDIR}/${BACKUPFILE}" ${SRCDIR}
fi
$ bash ./script.sh logfile backupdir

난수 생성

#!/bin/bash

# 0부터 9까지의 랜덤 숫자 생성
random_num="$(($RANDOM % 10))"
echo $random_num

# 1부터 10까지의 랜덤 숫자 생성
random_num="$(($RANDOM % 10+1))"
echo $random_num

# 100부터 110까지의 랜덤 숫자 생성
random_num="$(($RANDOM % 11+100))"
echo $random_num

# 200부터 300까지의 랜덤 숫자 생성
random_num="$(($RANDOM % 101+200))"
echo $random_num

데이터를 솎아내서 백업하기

ls -al의 결과에서 '합계'와 상대경로 . , ..을 제외하고 나머지를 파일명과 파일사이즈만 출력하고 그 결과를 파일에 저장

#!/usr/bin/bash

PRE_IFS=${IFS} # IFS 백업
IFS="
" # IFS를 개행으로 변환
TOT=0 # 파일용량 합계 변수

cd /home/user # 정해진 위치에서
FileName="result.txt"
touch ${FileName}

echo "---------------------"
for i in $(ls -al /)
do
  S=$(echo ${i} awk '{print $5}' ) # 한 행의 5번째 필드(파일사이즈) 저장
  F=$(echo ${i} awk '{print $9}' ) # 한 행의 9번째 필드(파일이름) 저장

  # 만일 상대경로나 빈 칸인 경우 continue
  if [[ ${F} == "." || ${F} == ".." || ${F} == ""  ]]
  then
    continue
  fi

  # TOT=$(( ${TOT} += ${S} )) 이 수식은 실행되지 않음. awk로 나온 결과물은 문자열이기 때문에 $(( )) 산술연산이 안먹힘
  # expr은 자식프로세스로 터미널에서 실행한 결과이기 때문에 상관없음
  TOT=$(expr ${TOT} + ${S})

  echo "${S} ${F}" >> ${FileName}
done

echo ${TOT} # 파일사이즈 합계 출력
IFS=${PRE_IFS} # IFS 롤백

Loading script...