UNIX 및실습 13 장. 콘쉘프로그래밍 1
학습목표 다양한쉘변수를이해하고활용하는방법을익힌다. 스크립트파일안에서사용자입력을받아처리하는방법을익힌다. 산술, 비교, 논리, 비트연산자와문자열테스트, 파일테스트를활용하는방법을익힌다. 조건문과반복문의사용방법을익힌다. 함수를이용해스크립트를작성하는방법을익힌다. 스크립트의실행오류를찾아서수정하는방법을익힌다. 2
Section 01 쉘스크립트 스크립트? 인터프리터라불리는다른프로그램에의해실행되는프로그램 Perl, 자바스크립트, Tcl/Tk, 쉘스크립트 쉘이실행하는프로그램 유닉스명령 + 쉘이제공하는프로그램구성요소 쉘스크립트파일이름은키워드나앨리어스, 내장명령과같은이름을쓰지않는것이바람직함 예 : test_script #!/bin/ksh # My First Script Program print I love UNIX! pwd 3
쉘스크립트실행하기 스크립트실행방법 쉘을실행하면서인자로스크립트이름지정 telnet hanbitbook.co.kr $ ksh test_script I love UNIX! /export/home/user1/unix/ch13 $ 파일을직접실행 telnet hanbitbook.co.kr $ chmod +x test_script $ test_script I love UNIX! /export/home/user1/unix/ch13 $ 4
쉘스크립트종료하기 - exit 스크립트의종료 파일의마지막명령을실행 exit 실행 종료상태 $? 변수에저장 exit [ 종료상태 ] 예 : test_exit #!/bin/ksh #test_exit : exit and $? # Test Script exit 20 telnet hanbitbook.co.kr $ test_exit $ print $? 20 $ 5
쉘스크립트파일내용 - #! 스크립트를실행할프로그램지정 파일의가장처음에위치 예 : test_sharp #!/bin/more # test_sharp: 스스로를출력하는스크립트 # 이스크립트를시행시키면자기자신을화면에출력합니다. # 주석문도모두출력되지요. print 이문장도출력됩니다. 예 : test_sharp2 #!/bin/rm # test_sharp2: 자기자신을지우는스크립트 # 이스크립트를실행시키면이파일이지워집니다. WHATEVER=65 print 이부분은절대로출력되지않을겁니다. exit $WHATEVER # exit 문도실행되지않지요. 6
쉘스크립트파일내용 - 주석 주석은 # 으로시작 프로그램의설명 행전체또는행의일부를주석으로처리 예 : test_sharp2 #!/bin/rm # test_sharp2: 자기자신을지우는스크립트 # 이스크립트를실행시키면이파일이지워집니다. WHATEVER=65 print 이부분은절대로출력되지않을겁니다. exit $WHATEVER # exit 문도실행되지않지요. 7
쉘스크립트파일내용 쉘명령어 쉘이실행할수있는모든명령어사용가능 여러명령을반복수행해야할때파일로작성하여실행 예 : find_script #!/bin/ksh # find_script : /bin, /usr/bin 에있는쉘스크립트검색 cd /bin file * grep 스크립트 cd /usr/bin file * grep 스크립트 실행결과 : telnet hanbitbook.co.kr $ find_script alias: 실행할수있는 /bin/ksh 스크립트 appletviewer: 실행할수있는 /bin/ksh 스크립트 arch: 실행할수있는 /usr/bin/sh 스크립트 basename: 실행할수있는 /usr/bin/sh 스크립트 8
쉘스크립트파일내용 쉘 프로그램 각쉘이제공하는프로그램을위한구문 쉘변수, 인자처리, 각종연산자, 제어문등포함 telnet hanbitbook.co.kr $ cat -n /etc/init.d/nfs.server more 1 #!/sbin/sh 2 # ( 중략 ) 12 case "$1" in 13 'start') 14 # Clean up old /etc/dfs/sharetab remove all nfs entries 15 16 if [ -f /etc/dfs/sharetab ] ; then 17 /usr/bin/awk '$3!= "nfs"' /etc/dfs/sharetab \ 18 /usr/bin/sed -e 's/ / /' >/tmp/sharetab.$$ ( 중략 ) -- 계속 --(26%) 9
Section 02 쉘변수사용하기 변수값활용하기 형식 의미 $name $(name) ${name-word} ${name+word} ${name=word} ${name?word} name 의값으로대체 name 의값으로대치. 변수이름이다른구문과인접해있을때사용 name 이정의되어있으면그값을, 그렇지않으면 word 값사용 name 이정의되어있으면 word 값을사용 name 이정의되지않았으면 word 를대입하고그값사용 name 이정의되어있으면그값을사용하고그렇지않으면 word 출력후종료 10
사용예제 쉘변수사용하기 telnet hanbitbook.co.kr $ test=test $ print $test test $ print ${test} test $ print ${test-word} test $ print ${test1-word} word $ print ${test+word} test $ print ${test=word} test $ print ${test1=word} word $ print ${test?word} test $ print ${test2?word} ksh: test2: word $ 1) test 변수에 "test" 문자열저장 2) test 변수값출력 3) test 변수값출력 4) test 변수가정의되어있으므로그값을출 력 5) tset1 변수가없으므로문자열 word 출력 6) test 변수가정의되어있으므로 word 출력 7) test가정의되어있으므로그값을출력 8) test1 변수가없으므로 word를그값으로 저장 9) tset 변수가정의되어있으므로그값출력 10) tset2 변수가없으므로 word 출력후스크 립트종료 11
쉘변수문자열처리 변수의값이문자열일때문자열내패턴을찾아일부분을제거하는방법출력 표현식 ${vairable%pattern} ${variable%%pattern} ${variable#pattern} ${variable##pattern} 기능 variable 의뒤부터패턴과일치하는첫번째부분을찾아서제거 variable 값의뒤부터패턴과일치하는가장큰부분을찾아서제거 variable 값의앞부터패턴과일치하는첫번째부분을찾아서제거 variable 값의앞부터패턴과일치하는가장큰부분을찾아서제거 12
쉘변수문자열처리 사용법 % # telnet hanbitbook.co.kr $ path1"/usr/bin/local/bin" $ print ${path1%/bin} /usr/bin/local $ print ${path1%%/bin*} /usr $ telnet hanbitbook.co.kr $ path2="/export/home/user1/.profile" $ print ${path2#/export/home} /usr1/.profile $ print ${path2##*/}.profile $ 13
변수속성지정 - typeset 변수의속성 대소문자, 폭, 정렬방향, 읽기전용, 정수타입 명령 기능 typeset typeset x typeset a b c typeset r d typeset i num typeset u name typeset l name typeset L# name typeset R# name typeset z# name 모든변수출력 export 된모든변수출력지역변수 a b c 정의변수 d를읽기전용으로지정 num 변수에는정수만저장 name의값을대문자로변환하여저장 name의값을소문자로변환하여저장 name의값의처음 # 개문자만남기고삭제 name 값의마지막 # 개문자만남기고삭제 name을 # 개문자로구성된문자열을만들고끝에 null문자추가 14
명령행인자처리 명령행인자 스크립트를실행할때인자로주어진값 위치매개변수 명령행인자를저장하는스크립트변수 인자의위치에따라이름이정해짐 명령행인자 의미 $0 쉘스크립트의이름 $1 - $9 명령행에주어진첫번째부터 9번째까지인자 $(10) 10번째인자 $# 전체인자개수 $* 모든인자 $@ $* 과같은의미 $* $1 $2 $3 $@" $1 $2 $3 $? 최근실행된명령의종료값 15
예 : test_position #!/bin/rm # test_position: 명령행인자를테스트 print '$# : ' $# print $1 $2 $3 print '$* : ' $* print '$@ : ' $@ print '$? : ' $? 실행결과 : telnet hanbitbook.co.kr 명령행인자처리 $ test_position one two three $# : 3 one two three $* : one two three $@ : one two three $? : 0 $ 16
명령행인자처리 - shift shift [n] 위치매개변수의항목을지정한개수만큼왼쪽으로이동 개수를지정하지않으면하나씩이동 예 : test_shift #!/bin/ksh # test_shift: shift 명령테스트 print $* shift print $* shift print $* 17 telnet hanbitbook.co.kr $ test_shift 1 2 3 4 5 1 2 3 4 5 2 3 4 5 3 4 5 $
Section 03 사용자로부터입력받기 - read 쉘내장명령으로터미널이나파일로부터입력처리 사용형식 형식 read x read first last read x? Input x : 의미 표준입력에서한행을입력받아 x 에저장 표준입력에서한행을입력받아첫번째단어를 first 에저장하고나머지모두를 last 에저장 Input x : 문자열을표준오류로출력하고, 사용자의입력을받아서 x 에저장 18
예 : test_read 입력받기 - read #!/bin/ksh # 키보드입력처리를테스트하는스크립트 read x print The value of x is $x # 아무메시지없이사용자입력을기다림 # 사용자가임의의값을입력하면출력 read y?"input y : " print x is $x y is $y # Input y : 를출력한후입력기다림 # x, y 값출력 print -n "Please enter your name : " read first last print Hi $first # Please~ 문을출력. 줄안바꿈 # 첫단어는 first, 나머지는 last에저장 # first name만출력 19
입력처리 - here 문서 명령 << TERMINATOR 입력 TERMINATOR 표준입력을사용자로부터직접받아들이지않고자동처리 TERMINATOR 가입력될때까지기술된부분을키보드입력으로처리 예 : test_here #!/bin/ksh # here 문서테스트. user1 에게메일을보냄 mail user1 << END This is a test mail for here document END 20
Section 04 연산자 프로그램에서자료를처리하는방법 산술연산자, 비교연산자, 논리연산자, 비트연산자제공 수치연산자사용시 let 또는 (( )) 사용해야함 연산자 의미 사용예 - 음수 ( 단항연산 ) -5! 논리부정 (not) ((! x < y )) ~ 비트반전 (not) ~y * / % 곱셈, 나눗셈, 나머지연산 let y=3 * 5 + - 덧셈, 뺄셈 let x=x+1 << >> 비트왼쪽시프트, 비트오른쪽시프트 (( y = x << 3 )) <= >= < > ==!= 비교연산 (( x < y )) & ^ 비트 AND, XOR, OR 연산 let z = x ^ y && 논리 AND, OR (( x<y x==3 )) = 변수값지정 let z=1 *= /= %= += == <<= >>= &= ^= = 단축연산 let z+=1 21
연산자 사용방법 telnet hanbitbook.co.kr $ a=5 $ print $a 5 $ let a = 20 ----> let 에서공백을사용못함 ksh: =: 할당하려면 lvalue 가필요합니다 $ let a = 20 ----> 공백을포함하려면 " " 사용해야함 $ print $a 20 $ (( a = 30 )) ----> (( )) 에서는공백사용가능 $ print $a 30 $ a=$a*5 ----> let 이나 (( )) 을사용하지않으면문자열로처리 $ print $a 30*5 $ print $((5*6)) ----> 계산결과를바로출력할수있음 30 $ print $((! 2 + 3 * 4 )) ----> 우선순위에따라! 먼저수행. 2 는 0 이됨. 12 $ print $(( 2 << 1 )) ----> 왼쪽 shift 는 *2 와같음. 2 번 shift 는 *4 4 $ print $(( 3 ^ 5 )) ----> XOR 연산결과 6 $ 22
Section 05 제어문 프로그램내의문장실행순서를제어하는것 선택적실행문 프로그램실행문을조건에따라선택적으로실행 if, select 반복실행문 프로그램실행문을정해진횟수나조건에따라반복실행 while, do, for 순차적수행선택적수행반복수행 문장 1 문장 2 조건 No(False) 문장 1 Yes(True) 문장 2 Yes(True) 조건문장 1 No(False) 문장 2 23
선택적실행문 - if~then~else 주어진조건의참, 거짓여부에따라명령실행 예 : test_if if 조건명령 then 명령 [ else 명령 ] fi 엄밀히말하면조건명령을실행하여그종료값이 0 이면 then 다음의명령을실행하고, 0 이아닌값이면 else 다음의명령을실행한다. #!/bin/ksh # test_if: if 문을테스트하는스크립트 read x?"input x : " read y?"input y : " # x 값을입력받음 # y 값을입력받음 if (( x < y )) then print x is less than y $x $y else print y is less than x $y $x fi 24
선택적실행문 - if~then~elif~else 조건이실패일때새로운분기명령실행 예 : test_elif if 조건명령 1 then 명령 elif 조건명령 2 명령 else 명령 fi #!/bin/ksh # test_elif: if-elif 문테스트 read score?" 점수를입력하세요 : " if (( $score > 90 )) then print 훌륭합니다. elif (( $score >= 80 )) then print 잘하셨네요 else print 좀더열심히하세요 fi 25
조건테스트 - 문자열연산자 조건명령에사용하는문자열연산자 내장명령 [[ ]] 사용 문자열연산자 string = pattern string == pattern string!= pattern string1 < string2 동작 string이 pattern과일치. = 연산자양쪽에공백 string이 pattern과일치하지않음 string1이 string2보다사전에서먼저나오면참특수기호의경우아스키코드값으로비교 -z string string 의길이가 0 -n string string 의길이가 0 이아님 26
조건테스트 - 문자열연산자 예 : test_string #!/bin/ksh # test_string: 문자열테스트스크립트 read answer?"are you OK (y/n)? " # answer 변수에값저장 if [[ $answer = [Yy]* ]] # 문자열비교. y로시작하는문자열인가 then print Happy to hear it. # y로시작하면 else print That is too bad. # y로시작하지않으면 fi 27
조건테스트 - test 플래그 파일관련테스트 콘쉘전용플래그 test 플래그 기능 -a file 파일이존재 -e file 파일이존재 -L file 심볼릭링크파일 -O file 사용자가 file의소유자 -F file 사용자의그룹 ID가파일의그룹 ID와같음 -S file 소켓파일 28
조건테스트 - test 플래그 본쉘과공통사용플래그 test 플래그 기능 -r file 읽기가능 -w file 쓰기가능 -x file 실행가능 -f file 일반파일 -d file 디렉토리파일 -b file 블록장치특수파일 -c file 문자장치특수파일 -p file 파이프파일 -u file setuid 권한부여파일 -g file setgid 권한부여파일 -k file sticky bit 접근권한부여파일 -s file 파일의크기가 0 이아님 29
조건테스트 - test 플래그 예 : test_file #!/bin/ksh # test_file: 파일연산자테스트 read file?" 파일이름을입력하세요 : " if [[! -a $file ]] then print 해당파일이존재하지않습니다. 파일이름을다시확인하세요. elif [[ -f $file ]] then print 일반파일입니다. elif [[ -d $file ]] then print 디렉토리파일입니다. else print 특수파일입니다. fi telnet hanbitbook.co.kr $ test_file 파일이름을입력하세요 : /dev/null 특수파일입니다 $ test_file 파일이름을입력하세요 : /etc/passwd 일반파일입니다 $ test_file 파일이름을입력하세요 :. 디렉토리파일입니다 $ 30
선택적실행문 - case 문 주어진변수의값에따라실행할명령따로지정 변수의값이 value1 이면 value1부터 ;; 을만날때까지명령실행 값의지정에특수기호, (or연산자) 사용가능 일치하는값이없으면기본값인 * 부터실행 case 변수 in value1) 명령 ;; value2) 명령 ;; *) 명령 ;; esac 31
선택적실행문 - case 문 예 : test_case #!/bin/ksh # test_case: case 테스트스크립트 read cmd?" 명령을선택하세요 : " case $cmd in [0-9]) # 0부터 9까지임의의숫자 date ;; [aa-c]*) # 소문자 a, 대문자 A,B,C로시작하는임의의문자열 pwd ;; "cd" "CD") # cd 또는 CD print $HOME ;; *) print Usage : 명령을선택하세요 ;; esac 32
반복실행문 - for 리스트안의각값들에대해지정한명령을순차실행 for 변수 in list do 명령 done Tip] list 대신 $(< file) 을사용하면외부파일의내용을입력으로받아서처리! 예 : test_for #!/bin/ksh # test_for: for 테스트스크립트 for num in 0 1 2 do print number is $num done telnet hanbitbook.co.kr $ test_for number is 0 number is 1 number is 2 $ 33
외부파일내용참조 예 : test_for2 반복실행문 - for #!/bin/ksh # test_for2: 외부파일입력처리 for person in $(< list) # `cat list`와동일 do mail $person < letter # 현재디렉토리에있는파일 print ${person} 에게메일을보냈습니다. done print 모든메일을보냈습니다. 34
반복실행문 - for 명령행인자처리가능 예 : test_for3 #!/bin/ksh # test_for3: 명령행인자처리 for person in $* do mail $person < letter print ${person} 에게메일을보냈습니다. done print 모든메일을보냈습니다. 35
반복실행문 - while 조건명령이정상실행되는동안명령반복 while 조건명령 do done 명령 예 : test_while #!/bin/ksh # test_while: while 을이용해 1 부터 10 까지을구하는스크립트 count=1 sum=0 while (( count <= 10 )) do (( sum += count )) let count+=1 done print 1 부터 10 까지의합 : $sum telnet hanbitbook.co.kr $ test _while 1 부터 10 까지의합 : 55 $ 36
반복실행문 while case 결합예 #!/bin/ksh # test_while2: while 을이용해 pwd, date, 명령실행, quit 입력시종료 go=1 # while 종료를알리는변수 while (( go )) do print n 명령을입력하세요 \( pwd, date, quit \) : read cmd case $cmd in pwd) pwd ;; date) date ;; quit) go=0 ;; *) esac done # 반복종료 print 잘못입력하셨습니다. print pwd, date, quit 중에서선택하세요. ;; 37
반복실행문 while shift 결합예 #!/bin/ksh # test_while3: 명령행인자처리 while (( $# > 0 )) do print $1 shift # 명령행인자가남아있으면 # 각명령행인자에대해처리할작업기술 done 38
반복실행문 - until 조건명령이정상실행될때까지명령반복 예 : test_until until 조건명령 do done 명령 #!/bin/ksh # test_until: 지정한사용자가로그인하면알리는스크립트 read person?" 로그인이름 :" # 유저이름을 person 에저장 until who grep $person # > /dev/null do sleep 5 # 유저가접속중이아니면 5초쉼 done print "\007" # beep. 삑소리를냄 39
반복실행문 - select 메뉴를생성할수있는반복실행문 list 에지정한항복을선택가능한메뉴로만들어화면에출력 각항목에자동부여된번호선택가능 사용자입력은 select 와 in 사이에지정된변수에저장 보통 case 문과결합하여입력값처리 select 변수 in list do 명령 done 40
반복실행문 - select 예 : test_select telnet hanbitbook.co.kr $ test _select 1) pwd 2) date 3) quit 명령을입력하세요 : 1 /export/home/user1/unix/ch13 1) pwd 2) date 3) quit 4) 명령을입력하세요 : 3 $ #!/bin/ksh # test_select: 사용자입력에따라 pwd,date 명령실행. PS3=" 명령을입력하세요 : " select cmd in pwd date quit do case $cmd in pwd) pwd ;; date) date ;; quit) break ;; *) print 잘못입력하셨습니다. 번호를선택하세요. ;; esac REPLY= done 41
continue 루프안에서사용 이후실행순서를무시하고루프의처음으로돌아가는명령 예 : test_cont #!/bin/ksh # test_cont: continue 테스트 for person in $(< list) # `cat list` 와동일 do if [[ $person == sjyoun ]] then continue fi mail $person < letter print ${person} 에게메일을보냈습니다. done print 모든메일을보냈습니다. 42
실습과제 사용자로부터점수를입력받아등급을출력하는일을반복하는스크립트작성 A+, A, B+, B, C+, C, D+, D, F 처리 잘못된점수는오류메시지출력 계속할것인지여부를물어서 y 또는 Y 인경우계속하고, 아니면그동안정상적으로처리한점수들수와평균값출력후종료 임의개수의명령행인자들로, 수행할명령들을받아들여서차례대로실행하고그결과값이 0 이면 정상종료, 그렇지않으면 비정상종료 라는메시지를출력하는스크립트작성 실행예 : ex2 pwd ls who w select 를이용하여프로그래머가정한 5 개이상의명령중하나를선택하여수행하는스크립트작성 ( 명령중하나는반복문을빠져나오는 break 이어야함 ) 43
Section 06 함수 함수 : 하나의목적으로사용되는명령들의집합 앨리어스와의차이점 function 함수이름 조건에따라처리가능 인자처리가능 { 명령들 예 : trash } telnet hanbitbook.co.kr $ mkdir ~/.TRASH $ function trash { > mv $* ~/.TRASH > } $ 44
정의된함수확인 telnet hanbitbook.co.kr $ functions $ function trash { mv $* ~/.TRASH } $ 함수호출 telnet hanbitbook.co.kr $ touch a b c $ ls a b c $ trash a b c $ ls $ ls ~/.TRASH a b c $ 함수호출 45
함수종료조건 함수의종료 - return 함수본문안의마지막문장실행 return 문실행 return [n] 지정한값이함수의종료값으로 $? 에저장됨 46
예 : add 함수의종료 - return #!/bin/ksh # 함수리턴값테스트 function sum { typeset sum } (( sum= $1 + $2 )) return $sum sum $1 $2 print $1 + $2 = $? telnet hanbitbook.co.kr $ add 3 4 3 + 4 = 7 $ 47
함수의삭제 - unset unset -f 함수명 정의된함수를삭제 사용법 telnet hanbitbook.co.kr $ unset -f trash $ functions $ 48
Section 07 디버깅 스크립트작성도중발생한오류수정방법 구문오류 쉘이실행도중구문오류가발생한라인번호출력 실행오류 오류메시지없이실행이안되거나비정상종료 오류수정방법 ksh -x, trap 49
디버깅 : ksh -x 가장간단한스크립트실행오류수정방법 스크립트의각행이실행될때마다화면에출력됨 telnet hanbitbook.co.kr $ ksh -x test_while + alias l=ls -l + count=1 + sum=0 + (( count <= 10 )) + (( sum+=count )) + let count+=1 + (( count <= 10 )) + (( sum+=count )) + let count+=1 + (( count <= 10 )) ( 중략 ) + print 1 부터 10 까지의합 : 55 1 부터 10 까지의합 : 55 $ 50 스크립트의내용
디버깅 : trap 이용 trap 명령시그널 지정한시그널이스크립트로전달될때마다지정한명령실행 스크립트의명령이한줄씩실행될때마다 DEBUG 시그널이스크립트로전달됨 DEBUG 시그널을받을때마다원하는변수값출력가능 스크립트가실행되는도중변수값확인 51
예 : test_trap 디버깅 : trap 이용 #!/bin/ksh # test_trap: trap 테스트스크립트 Trap print $LINENO : count=$count DEBUG count=1 sum=0 while (( count <= 10 )) do (( sum += count )) let count+=1 done print 1 부터 10 까지의합 : $sum 52
실습과제 두수중에서더큰값을돌려주는 max2 함수작성 세수중에서가장큰값을돌려주는 max3 함수작성 명령줄에서입력을받아들여정수가 2 개면 max2 를, 3 개면 max3 를이용하여가장큰값을돌려주는스크립트작성 사용자로부터희망 id, 이름, 학번, 원하는쉘을받아들여파일 (add_user.req) 에추가하는스크립트작성 저장예 abc: 20121234 홍길동 :/usr/ksh 53