티스토리 뷰

Study/Language >

쉘 프로그래밍 이론

Jake Yoon 2009. 12. 24. 18:24

쉘 프로그래밍

  •  쉘 프로그래밍이란?

프롬프트에서 내리는 일련의 명령 수행 과정을 순차적으로 실행되도록 하는 배치(batch) 성격을 가지는 프로그램을 의미한다.

  • 장점

작성이 쉽다. 확장이 쉽다.  ex) 쉘 스크립트로 작성하기 복잡하면 perl이나 python으로 사용 권장.

  • 사용 사례

    /etc/init.d/* 에 있는 서비스 스크립트, X윈도우 실행 하는 startx

  • 쉘 스크립트 구조

    실행 쉘 선언부, 실행 코드 부분

01 | #! /bin/sh  

02 | SRC_DIR = $HOME

03 | echo "======Bourne shell script is very simple========="

04 | echo -n "Your home directory : "

05 | echo $SRC_DIR

1번줄에 #!으로 시작하는 부분은 실행 쉘 선언부, 2번줄 부터가 실행 스크립트 부분

#!로 시작하는 부분에는 스크립트 코드를  처리할 쉡의 절대 경로를 지정한다. ( 특별히 지정하지 않으면 /bin/sh가 사용)

cf) 테스트 하기 위해서는 위의 예제를 1. sample.sh로 저장, 2. chmod +x sample.sh로 실행권한 변경 3. ./sample.sh로 실행한다.

본 쉘은 POSIX(Portable Operation System Interface for Computer Environment) shell 이라고 부른다. 유닉스 진영에서 가장 많이 사용되었고, 가장 대표적인 쉘

but, 그 본래의 기능이 너무 미약하여 나중에는 이를 새롭게 다시 작성하여 bash(Born Again sh)를 내놓았습니다. 추가적인 기능으로 명령어 히스토리, 명령어 자동완성

 

  • 쉘 변수 :  일반 변수/환경 변수

    • 종류에 따른 분류  : 일반변수  , 환경 변수 (해당 쉘과 해당 쉘에서 실행되는 다른 프로그램들도 모두 상속받는 변수,다른 C나 여러 가지 다른 언어로 짜여진 프로그램과도 환경 설정값이나 여러가지 정보 전달에 이용), 환경 변수는 다른 프로그램을 실행하거나 할 때 중요하게 취급되며 기본적으로 쉘 환경에서는 중요한 몇몇 개의 변수가 환경변수로 미리 선언되어 있음.
    • 형에 따른 분류 : 일반 문자형, 정수형, 배열

cf ) 쉘 변수는 쉘에서 사용하는 어떤 값을 저장하는 변수로서, 선언이 따로 필요 없음. but, 드물게 변수만 생성하는 경우, 변수 타입을 미리 지정한다. (정수형이나 배열변수) 그 외엔 모두 쉘에서 사용되는 변수는 기본으로 문자열(string)으로 인식

ex) HOME, PWD, PATH, TERM, SHELL, USER, UID, LANG

 

변수 지정(선언) : [set] name=value , '=' 문자 앞뒤로 공백을 허용하지 않는다.

변수 호출 :  ${name}, 괄호 생략 가능

변수 삭제 : unset name, name에 해당하는 변수를 해제(제거), 함수도 제거 가능

변수 범위 : 쉘에서 사용되는 변수들은 글로벌 변수이다. 하지만 재귀 함수 같은 경우 특정 코드 블록에서만 사용 가능 하도록 하기 때문에 local을 붙이면 해당 코드 블록에서만 사용가능

 

cf) 외부의 유틸리티가 내부에서 들어와서 쓰이느 경우가 쉘에는 매우 많다고 함.

  • 조건문 ( if,case)

if문 : 실제 구문 문법은 test라는 유틸리트 구문을 사용, []를 사용

test를 사용하는 경우는 그 구문의 성공 여부를 검사하기 위해서 '$?'를 검사해볼 수 있다. 0이면 참, 그 이외의 경우에는 거짓

단 대체 시에는 연산자 앞뒤로 공백이 하나씩 있어야 한다. 공백이 없을 시에는 문법 오류 발생.

문자열연산    A = B  : 문자열 A와 B는 같음(= 앞뒤에 공백이 꼭 있어야 함), A != B : 문자열 A와 B는 다름.

-zA : 문자열 A의 길이가 0이면 참(A가 선언되어 있어야함), -nA : 문자열 A의 길이가 0이 아니며 참(A가 선언되어 있어야 함)

산술 연산     A -eq B : (equal) A, B는 같음, A -ne B : (not eqaul) A, B는 다름

A -gt B : (greater than) A는 B보다 크다.   A -ge B : (greater equal) A는 B보다 크거나 같다.

A -lt B : (less than) A는 B보다 작다.       A -le B : (less equal) A는 B보다 작거나 같다.

결합 연산     A -a B : AND결합, &&로 대체 가능,   A -o B : OR 결합, ||로 대체 가능

단항 연산    !(A) : A의 결과를 부정함(A의 결과를 뒤집음).

ex) $test "Sun" = "Moon"; echo $?                  if문으로 바꾸면  if [ "Sun" = "Moon" ]; then

      1                                                                                실행할 코드 ...         

           $test "( -s pro1.c ) -a ( -r pro1.c )"; echo $?

0

테스트 구문       설명

-r file                해당 파일이 존재하고 읽기 가능하면 true리턴

-w file               해당 파일이 존재하고 쓰기 가능하면 true리턴

-x file               해당 파일이 존재하고 실행 가능하면 true리턴

-f file                해당 파일이 존재하고 일반 파일일 경우 true리턴

-d file               해당 파일이 존재하고 디렉토리일 경우 true리턴

-p file               해당 파일이 존재하고 파이프일 경우 true리턴

-s file               해당 파일이 존재하고 파일용량이 0보다 크면  true리턴

case문 : 다중상태 분기를 할수 있다.

case $test_var_in

value1)

'$test_var이 value1인 경우'

;;

value2)

'$test_var이 value2인 경우'

;;

 

*)

'default인 경우'

;;

esac

  • Loop Statement : while, for, util 구문

while 반복문

while [ test condition ]

do

commands...

done

 

01    while [ 1 ]; do

02          commands..

03   done

04   while : ; do

05         commands..

06   done

07   while [ $dday -ne 0 ]; do

08         commands..

09   done

for 반복문

for iterator in value1, value2 ...

do

commands...

done

 

for fname in * ; doto

echo "file : $fname"

done

for names in A B C ; do

echo "NAME : $names"

done

for i ; do

echo "arg : $i"

done

for i in seq 1 10 ; do

echo "seq : $i"

done

until 반복문

until [ test_condition ]

do

commands...

done

cf) testcondition이 참이면 루프를 멈추게 된다.

iterator = 10

until [ $iterator -lt 0 ]

do

echo $iterator

$iterator = expr $iterator - 1

done

select

select menu in [ lists... ]

do

title...

done

 

ps3 = "which color do you like ?(1,2,3..) >>"

select userinput in "Bule" "Red" "Yello" "Exit"

do

if [ $userinput = "Exit" ]; then

break

fi

echo "Your favorite color : $REPLY) $userinput"

done

echo "+ End of Question"

 

  • 쉘 변수의 확장

    인용 부호

" " - 문자열을 출력하되 묶여진 문자열 안에서 $기호를 만났을 때 변수로 인식하여 변수 값으로 교체함, Tab이나 New Line을 인식

' ' - 문자열을 출력하되 묶여진 문자열 안에서 $기호를 만났어도 변수가 아닌 그냥 $를 포함한 일체를 단순 문자로 출력

` ` - 명려어로 인식하여 묶여진 문자열을 명령어로서 실행되고, 변수에는 실행된 출력이 저장된다.

\ - 문자의 특수한 기능을 제거하여 일반 문자로 인식

var="your home directory is $HOME"

echo $var1

your home directory is /root

var='your home directory is $HOME'

echo $var1

your home directory is $HOME

var1=`ps`

echo $var1

ID TTY TIME CMD 24536 pts/0 .....

변수의 형(type) : 정수형 , 배열

일반적인 변수로서 저장된 값은 기본적으로 문자열로 인식

정수형 변수 값 지정 : type -i varname

    변수 값 호출 : $varname

     배열 변수 값 지정 : typeset -a varname=(var1 var2 var3 ...)

  declare - a varname=(var1 var2 var3...)

  varname[0] = "value1", varname[1] = "value2"

 값 호출 : ${varname[0]},${varname[*]},${varname[@]}

 값 삭제 : unset ${arr_var[2]}, unset ${arr_var[*]}

특별 변수

쉘 프로그램이 시작되거나 실행되는 도중에 그 값들이 정해지며, 내부적으로 자동으로 만들어지는 변수

$$ - 실행되는 쉘 프로그램의 PID값,   $! - 마지막으로 실행된 백그라운드 프로세스의 PID

$? - 최근 명령의 종료값 (0-ture, 0이외값 false), $# - 전달 인수의 개수

$0 - 명령 수행 시 명령의 이름을 기억하는 변수, $1 $2 $2 ... - 전달 인수 하나

$* - 명령에 전달된 전달 인수 전체를 목록으로 표시,   #@ - 전달 인수를 문자열의 목록으로 표시

for arg_list in $* ;do

echo "parameter : $arg_list"

done

확장된 변수 치환법

해방 변수의 값이 없을 경우에 default 값을 지정하거나 혹은 그 외에 변수의 길이등을 지정할 수 있다.

${varname:=value} - 변수 varname이 null이면 value로 할당하여 저장하고,  아니면 그냥 기존의 값으로 치환

${varname:-value} - 변수 varname이 null이면 value를 반환(저장하지 않음)

${varname:=value} - 변수 varname이 null 아니면 value를 반환

${varname:?value} - 변수 varname이 null이면 에러내면서 value의 값을 화면에 출력(저장하지 않는다)

${#varname} - varname의 문자열 길이를 의미

${#arrayname[*]} - 배열 안에 요소의 수를 나타낸다.

${varname:offset:len} - 변수 varname의 문자열을 offset 위치부터 len만큼의 길이를 잘라냄

괄호 문자 : (),[],{}

명령어 치환, 수치 연산, 배열 변수 , 공백의 유무를 확실하게 파악

(varname) -  varname에 해당하는 문자열을 명령어로 인식

((varname)) - varname에 해당하는 문자열을 수치 연산으로 인식

{....} - 문자열 치환으로 사용

[....] - 배열 변수의 인덱스에 사용

{..;...;}  - 괄호가 존재하는 경우 괄호 안은 명령어로 인식된다. 현재 쉘에서 실행

방향 재지정(리다이렉션)

파일 기술자(file descriptor)들 중에서 0부터 2까지 자동 생성(0: 표준입력(stdin) , 1:표준출력(stdout),  2:표준 에러(stderr))

리다이렉션은 이 입력이나 출력, 혹은 파일들의 입출력 방향을 다른곳으로 돌려주기 위한 것

> -  표준 출력을 파일로,   >> - 표준 출력을 기존의 파일에 덧붙이거나 없으면 새 파일로 한다.

< - 표준 입력을 파일에서 ,   1> - 표준 출력을 파일로

2> - 표준 에러를 파일로,      >&2 - 결과를 표준에러로 리다이렉션

a | b - 파이프,  a의 표준 출력은 b의 표준 입력으로 연결됨

HERE 문서 사용 - 방향 재지정을 이용해서 데이터를 현재 쉘로 리다이렉션하는 기법

  • 쉘 스크립트의 재사용 : 모듈화/함수

    my_env.sh 내용      MY_NAME="Steven Kwon"

    (쉘 스크립트 모듈)   PATH ="$PATH:."

    export PATH

    function print_myname(){ ... 생략 ...

    }

my_script.sh 내용   #!/bin/sh

    .~/sh_mod/my_env.sh

   echo "My NAME : $MY_NAME"

cf) 마침표를 이용해서 my_env.sh 파일을 삽입해 오고 있다.

기본적으로 리눅스나 유닉스의 경우에 /etc/rc.d 디렉토리나 /etc/init.d 디렉토리에 있는 스크립트만 봐도 각종 함수로 작성되어 있는것을 확인 할 수 있다.

 

함수의 작성 : 함수 선언은 아래와 같이 하며, 작성시에는 ()있지만, 호출시에는 괄호가 없다.

선언    [function] func_name(){

commad....

return #

}

사용    func_name arg1 arg2 ...

 shift 명령 - 인수 리스트(argument list)를 다시 매핑할 수 있다.

 function hello(){

while : ;

do

if [ "x$1" = "x" -o "x$2" = "x" ];then

return

fi

echo "name ($1) age($2)" ;shift 2

done

}

hello naseuk 27 naseuk2 28

 

  • 시그널 핸들링

trap (command or function) (SIGSPEC....)

SIGSPEC에는 트랙이 잡아야 할 시그널들을 적는다.

  • 스크립트 디버깅

디버깅을 위한 옵션 -v , -x

-v : 스크립트 실행 흐름을 알수 있도록 스크립트가 실행되는 소스라인의 명령행을 표시

-x : 명령행의 실행 후 값과 결과를 출력하여 소스가 아닌 변수의 교체가 일어난 뒤에 실행되는 명령행을 출력합니다.

  •  쉘에서 사용된느 외부/내부 명령들

정렬 및 출력 : sort, uniq, head, tail, expand, paste, tee

실행 및 검사 : test, sum, wc, stat, exec, eval

평가 및 포매팅 : expr, let, echo, printf

데이터 변경 : join, split, cut, tr, fold, seq, read ...

작업 컨트롤 : fg, bg, jobs, wait

외부 계산기 : bc

기타 : date, time, sleep, usleep, getopt

 

sort : 알파벳순으로 라인 단위 정렬,    uniq : 상하 근접한 1개의 인에 중복되는 라인 제거

tee : 표준 입력으로 들어오는 데이터를 파일로 저장하고 다시 표준 출력(즉 화면에 내용을 출력)

cf ) sort로 먼저 라인 단위 정렬 후, uniq를 많이 사용하며, 일반적으로 sort msg.txt | uniq |tee msg2.txt 사용한다.

 head, tail : 해당 파일의 앞부분/ 뒷부분을 일정 라인만큼 출력해주는데 사용 cf)로그 파일 감시 할경우 tail -f

test : 파일의 유무나 디렉토리, 권한 등의 여러 가지 검사를 하는 방법

sum : 파일의 체크섬을 구해주는 것으로서 간단하게 쉘에서 파일의 무결성 테스트에 사용

wc : 단어나 라인 수를 구해준다. stat : 파일 시스템이나 파일에 대한 정보를 얻어 올 수 있다.

exec : 뒤에 나오는 명령어를 현재 쉘에서 실행

expr : 문자열을 기반으로 한 수식 계산이나 정규 표현식을 평가할 수 있는 프로그램(+,-,/,%)

   비교값을 (>,<,>=,<=,=,!=)등의 연산 기호로 반환, 리턴값이 1이면 참, 0이면 거짓

   expr ARG1 + ARG2  -  ARG1과 ARG2의 핪을 구한다. 사칙연산 기호(+,-,%,/) 사용 가능, 한번에 한 개의 수식만 가능

   expr STR : REGEX  - 문자열 STR에 패턴 REGEX를 매치시킨다.

join : 두 파일의 데이터를 합칠 수 있다. 횡으로 결합한다.

split : 특정 바이트경계로 여러 개의 파일로 잘라주게 된다. 입력은 파일로 입력받거나 표준 입력으로 받는다. 보통 큰 파일을 여러 개로 나눌때 사용

cut : 라인의 특정 부분을 떼어내어 사용하는데 주로 사용, 이는 바이트,필드 단위 가능

tr : 문자 단위로 변경이 되므로 단어 단위의 변경을 원하는 경우

 

 

#!/bin/bash
# 몇몇 시스템에서는 #!/bin/bash2 로 바꿔줘야 될지도 모릅니다.
#
# 무작위로 패스워드 만들어 내기는 Antek Sawicki <tenox@tenox.tc> 가
# 이 HOWTO에서 쓸 수 있도록 인심좋게 허락해 주었습니다.
#
# ==> HOWTO 저자에 의한 주석 ==>


MATRIX="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
LENGTH="8"
# ==> 더 긴 패스워드를 원한다면 'LENGTH'를 늘리세요.


while [ ${n:=1} -le $LENGTH ]
# ==> := 가 "기본 바꾸기" 연산자였던거 기억하시죠?
# ==> 그래서, 만약에 'n'이 초기화 안 됐다면 1로 세트됩니다.
do
 PASS="$PASS${MATRIX:$(($RANDOM%${#MATRIX})):1}"
 # ==> 아주 훌륭한데 약간 헷갈리네요.

 # ==> 제일 안쪽부터 살펴 봅시다...
 # ==> ${#MATRIX} 는 MATRIX 배열의 길이를 리턴합니다.
 # ==> $RANDOM%${#MATRIX} 는 1부터 MATRIX 길이까지 숫자 중에서 무작위 숫자를 리턴합니다.

 # ==> ${MATRIX:$(($RANDOM%${#MATRIX})):1}
 # ==> 는 MATRIX 임의의 위치에서 길이가 1인 문자열을 리턴합니다.
 # ==> 3.3.1절의 {var:pos:len} 위치 바꾸기와 예제들을 참고하세요.

 # ==> PASS=... 결과를 바로 전 PASS에 덧붙입니다.

 # ==> 이 과정을 좀 더 확실하게 보고 싶다면 다음 줄의 주석을 풀어 보세요.
 # ==>   echo "$PASS"
 # ==> 루프를 한 번 돌때마다 PASS가 한 글자씩 만들어지는 것을 볼 수 있습니다.

 let n+=1
 # ==> 다음을 위해서 'n'을 증가
done

echo "$PASS"
#== 필요하다면 파일로 재지향.
 

#!/bin/bash

exec < $1

while read line

do  

echo $line

done

 

#!/bin/bash
declare -i maxLength=0
declare -i tempLength
declare -i x

for first in "$@"; do
  let tempLength=${#first}

  if [ $first != $1 ] && [ $maxLength -lt $tempLength ]; then
    maxLength=tempLength
  fi
done

for tryfile in "$@"; do
  if [ $tryfile != $1 ]; then
    echo -n "$tryfile"
  
    let tempLength=${#tryfile}
    tempLength=maxLength-tempLength
  
    for x in `seq 0 $tempLength`; do
      echo -n " "
    done
  
    grep -c $1 $tryfile
  fi
done
 

출처 : Tong - kangdydtjs님의 Linux통


댓글