본문 바로가기
프로그래밍/리눅스

Linux CentOS7 Shell 프로그래밍

by 참외롭다 2023. 5. 22.
반응형


 

셸 프로그래밍이란 셸에서 사용되는 여러 명령어를 모아 하나의 파일로 만드는 과정을 말한다.

 

# 셸 스크립트 작성법

 

# vi 편집기를 사용해 파일을 생성한다.
$ vi who2.sh

# 사용할 셸을 명시한다.
#!/bin/bash

# 원하는 명령 및 구문을 입력한다
echo "Login List"
who

# 스크립트를 실행 가능하도록 권한을 변경한다.
$ chmod 755 who2.sh

# 실행시킨다.
$ ./who2.sh

# 셸 스크립트 실행하기

 

사용자의 홈 디렉터리에서 셸 스크립트를 생성한 뒤 실행하려면 파일명 앞에 './' 를 붙여야 한다. 현재 디렉터리 안에 있는 해당 파일을 실행시키라는 뜻이다.

 

현재 디렉터리에 위치하지 않은 스크립트를 실행하기 위해선 스크립트의 경로가 PATH 환경 변수에 등록되어 있어야 한다.

 

# 계속적으로 사용할 스크립트라면 'mkdir ~/bin' 또는 'mkdir ~/.local/bin' 명령을 실행한 후에
# 생성한 디렉터리 내부에 위치시키면 스크립트 파일의 위치와 상관없이 실행 가능하다.

# 홈 디렉터리를 PATH에 등록하려면 'export PATH=$PATH:$HOME' 을 실행하면 된다.

$ echo $PATH

# 허가권 없이 셸 스크립트 실행하기

 

셸 스크립트에 실행 권한을 부여하지 않고도 실행하려면 다음과 같은 3가지 방법을 사용할 수 있다.

 

# 앞부분에 sh 명령 덧붙이기

$ sh who2.sh

# 앞부분에 source 명령 덧붙이기

$ source who2.sh

# 앞부분에 . 덧붙이기

$ . who2.sh

# 셸 프로그래밍 문법

 

# 주석
# '#'를 이용해 한 라인을 실행과는 무관하게 한다.

#!/bin/sh
# Auth : posein
# Work : hello world
echo 'hello world' # 문자열을 출력한다.

# 변수
# 데이터를 저장하는 공간을 할당한다.
# 셸에서의 변수형은 문자열만을 가지기때문에 변수형 선언이 필요없다.
# var=value

# 기타 변수 대응법

# name 변수에 들어있는 값으로 치환한다.
${name}

# name 변수가 null이면 value를 할당하여 저장하고, name에 값이 있으면 그 값을 사용한다.
${name:=value}

# name 변수가 null이면 value를 사용하지만 name에 저장하지는 않는다.
${name:+value}

# name 값이 있다면 그 값을 사용하고, 값이 없으면 value 값을 대입한다.
# name에 value 값을 저장하지 않는다.
${name:-value}

# name 변수의 값이 있으면 기본값으로하고, null인 경우 error를 내면서 value 값으 보여준다.
${name:?value}

# name의 문자열 길이를 반환한다.
${#name}

# name 값에서 offset 만큼 삭제한 후에 갑을 반환한다.
${name:offset}

# name 값에서 offset 만큼 삭제한 후에 length만큼 센 뒤 값을 반환한다.
${name:offset:length}

# 관련 환경 변수

# 입력필드 구분자로서 셀 상에서 입력을 읽어 들일 때 글자를구분하기 위한 목적으로 사용되는 문자 목록을 말한다.
$IFS  

# 아규먼트 변수

# 특별한 내장 변수로 '위치 매개 변수' 라고도 한다.
# 이 변수는 매개 변수를 불러올 때 스크립트의 명령행 인자를 담당한다. 

# $0 : 실행된 셸 스크립트명
# $1 : 스크립트에 념겨진 첫 번째 아규먼트
# $2 : 스크립트에 넘겨진 두 번째 아규먼트
# $# : 스크립트에 넘겨진 아규먼트의 개수 
# $$ : 셸 스크립트의 PID
# $* : 스크립트에 전달된 인자 전체를 하나의 변수에 저장하면 IFS 변수의 첫 번째 문자로 구분
# $@ : $* 와 동일(다른점은 IFS 환경 변수를 사용하지 않음)
# $? : 실행한 뒤의 반환 값, 참이면 0, 거짓이면 1을 반환
# $- : 현제 Shell이 호출될 때 사용한 옵션들

$ vi test.sh
#! /bin/bash
num=2

# 'this is the 2nd' 라고 출력된다.
echo "this is the ${num}nd"

# var 가 null이므로 값에 ls가 들어가면서 해당 명렁이 실행된다.
$ vi var
#! /bin/bash
var=
${var:=ls}
echo $var

# DIR 값이 null인 경우 temp를 대입하지만, 저장하지는 않는다.
$ echo $DIR
$ echo ${DIR:-temp}
temp
$ echo $DIR

$ vi test4.sh

#!/bin/bash
echo "This script file $0"
echo "Argument Count is $#"
echo "Process ID is $$"
echo "Argument List \$* : $*"
echo "Argument List \$@ : $@"
echo "Argument 1 : $1"
echo "Argument 2 : $2"
echo "Argument 3 : $3"

$ ./test4.sh a b c

This script file ./test4.sh
Argument Count is 3
Process ID is 133
Argument List $* : a b c
Argument List $@ : a b c
Argument 1 : a
Argument 2 : b
Argument 3 : c

# Shell 변수 관련 명령어

# set : 일반적으로 셸 변수를 출력하는 명령.
# env : 환경 변수를 출력하는 명령어. export 된 변수는 이 명령으로 확인해야 한다.
# export : 특정 변수의 범위를 환경 데이터 공간으로 전송하여 자식 
# 프로세스에서도 특정 변수를 사용 가능하게 한다. 전역변수 개념으로 이해할 수 있다.
# unset : 선언된 변수를 제거한다.

$ vi main

echo shell programming
name=test
city=seoul
echo name is $name
echo city is $city
# 전역변수화
export name
./sub
echo stop main

$ vi sub

echo start sub
echo name is $name
echo city is $city
echo stop sub

$ chmod 755 main sub
$ ./main

shell programming
name is test
city is seoul
start sub
name is test
city is
stop sub
stop main

# echo문 과 escape 문자

 

echo문은 라인을 제거하는 명령해석기이며 -e 옵션과 같이 ₩로 시작하는 이스케이프 특수 문자를 사용할 수 있다.

 

# escape 특수 문자의 종류
# \f : 폼피드, 앞 문자열만큼 열을 밀이서 이동시킨다.
# \n : 새로운 줄로 바꾼다.
# \r : 캐리지 리턴. 앞 문자열의 앞부분부터 뒷 문자열만큼 대체하고 반환한다.
# \t : 탭만큼 띄운다.
# \\ : \를 표기한다.

$ echo "Hi\nHello"
Hi\nHeloo

# 새로운 줄로 바꾼다.
$ echo -e "Hi\nHello"
Hi
Hello

$ echo -e "yuloje\ralin"
# 앞 문자열의 yuloje를 받아서 앞 부분부터 alin으로 대체한 후에 반환하여 alinje라고 출력된다.
alinje

# 간단한 조건식

 

명령행에서는 test 명령을 이용해 간단히 비교할수 있고, 스크립트에서는 if문과 같이 사용된다. 셸 스크립트에서 사용 할 경우 test라는 명령어를 생략하고 []를 이용한다. 테스트 조건들은 '[]' 사이에 입력하면 되고 반드시 공백문자가 들어가야한다.

 

$ test 표현식
$ [표현식]

# 문자열 비교 표현식

# [string] : 빈 문자열이 아니라면 참
# [string1 = string2] : 두 문자열이 같다면 참
# [string1 != string2] : 두 문자열이 다르면 참
# [-n string] : 문자열이 null이 아니라면 참
# [-z string] : 문자열이 null이면 참


# 산술 비교 표현식

# [expr1 -eq expr2] : 두 표현식 값이 같다면 참
# [expr1 -ne expr2] : 두 표현식 값이 같지 않다면 참
# [expr1 -gt expr2] : expr1 > expr2 이면 참
# [expr1 -ge expr2] : expr1 >= expr2 이면 참
# [expr1 -lt expr2] : expr1 < expr2 이면 참
# [expr1 -le expr2] : expr1 <= expr2 이면 참
# [!expr1] : expr1이 참이면 거짓, 거짓이면 참
# [expr1 -a expr2] : expr1 AND expr2의 결과
# [expr1 -o expr2] : expr1 OR expr2의 결과


$ test 20 -gt 30
$ echo $?
1

$ test 20 -lt 30
$ echo $?
0

# 로그인한 사용자가 yuloje인지 판별하여 결과값 0,1을 반환한다.

$ vi ex.sh
["$LOGNAME" = "yuloje"]
echo $?
1

# 조건문

 

# if 문

$ vi test.sh

if ["$SHELL" = "/bin/bash"]
then
	echo "your login shell is bash"
else
	echo "yout login shell is $SHELL"
fi

# case 문

# case 문자열
# in
# 	정규식1) 명령어;;
#	정규식2) 명령어;;
#	...
#	esac

# vi cal
#!/bin/bash
read number;
case $number in
1)who;;
2)date;;
3)pwd;;
4)ls -l;;
*)echo "없는 번호"
esac

# select 문

# select 변수 in 값1, 값2...
#	do
#		실행문장
#	done

$ vi sele1

#!/bin/bash
echo "What is your favorit OS ?"
selelct var in "Linux" "Free BSD" "Windows" "Solaris" "Other"
	do
    	echo "You have selected $var"
        break
    done

./sele1    

What is your favorite OS?
1)Linux
2)Free BSD
3)Windows
4)Solarit
5)Other
#? 1
you have selected Linux

# 반복문

 

# for 문
#	for 변수 in 값1, 값2
#	do
#		실행문장
# 	done

$ vi tt4
for flower in Rose Tulip Lily
	do
    	echo $flower
    done
$ ./tt4
Rose
Tulip
Lily

# while 문
# while 조건문
#	do
#		실행문장
#	done

$ vi tt7
var=1
while ["$var" -le 5]
	do
    	echo $var
        var = "expr $var + 1"
    done

# until 문
#	until 조건문
#	do
#		실행문장
#	done

$ vi tt9
COUNTER=20
until [$COUNTER -lt 10]
	do
    	echo COUNTER is $COUNTER
        COUNTER=`expr $COUNTER - 1`
        done

# 함수

 

$ vi fun
#! /bin/bash
user_print()
{
	echo "shell programming"
}
echo "start user_print"
user_print
echo "end user_print"

$ ./fun

# 패턴과 패턴비교

 

# 일종의 문자열을 연산하는 것으로 특정한 패턴을 놓고 변수의 문자열 값이 이 패턴과 일치하는지 검사한다.
# ${var#pattern} : 처음부터 pattern과 맞는 var의 부분을 찾아 이 중 가장 작은 부분을 제거하고 나머지 반환
# ${var##pattern} : 처음부터 pattern과 맞는 var의 부분을 찾아 이 중 가장 큰부분을 제거하고 나머지 반환
# ${var%pattern} : 끝에서부터 pattern과 일치하는 var의 최소 부분을 제거하고 나머지 반환
# ${var%%pattern} : 끝에서부터 pattern과 일치하는 var의 최대 부분을 제거하고 나머지 반환

$ vi ex3

#!/bin/bash
unset var1
echo ${var1:-string2}
var1=string1
echo ${var1:-string2}
var1=/etc/sysconfig/network
echo ${var1#*/}
echo ${var1##*/}
var2=/etc/sysconfig/network-scripts/sysconfig/ifcfg-lo
echo ${var2%sysconfig*}
echo ${var2%%sysconfig*}
exit 0

$ ./ex3
string2
string1
etc/sysconfig/network
network
/etc/sysconfig/network-scripts/
/etc/

# 셸 프로그래밍 기타 사항

 

# 종료 코드
# 0 은 성공을 뜻하고 1 이상은 오류 상황을 뜻한다.
# 0 : 성공
# 1 : 일반적인 오류
# 2 : 셸 내장 명령어의 틀린 사용
# 126 : 파일이 실행가능하지 않음 
# 127 : 명령어를 찾을 수 없음
# 128 : 잘못된 인수 적용
# 128+n: 치명적인 시그널 n 에러
# 130 : ctl + c 키 조합에 의한 종료

# 우선순위
# 앨리어스
# 지정 키워드
# 함수
# type, function 같은 내장 명령어
# 스크립트, 프로그램, PATH 와 같은 환경 변수에 들어있는 디렉터리를 셸이 확인하여 실행

# 셸 프로그래밍 관련 명령어

 

# read

 

임의의 값을 키보드로부터 입력받는 명령어로 연속으로 여러 값을 받을 수도 있다. 인자값은 차례대로 처리된다.

 

# read 변수

$ read flower
rose

$ echo $flower
rose

$ read var1 var2
lin joon

$ echo $var1 $var2
lin joon

vi read1
#!/bin/bash

echo "두 자연수를 입력받아 뺄셈하는 프로그램"
echo "첫 번째 자연수는?"
read int1
echo "두 번째 자연수는?"
read int2

if [ $int1 -ge $int2 ]
	then
    	expr $int1 - $int2
    else
        expr $int2 - $int1
fi

$ ./read1

두 자연수를 입력받아 뺄셈하는 프로그램
첫 번째 자연수는?
6
두 번째 자연수는?
2
4

# echo

 

해당 문자열을 표준 출력에 표시하고 행 바꿈을 한다. 만일 문자열이 없으면 행 바꿈만 실행한다.

 

# echo [option] 문자열

# -n : 출력 결과에서 행 바꿈을 하지 않는다.
# -e : escape 문자를 사용할 수 있게 해준다.

$ echo HI!
HI!

$ echo -e "YES\nNo"
Yes
No

# break

 

for, while, until 등의 순환문에서 빠져 나온다.

# break [n]

$ vi br1
#!/bin/bash
rm -rf file*
echo > file1
echo > file2
mkdir file3
echo > file4
for var in file*
	do
    	if[ -d "$var"]
        	then
            break;
        fi
    done
    echo "directory name : $var"
    exit 0

$ ./br1
directory name : file3

# continue

 

for, while, until 등의 순환문을 계속해서 실행한다.

 

# continue [n]

$ vi co1
#! /bin/bash

rm -rf file*
echo > file1
echo > file2
mkdir file3
echo > file4

for var in file*
do
	if[ -d "var]
    	then
        	continue
     fi
echo "file name : $var"
done
exit 0

$ ./co1

file name  file1
file name : file2
file name : file4

# exit

 

상태 코드 n과 함께 스트립트를 종료한다. n은 0(성공) 이나 0 이 아닌 숫자(실패) 가 될 수 있다.

만약 n 이 주어지지 않으면 가장 최근에 사용된 명령의 상태 코드가 사용된다.

 

# exit[n]

# function

 

함수를 정의한다. 위치 매개 변수 ($1, $2, -)가 명령어 부분에서 사용 가능하다.

 

# function 함수명
# {
#	명령어	
# }

$ vi myfunc
#!/bin/bash
funciton myfunc
{
	echo "parameter is $1"
}

$ ./myfunc love

parameter is love

# getopts

 

명령행 인자를 처리해 올바른지 점검한다. 셀 스크립트 순환문에서 사용되며 명령행 옵션이 표준 형식에 맞는지 확인한다.

 

# getopts 문자열 변수명

# return

 

함수정의에서 사용되며 함수가 종료될 때 상태 코드 n을 반환한다. 만일 n 이 생략되면 직전에 실행된 명령 상태 코드를 반환한다.

# return [n]

# set

 

특별한 옵션 없이 set이라고 입력하면 셸 변수와 값을 출력한다. set은 셸에서 보통 변수의 값을 선언할 때 주로 사용하는데, 

bash 에서는 생략가능하다.

 

# set [option][문자열]

# -f : 셸에서 *,?와 같은 와일드카드 문자를 이용해 확장을 없엔다.
# -C : 리다이레션 기호인 > 를 이용해 파일의 덮어쓰기를 제한한다.
# -o : bash 환경 설정 관련 값을 확인하고 설정하는 옵션이다.

# 셀 변수와 값을 출력한다.
$ set

# a라는 변수에 값을 1로 지정한다. bash에서 set은 생략가능하다.
$ set a=1

# set 명령으로 설정된 매개인자를 확인할 수 있다.
$ set a b c
$ echo $1 $2
a a b

# 설정된 bash 환경 설정 값들을 확인한다.
$ set -o

# *,? 와 같은 와일드카드 문자의 기능을 없엔다.
$ set -o noglob

# *,? 와 같은 와일드카드 문자의 기능을 활성화한다.
$ set +o noglob

# 리다이렉션 기호인 >를 이용한 파일의 덮어쓰기를 금지한다. 파일을 새롭게 생성하거나 >> 기호를 이용한 파일 내용 추가는 가능하다
$ set -o noclobber

# shift

 

위치 매개 변수를 이동할 때 사용하는 명령이다.

# shift [n]
# 위치 매개변수를 n만큼 이동하는데, 만약 n이 생략되면 기본적으로 1이 적용된다.
# 즉, $2는 $1로, $3은 $2로 이동한다. 보통 매개 변수를 처음부터 끝까지 검색하느데 사용한다.

$ vi sh1
#!/bin/bash

while [$# -gt 0]
do
	echo "$# : $*"
    shift
done

$ ./sh1 a b c
3: a b c
2: b c
1: c

$ vi sh2
#!/bin/bash
echo $#
echo $1
echo $2
echo $3
echo $4
echo "____________"
shift 2

echo $#
echo $1
echo $2
echo $3
echo $4

$ ./sh2 a b c d

4
a
b
c
d
____________
2
c

# source

 

지정한 텍스트 파일의 내용을 읽고 실행한다. 콤마(.)와 source는 같은 기능을 한다.

 

# source 파일[인수]
# .파일[인수]

# eval

 

인자의 값을 구하는데 사용하고 만약 값이 실행 명령어와 같은 문자열인 경우에는 해당 명령을 실행시킨다.

 

$ vi ev1
#!/bin/bash
var1=10
var2=var1
eval result='$'$var2
echo $result

$ ./ev1
10

$ vi ev
echo "명령어입력?"
read command
eval $command
$ ./ev
명령어입력
pwd
/home/posein

# expr

 

인자로 사용되는 표현식의 값을 구할 때 사용하는데 산술연산이 가능하다. 셸 상태에서 명령어로도 사용 가능하다.

다만 셸 상태에서는 연산자 사용 시 * 등을 사용할 경우 앞에 \ 를 붙여서 사용해야하고 값과 연산자 사이는 반드시 한 칸 띄워야 한다.

 

# expr 값 연산자 값

# $var 값에 1을 더하여 var2를 지정한다.
$ var2 = `expr $var + 1`

# 더하기 연산을 실행해 값을 출력한다.
$ expr 3 + 2
5

# 곱하기 연산에는 \기호를 사용해야한다.
$ expr 3 \* 2
6

# printf

 

최근에 등장한 shell에서만 사용 가능한 명령어로 형식화된 출력을 할 때 사용한다.

 

# printf "format string" parameter1 parameter2

$ printf "$s\n" shell
shell

$ printf "%s %d\t%s\n" "shell programming" 100 gogo
shell programming 100 gogo

# true

 

셸 상에서 참의 의미를 가지는 0을 반환한다. 셸 스크립트 상에서 무한 루프를 만들때 사용된다.

 

$ vi tr1

#!/bin/bash
while true
do
echo "over and over"
done

$ vi tr2
#!/bin/bash
rm -f file
if [ -f file]
	then 
    	true
    else
    	echo "file did not exist"
    	fi
    exit 0

$ ./tr2
file did not exist

# type

 

주어진 인자가 셸 스크립트, 함수, 엘리어스인지를 검사하여 출력한다.

기본적으로 함수이면 정의된 내용까지 모두 출력하고 명령어에도 검사 가능하다

 

# type [option] 인자
# -all : 지정한 인자 이름으로 정의된 모든 정보를 출력한다.
# -path : 실행 파일이나 셸 스크립트에 한정하여 관련 정보를 출력한다.
# -type : 인자 이름과 일치하는 키워드 하나만 출력한다

# pushd 가 함수로 설정되어 있는 경우에 관련 내용도 함께 출력한다.
$ type pushd
pushd is a function
pushd()
{
	dirname=$1;
    DIR_STACK="$dirname ${DIR_STACK:-$PWD}";
    cd ${dirname:?"missing directory name."};
    echo "$DIR_STACK"
}

# pushd로 정의된 모드 정보를 출력한다.
$ type -all pushd
pushd is a function
pushd()
{
	dirname=$1;
    DIR_STACK="$dirname ${DIR_STACK:-$PWD}";
    cd ${dirname:?"missing directory name."};
    echo "$DIR_STACK"
}
pushd is a shell builtin

# 형식만 출력하는데 우선순위가 높은 형식 하나만 출력한다.
$ type -type pushd
function

# 형식만 출력하는데 해당 이름으로 설정된 모든 형식을 출력한다.
$ type -all -type bushd
function
builtin
반응형