주석문
'#'로 시작하는 줄.
줄의 중간에 '#'이 와도 주석으로 인식하지만 다음과 같이 이상하게 동작하는
경우가 발생한다. >
실제 원하는 출력은 "abcdkkk"이지만 실제 출력은 "abcd kkk"가 되어 버린다.
즉, abcd뒤쪽의 공백문자가 포함되어 버린다.
따라서 줄의 중간에 '#'을 넣어서 주석을 만들지는 말것.
실제 test를 해보니까 그렇지 않는 것 같다(bash-2.05b).
Shell Script 실행하기
위에서 1, 2번 그리고 3,4번은 실행이 다르다. 1,2번은 기존에 존재하던 shell에서
스크립트를 수행하고 3,4번은 새로운 셀을 생성해서 실행하게 된다. 이 경우에
문제가 되는것은 스크립트 내부에서 "exit 0"과 같은 명령을 사용하면 1, 2번의
경우는 현재 shell이 닫혀버린다.
그리고 또하나의 문제는 1,2번의 경우는 현재 shell에서 이어져서 수행되므로
스크립트 내부에서 사용한 변수나 함수들이 그대로 남아있다. 하지만 3,4번의
경우는 현재 셀의 서브셀로 수행되므로 스크립트가 종료된후에는 스크립트내부의
변수나 함수는 현재 셀에서 유효하지 않게 된다.
따라서 수행하는 스크립트의 변수나 함수를 현재 셀에서도 유효하게 할때는
". sample" 나 "source sample"를 사용하고 그냥 스크립트를 수행하고 종료시킬때는
"bash sample" 나 "./sample"를 사용
매직넘버
리눅스에서는 기본 셀인 배쉬이외에도 다양한 종류의 셀을 사용할 수 있다.
따라서 배쉬용 셀스크립트가 씨셀에서 실행된다면 오류가 발생할 수 있다.
따라서 스크립트 파일의 처음 행에 파일을 실행시키는데 사용할 셀의 절대
경로를 아래와 같이 표시하게 된다.
이 매직넘버는 수행을 의미하므로 다음과 같이 사용할 수도 있다. 다음은 자기
자신을 출력하는 shell script이다.
이 파일은 스스로를 출력하는 셀스크립트이다.
셀스크립트 확인하기
결과는 ASCII가 아니라 C 스크립트 파일로 나타난다.
변수의 사용
$A A라는 변수
이렇게 사용하는 것은 지양하도록 한다. 만약 A라는 변수가 정의되지
않았을때 완전히 공백이 되어 버리기 때문에 다음과 같은 경우에
에러가 발생한다.
${A} A라는 변수
"$A" A라는 변수
'$A' '$A'라는 스트링
\$A '$A'라는 스트링
`$A` A라는 변수명의 명령의 실행.
`AA` AA라는 명령의 수행.(이식성이 강한 오래된 관습)
다음과 같이 사용할 경우
BB는 다음과 같이 확장된다.
따라서 다음과 같이 사용하는 방법을 고려해 볼 것.
AA는 명령이 수행된후 곧바로 확장되게 된다.
$(AA) AA라는 명령의 수행.
$(shell AA) AA라는 명령의 수행.
변수에 값을 대입하기
<case1>과 <case2>는 차이가 없어 보이지만 엄청난 차이가 존재한다. shell은 위
명령을 수행하면서 <case1>의 경우는 'AA'라는 명령을 수행하면서 그 파라메터로
'='와 "3333"을 대입하는 것으로 처리한다. 따라서 결과는 'AA'라는 명령이 없다고
출력된다.
<case2>의 경우는 shell이 'BB'라는 변수에 "3333"을 대입하는 것으로 정확하게
인식한다. 따라서 <case2>의 경우가 정확한 표현이다. 즉 '='의 양쪽을 띄어쓰지
않는다.
변수의 확장의 방지
조건 제어구문
조건의 Test
test or []
[]를 사용할때는 '['와 ']'와 조건사이에 빈칸을 삽입해야만 한다.
true와 false
셀스크립트의 조건문에서 참은 0을, 거짓은 0이 아닌값을 의미한다.
조건문의 논리를 단순화하기 위해 true를 대신하는 별칭으로 ':'를 종종
사용한다.
함수의 실행결과(반환값)을 조건문에 대입하기
잘못된 예1 >
잘못된 예2 >
맞는 예1 >
맞는 예2 >
맞는 예3 >
if문
테스트 조건 파일 옵션
만약 위 조건의 NOT일때는 "[ ! -f abcd.txt ]"가 된다.~
논리 연산 옵션
정수 비교 옵션
문자열 비교 옵션
문자열 비교시 주의할 점.
"[ $S1 = $S2 ]"와 같이 사용하면 $S1이나 $S2가 비어있다면 에러가
발생한다. 따라서 다음과 같은 방법을 사용한다.
"[ x$S1 = x$S2 ]" 혹은 [ "$S1"="$S2" ]
while 문
while 조건에 준하는 명령
do 실행할 명령어
done
case 선택 제어문
for 반복 제어문
for문에 유용한 seq 명령어
until 반복제어문
명령을 먼저 실행한 후에 조건을 검사한다.
and 리스트
or 리스트
문장블럭
쉘스크립트와 함수
쉘스크립를 명령어 라인에서 직접 실행하면 서브셀이라 불리는 셀의 복사가
기동되어 스크립트안의 처리는 서브셀에서 한다. 서브셀과 원래 셀의 현재
디렉토리는 독립되어 있으므로 스크립트안에서 아무리 혅 디렉토리를 변경해도 원래
쉘의 디렉토리에는 영향을 주지 않는다. 한편, 함수의 경우는 원래 쉘에서 그대로
실행되므로 현재 디렉토리의 변경에 효과적이다.
("source"나 "."로 쉘스크립트를 실행하면 현재 쉘에서 수행된다.)
방법 : 함수를 저장하고 있는 파일을 ". func.txt"라고 실행시키면 함수가 현재
쉘에 등록된다.
함수의 사용
함수를 사용할 때 주의 사항은 함수명과 뒤쪽의 '{'사이에는 반드시 공백이
존재해야 한다.
function이라는 단어는 생략이 가능하다.
함수에서 인자를 받기
함수의 반환값 이용하기
1. 숫자값을 반환하기.
2. 문자 스트링을 반환하기
: 명령
local 키워드
함수내부의 지역 변수를 선언할때 사용.
local var="aldfjas"
eval
인수를 평가하게 해준다.
exec
현재 셀을 다른 프로그램으로 대체한다.
exit n
스크립트의 반환값을 지정한다. 만약 종료상태를 지정하지 않고 종료한다면
마지막에 실행된 명령의 반환값으로 사용될 것이다.
printf
최신 셀에서만 유효하다.
set 명령
set 명령은 쉘을 위한 파마메터 변수를 설정한다. 이것은 빈칸으로 구분되는
값을 출력하는 명령에서 필드를 사용하는 유용한 방법이 될 수 있다.
shift
$2 -> $1
$3 -> $2
trap
시그널(signal)을 받아들일 때 수행하는 동작을 지정하는 데 사용된다.
[list]
trap `rm -f /tmp/my_tmp_file_$$` INT
--> INT(CTRL+C)가 발생하면 수행하는 명령.
trap - INT --> trap해제.
[list]
명령의 수행
산술 확장
echo $((1+1)
echo $(( 1 + 1 ))
echo $[1+1]
echo $[ 1 + 1 ]
echo `echo 1+1 | bc -l`
toggle 상태 만들기.
파라미터 변수
[list]
$1,$2,... 스크립트에 주어진 파라미터
"$9"보다 큰 매개변수는 "${10}"와 같이 "{}"로 감싸주어야 한다.
$* 환경변수 IFS의 첫문자로 구분되고, 하나의 변수에 저장되는 모든
파라미터의 목록, 만약 IFS가 NULL이면 모든 파라메터가 붙어서
나온다.
$@ IFS 환경변수를 사용하지 않는 $*에 대한 변형
"$*"와 "$@"중에서 "$@"를 사용하는 것을 권장한다.
echo `basename $0`
실행된 스크립트에 "./"가 붙어있는 경우에 떼어내고, 스크립트
이름만을 출력한다.
[list]
파라메터 확장
예제.
shell script에서 2진수 계산하기
Here Document
>>>> 동작은 cat으로 "!FUNKY!"사이의 글자를 전달하는 역할을 한다.
ed a_test_file << !FunkyStuff! --> ed editor Here document로 입력한다.
3
d
.,\$s/is/was/
w
q
!FunkyStuff! --> here document끝.
Scrip 디버깅
조건 제어관련 셀명령
exit와 return
exit는 현재 쉘스크립트를 종료하는 역할을 하고 return은 함수나 sourced
script(?)에서만 반환하는 경우에 사용한다.
배열의 사용
bash의 새 버전부터는 1차원 배열을 지원한다. variable[xx]처럼 선언할 수도 있고
'declare -a variable'처럼 직접적으로 지정해 줄 수도 있다. 배열 선언을
역참조하려면(내용을 알아내려면) ${variable[xx]}처럼 중괄호 표기법을 사용하면
된다.
재지향
파일 디스크립터
C style의 문자열의 해석
위 예제에서 볼 수 있듯이 $"" 안에 들어간 문자열은 C의 문자열에서와 같이
\t,\n,\r등이 해석된다.
따라서 다음과 같이 사용할 수 있다.
파일에 대해서 20~30행을 제거하는 명령이다.
예약된 종료코드
저자가 제안하는 방법은 사용자 정의 종료 코드를 64 - 113(성공시 0도
포함)으로 제한해서 C/C++ 표준을 따르는 것입니다. 이렇게 해도 사용자는
여전히 50개의 코드를 쓸 수 있는 있기 때문에 나중에 스크립트의 문제를 좀 더
깔끔하게 해결할 수 있습니다.
지역화(Localization)
지역화된 쉘스크립트는 시스템의 로케일에 따라 정의된 언어로 텍스트를
출력하게 해준다.
error string
: bad interpriter
이 에러는 쉘스크립트파일이 UNIX형식이 아닌 DOS 형식일때 이러한 에러가
발생한다. 주의할 것.
생각해 볼 문제
위 문장이 어떻게 동작할 것인가. 단순하게 생각할때는 package라는 디렉토리가
존재한다면 문장이 출력되지 않을 것이다. 그리고 package라는 디렉토리가 존재하지
않는다면 문장을 출력하고 스크립트를 종료하게 될 것이다. 하지만 문장을 출력하고
종료되지 않는다. '('와 ')'로 묶인곳이 실행되면서 현재 shell이 새롭게 하나
생성되어서 실행된다. 따라서 exit를 수행할때는 새롭게 생성된 shell이 종료되고
현재 shell은 종료되지 않는다. 따라서 exit로 종료되지 않고 계속 수행되게 된다.
따라서 다음과 같이 수정한다.
임시파일의 생성
'#'로 시작하는 줄.
줄의 중간에 '#'이 와도 주석으로 인식하지만 다음과 같이 이상하게 동작하는
경우가 발생한다. >
코드: |
#!/bin/sh AA=abcd # 여기는 주석. BB=${AA}kkk echo ${BB} |
실제 원하는 출력은 "abcdkkk"이지만 실제 출력은 "abcd kkk"가 되어 버린다.
즉, abcd뒤쪽의 공백문자가 포함되어 버린다.
따라서 줄의 중간에 '#'을 넣어서 주석을 만들지는 말것.
실제 test를 해보니까 그렇지 않는 것 같다(bash-2.05b).
Shell Script 실행하기
코드: |
1. $ source sample 2. $ . sample 3. $ bash sample 4. $ chmod +rx sample;./sample // #!/bin/bash 포함. 모드를 변경할때 rx를 주어야 한다. shell scripts는 shell에서 읽어서 수행해야 하므로 read 속성이 주어져야만 한다. |
위에서 1, 2번 그리고 3,4번은 실행이 다르다. 1,2번은 기존에 존재하던 shell에서
스크립트를 수행하고 3,4번은 새로운 셀을 생성해서 실행하게 된다. 이 경우에
문제가 되는것은 스크립트 내부에서 "exit 0"과 같은 명령을 사용하면 1, 2번의
경우는 현재 shell이 닫혀버린다.
그리고 또하나의 문제는 1,2번의 경우는 현재 shell에서 이어져서 수행되므로
스크립트 내부에서 사용한 변수나 함수들이 그대로 남아있다. 하지만 3,4번의
경우는 현재 셀의 서브셀로 수행되므로 스크립트가 종료된후에는 스크립트내부의
변수나 함수는 현재 셀에서 유효하지 않게 된다.
따라서 수행하는 스크립트의 변수나 함수를 현재 셀에서도 유효하게 할때는
". sample" 나 "source sample"를 사용하고 그냥 스크립트를 수행하고 종료시킬때는
"bash sample" 나 "./sample"를 사용
매직넘버
리눅스에서는 기본 셀인 배쉬이외에도 다양한 종류의 셀을 사용할 수 있다.
따라서 배쉬용 셀스크립트가 씨셀에서 실행된다면 오류가 발생할 수 있다.
따라서 스크립트 파일의 처음 행에 파일을 실행시키는데 사용할 셀의 절대
경로를 아래와 같이 표시하게 된다.
코드: |
#!/bin/bash |
이 매직넘버는 수행을 의미하므로 다음과 같이 사용할 수도 있다. 다음은 자기
자신을 출력하는 shell script이다.
코드: |
#!/bin/cat |
이 파일은 스스로를 출력하는 셀스크립트이다.
셀스크립트 확인하기
코드: |
$ file sample |
결과는 ASCII가 아니라 C 스크립트 파일로 나타난다.
변수의 사용
$A A라는 변수
이렇게 사용하는 것은 지양하도록 한다. 만약 A라는 변수가 정의되지
않았을때 완전히 공백이 되어 버리기 때문에 다음과 같은 경우에
에러가 발생한다.
코드: |
if [ $A = '1' ];then echo "A is 1" fi |
${A} A라는 변수
"$A" A라는 변수
'$A' '$A'라는 스트링
\$A '$A'라는 스트링
`$A` A라는 변수명의 명령의 실행.
`AA` AA라는 명령의 수행.(이식성이 강한 오래된 관습)
다음과 같이 사용할 경우
코드: |
AA=`AA` BB=$(AA)kkk |
BB는 다음과 같이 확장된다.
코드: |
BB=`AA`kkk |
따라서 다음과 같이 사용하는 방법을 고려해 볼 것.
코드: |
AA=$(shell AA) BB=$(AA)kkk |
AA는 명령이 수행된후 곧바로 확장되게 된다.
$(AA) AA라는 명령의 수행.
$(shell AA) AA라는 명령의 수행.
변수에 값을 대입하기
코드: |
<case1> AA = "3333" (X) <case2> BB="3333" (O) |
<case1>과 <case2>는 차이가 없어 보이지만 엄청난 차이가 존재한다. shell은 위
명령을 수행하면서 <case1>의 경우는 'AA'라는 명령을 수행하면서 그 파라메터로
'='와 "3333"을 대입하는 것으로 처리한다. 따라서 결과는 'AA'라는 명령이 없다고
출력된다.
<case2>의 경우는 shell이 'BB'라는 변수에 "3333"을 대입하는 것으로 정확하게
인식한다. 따라서 <case2>의 경우가 정확한 표현이다. 즉 '='의 양쪽을 띄어쓰지
않는다.
변수의 확장의 방지
코드: |
#!/bin/sh #출력이 0_tmp, 1_tmp라고나오길 기대하지만 shell은 i_tmp라는 변수가 #존재하지 않으므로 공백을 출력하게 된다. for i in 0 1 2 do echo $i_tmp done #정상적으로 된 경우. 변수를 확장하지 않으므로 0_tmp, 1_tmp, 2_tmp가 출력. for i in 0 1 2 do echo ${i}_tmp done #두번째 방법 for i in 0 1 2 do echo $i{_tmp} done |
조건 제어구문
코드: |
if, case, while, for, until if test 조건 then 명령어 fi if test; then fi if test 조건 then 명령어 else 조건을 만족하지 않았을때 실행할 명령어 fi if test 조건 then 명령 elif test 조건 then 명령 else 명령 fi 다음과 같은 에러메세지가 발생하는 이유는 다음과 같다. [: =: unary operator expected 비교하는 파라메터를 ""로 감싸주지 않았을 때 만약 파라메터가 NULL String이라면 다음과 같이 취금되어 버린다. if [ = "yes" ] 따라서 파라메터는 무조건 다음과 같이 ""로 감싸서 처리해주어야 한다. if [ "$timeofday" = "yes" ] if [ "" = "yes" ] |
조건의 Test
test or []
[]를 사용할때는 '['와 ']'와 조건사이에 빈칸을 삽입해야만 한다.
true와 false
셀스크립트의 조건문에서 참은 0을, 거짓은 0이 아닌값을 의미한다.
코드: |
# true # echo $? 0 # false # echo $? 1 if true; then fi if faluse; then fi |
조건문의 논리를 단순화하기 위해 true를 대신하는 별칭으로 ':'를 종종
사용한다.
코드: |
if :;then fi |
함수의 실행결과(반환값)을 조건문에 대입하기
잘못된 예1 >
코드: |
exe_command if [ $? ]; then # 0이 아닌 어떤 반환값에 대해서도 들어올 것 같은데 # 실제로는 모든 반환값에 대해서 이곳으로 들어온다. 따라서 잘못된 # 예제이다. echo return value is true. fi |
잘못된 예2 >
코드: |
RET=`exe_command` if [ RET = 0 ];then # RET에는 exe_command의 반환값이 아니라 stdout 출력값이 들어가게 된다. # 따라서 위 문장은 정확한 표현이 아니다. echo return value is true fi |
맞는 예1 >
코드: |
exe_command if [ $? -eq 0 ]; then echo return value is true. fi |
맞는 예2 >
코드: |
if exe_command; then echo return value is true. fi |
맞는 예3 >
코드: |
exe_command case $? in 0) echo "return zero";; 1) echo "return one";; *) echo "unknown return value";; esac |
if문
코드: |
if [ -f ~/.bashrc ]; then . ~/.bashrc fi if [ -f ~/.bashrc ] then . ~/.bashrc fi |
테스트 조건 파일 옵션
-b 디스크 드라이브와 같은 Block형 장치
-c 터미널, 프린터, 네트워크같이 Character형 장치
-d 디렉토리
-e 존재한다.(이식적이지 않으므로 -f를 사용한다.)
-f 일반파일
-k 스티키비트가 설정됨
-p 네임드 파이프의 형태
-r 읽기 권한이 허가된 상태
-s 사이즈가 0이 아님
-w 쓰기 권한이 허가된 상태
-x 실행 권한이 허가된 상태
-L 심벌릭 링크의 형태
-S 소켓의 형태
만약 위 조건의 NOT일때는 "[ ! -f abcd.txt ]"가 된다.~
논리 연산 옵션
-a 논리적 AND
-o 논리적 OR
! 논리적 NOT
정수 비교 옵션
-eq Equal(=)
-ge Greater then or Equal to(>=)
-gt Greater then(>)
-le Less then or Equal to(<=)
-lt Less then(<)
-ne Not Equal(!=)
문자열 비교 옵션
-n 문자열의 길이가 0이 아니다.
-z 문자열의 길이가 0이다.
= 두 문자열이 같다.
주의할점은 "=="와 혼동하지 말것.
!= 두 문자열이 같지 않다.
문자열 비교시 주의할 점.
"[ $S1 = $S2 ]"와 같이 사용하면 $S1이나 $S2가 비어있다면 에러가
발생한다. 따라서 다음과 같은 방법을 사용한다.
"[ x$S1 = x$S2 ]" 혹은 [ "$S1"="$S2" ]
while 문
while 조건에 준하는 명령
do 실행할 명령어
done
case 선택 제어문
코드: |
#!/bin/sh case "$FLAG" in +) exit;; --> '+'이면 종료. [aeiou] ) echo -e "you inputvowel\n" ;; --> aeiou이면 출력. *) echo -e "you input consonant\n" ;; --> 그이외. esac |
코드: |
#!/bin/sh echo "Is it morning? Please answer yes or no" read timeofday case "$timeofday" in yes | y | Yes | YES ) echo "Good Morning";; n* | N* | [nN][oO]) echo "Good Afternoon";; *) echo "Sorry, answer not recognized";; esac |
for 반복 제어문
코드: |
for TEMP in A B C D do echo $TEMP done |
코드: |
# /etc/passwd의 각 행에 대해서 수행. for LINE in `cat /etc/passwd` do echo $LINE >> backup echo $LINE done |
for문에 유용한 seq 명령어
코드: |
# seq 1 3 1 2 3 |
코드: |
#!/bin/sh for temp in `seq 1 3`; do echo $temp done |
until 반복제어문
명령을 먼저 실행한 후에 조건을 검사한다.
코드: |
until [ "`who | grep jarrett`"] do echo "hacker is NOT logged in" sleep 1 done echo -e "Notorious Hacker jarrett appeared at \a" `date` |
and 리스트
코드: |
statements1 && statements2 && statements3 && statements4 if [ -f file_one ] && echo "hello" && [ -f file_two ] && echo "there" then echo "all is true" else echo "there is one false" fi |
or 리스트
코드: |
statement1 || statement2 || statement3 || statement4 if [ -f file_one ] || echo "hello" || echo "there" then echo "there is true more one. else echo "all is false" fi |
문장블럭
코드: |
get_confirm && { grep -v "$cdcatnum" $tracks_file > $temp_file cat $temp_file > $tracks_file echo add_record_tracks } |
쉘스크립트와 함수
쉘스크립를 명령어 라인에서 직접 실행하면 서브셀이라 불리는 셀의 복사가
기동되어 스크립트안의 처리는 서브셀에서 한다. 서브셀과 원래 셀의 현재
디렉토리는 독립되어 있으므로 스크립트안에서 아무리 혅 디렉토리를 변경해도 원래
쉘의 디렉토리에는 영향을 주지 않는다. 한편, 함수의 경우는 원래 쉘에서 그대로
실행되므로 현재 디렉토리의 변경에 효과적이다.
("source"나 "."로 쉘스크립트를 실행하면 현재 쉘에서 수행된다.)
방법 : 함수를 저장하고 있는 파일을 ". func.txt"라고 실행시키면 함수가 현재
쉘에 등록된다.
함수의 사용
함수를 사용할 때 주의 사항은 함수명과 뒤쪽의 '{'사이에는 반드시 공백이
존재해야 한다.
function이라는 단어는 생략이 가능하다.
코드: |
#!/bin/sh # function foo() {... function foo(){ echo "Function foo is executing" } echo "ScriptStarting" foo echo "Script Ended" exit 0 # function foo() {... foo() { echo JAY;} result=$(foo) |
함수에서 인자를 받기
코드: |
#!/bin/sh function quit() { exit } function e() { echo $1 } e Hello e World quit |
함수의 반환값 이용하기
1. 숫자값을 반환하기.
코드: |
function func() { return 0 } result=$(func) |
2. 문자 스트링을 반환하기
코드: |
function func() { echo kkk } result=$(func) |
: 명령
코드: |
while : -> 무한 루프( while true와 동일하다) null 명령 if 조건 ;then : else 명령 fi |
local 키워드
함수내부의 지역 변수를 선언할때 사용.
local var="aldfjas"
코드: |
#!/bin/sh sample_text="global variable" foo(){ local sample_text="local variable" echo "Function foo is executing" echo $sample_text } echo "script starting" echo $sample_text foo |
eval
인수를 평가하게 해준다.
foo=10 --> foo <- 10
x=foo --> x <- foo
y='$'$x --> y <- $foo
echo $y --> $foo가 출력
foo=10 --> foo <- 10
x=foo --> x <- foo
eval y='$'$x --> y <- $foo
echo $y --> $foo인 10이 출력
exec
현재 셀을 다른 프로그램으로 대체한다.
exec echo "asljfasldfkasdlf" --> exec 아래의 명령은 실행되지 않는다.
exit n
스크립트의 반환값을 지정한다. 만약 종료상태를 지정하지 않고 종료한다면
마지막에 실행된 명령의 반환값으로 사용될 것이다.
반환값 0 : 성공
1 ~ 125 : 스크립트에 의해 사용될 수 있는 값.
126 : 파일이 실행 가능하지 않다.
127 : 명령이 발견되지 않았다.
128이상 : 시그널이 발생했다.
printf
최신 셀에서만 유효하다.
코드: |
printf "format string" paramerter1 parameter2 printf "%s\n" hello printf "%s %d\t%s" "Hi There" 15 Peopel |
\\ : 백슬래쉬
\a : 경고
\b : 백스페이스
\f : 폼피드
\n : 새줄
\r : 개행
\t : 수평탭문자
\v : 수직탭문자
\000 : 8진수 값을 가지는 한 문자
set 명령
set 명령은 쉘을 위한 파마메터 변수를 설정한다. 이것은 빈칸으로 구분되는
값을 출력하는 명령에서 필드를 사용하는 유용한 방법이 될 수 있다.
코드: |
# date의 출력결과가 "2003. 06. 22. (일) 19:54:41 KST"일때. set $(date) |
# $0 -> date 명령을 지시하지 않고 현재 쉘스크립트의 이름을 지시한다.
# $1 -> "2003."
# $2 -> "06."
# $3 -> "22."
# $4 -> "(일)"
# $5 -> "19:59:06"
# $6 -> "KST"
shift
$2 -> $1
$3 -> $2
trap
시그널(signal)을 받아들일 때 수행하는 동작을 지정하는 데 사용된다.
[list]
trap `rm -f /tmp/my_tmp_file_$$` INT
--> INT(CTRL+C)가 발생하면 수행하는 명령.
trap - INT --> trap해제.
[list]
명령의 수행
`명령` : 이식성이 강한 오래된 관습.
$(명령) :
산술 확장
echo $((1+1)
echo $(( 1 + 1 ))
echo $[1+1]
echo $[ 1 + 1 ]
echo `echo 1+1 | bc -l`
코드: |
$(()) #!/bin/sh # 주의할 점은 "x = $(($x+1)"이라고 '=' 양쪽을 띄어쓰지 않는다. x=0 while [ "$x" -ne 10 ]; do echo $x x=$(($x+1)) done #!/bin/sh x=0 while [ "$x" -ne 10 ]; do echo $x let x = $x + 1 done #!/bin/sh x=0 while [ "$x" -ne 10 ]; do echo $x let x-=1 done #!/bin/sh i=0 while [ "$i" -ne 10 ]; do filename=`printf %06d.jpeg "$i"` echo "$filename" sleep 1 done] |
toggle 상태 만들기.
코드: |
val=0 while true; do echo $val # 주의할 점은 '=' 양쪽에 공백이 들어가서는 안된다. val=$(( 1 - $val)) # 혹은 "let val= 1 - $val" sleep 1 done |
파라미터 변수
[list]
$1,$2,... 스크립트에 주어진 파라미터
"$9"보다 큰 매개변수는 "${10}"와 같이 "{}"로 감싸주어야 한다.
$* 환경변수 IFS의 첫문자로 구분되고, 하나의 변수에 저장되는 모든
파라미터의 목록, 만약 IFS가 NULL이면 모든 파라메터가 붙어서
나온다.
$@ IFS 환경변수를 사용하지 않는 $*에 대한 변형
"$*"와 "$@"중에서 "$@"를 사용하는 것을 권장한다.
echo `basename $0`
실행된 스크립트에 "./"가 붙어있는 경우에 떼어내고, 스크립트
이름만을 출력한다.
[list]
파라메터 확장
${param:-default}
param이 널이 아니면 param을 반환하고 널(혹은 존재하지 않으면)이면
default의 값을 반환한다. 이 경우에도 param의 값은 변하지 않는다.
${param:=bar}
만약 param이 널이 아니면 param을 반환하고 널이면 param에 bar를 넣고
param을 반환한다. 이 경우에 param의 값이 bar로 변경된다.
${param:+bar}
param이 존재하고 널이 아니면 bar의 값으로 설정하고 널이면 널을 반환한다.
어떤 경우에도 param값은 변경되지 않는다. ${param:-default}와 반대로
동작한다.
${#param}
param의 길이를 제공한다.
${param#word}
앞에서부터 가장 먼저 word가 일치하는 부분까지를 제거한 나머지 부분을
반환한다.
${param##word}
앞에서부터 가장 나중에 word가 일치하는 부분까지를 제거한 나머지 부분을
반환한다.
${param%word}
끝에서부터 가장 먼저 word가 일치하는 부분까지를 제거한 나머지 부분을
반환한다.
${param%%word}
끝에서부터 가장 나중에 word가 일치하는 부분까지를 제거한 나머지 부분을
반환한다.
${param:?bar}
param이 널이 아니면 param을 반환하고 param이 널이면 "param: bar"를
출력하고 현재 셀이 종료된다. 만약 기존의 셀에서 서브셀로 수행된
스크립트라면 현재 셀이 종료되고 그렇지 않고 기존의 셀에서 이어져서
수행되었다면 "param: bar"를 출력하고 계속 수행한다.
예제.
코드: |
#!/bin/bash unset foo echo ${foo:-bar} foo=fud echo ${foo:-bar} foo=/usr/bin/X11/startx echo ${foo#*/} // *는 0이상의 어떤 문자를 의미한다. echo ${foo##*/} bar=/usr/local/etc/local/networks echo ${bar%local*} echo ${bar%%local*} 출력 bar fud usr/bin/X11/startx startx /usr/local/etc/ /usr/ |
shell script에서 2진수 계산하기
코드: |
# 128과 180의 OR값 구하기 let 'result = 128 & 180' echo $result # bc로 리다이렉션해서 x180을 2진수로 변환한 결과를 출력한다. # ibase : 입력의 진수(2|8|10|A) # obase : 출력의 진수(2|8|10|A) echo 'ibase=A;obase=2;180'|bc |
Here Document
코드: |
cat << !FUNKY! --> Here document로 cat에 전달한다. hello this is a here document !FUNKY! --> Here document끝. |
>>>> 동작은 cat으로 "!FUNKY!"사이의 글자를 전달하는 역할을 한다.
ed a_test_file << !FunkyStuff! --> ed editor Here document로 입력한다.
3
d
.,\$s/is/was/
w
q
!FunkyStuff! --> here document끝.
Scrip 디버깅
코드: |
sh -n <script> set -o noexec 형식에러만을 확인한다. set -n 명령을 실행하지 않는다. sh -v <script> set -o verbose 실행하기 전에 명령을 출력한다. set -v sh -x <script> set -o xtrace 명령라인에서 처리한 후에 명령을 set -x 출력한다 set -o nounset 정의되지 않은 변수가 사용될 set -u 때 에러 메시지를 제공한다. |
조건 제어관련 셀명령
break for, until, while 루프를 빠져 나온다.
continue 루프를 계속 진행.
echo 표준 출력(1)으로 입력된 내용을 출력.
exec 현재의 셀을 이용해서 명령어를 실행시킨다.
exit 현재의 셀을 종료한다.
let 산술연산과 논리연산을 수행한다.
read 표준 입력(0)을 통해서 입력을 받는다.
return 함수를 종료한다.
test [] 조건을 검사한다.
`명령어` 명령어를 실행하고 그 결과를 대입한다.
exit와 return
exit는 현재 쉘스크립트를 종료하는 역할을 하고 return은 함수나 sourced
script(?)에서만 반환하는 경우에 사용한다.
배열의 사용
bash의 새 버전부터는 1차원 배열을 지원한다. variable[xx]처럼 선언할 수도 있고
'declare -a variable'처럼 직접적으로 지정해 줄 수도 있다. 배열 선언을
역참조하려면(내용을 알아내려면) ${variable[xx]}처럼 중괄호 표기법을 사용하면
된다.
재지향
파일 디스크립터
0 : 표준 입력
1 : 표준 출력
2 : 표준 에러
코드: |
cat 2>&1 # 표준 에러를 표준출력으로 같이 보낸다. make 1>a.a 2>&1 make의 결과를 a.a로 그리고 error 출력도 a.a로 보낸다. |
C style의 문자열의 해석
코드: |
# echo '111\n222\n333\n' 111\n222\n333\n |
코드: |
# echo $'111\n222\n333\n' 111 222 333 |
위 예제에서 볼 수 있듯이 $"" 안에 들어간 문자열은 C의 문자열에서와 같이
\t,\n,\r등이 해석된다.
따라서 다음과 같이 사용할 수 있다.
코드: |
# find . -type f -exec vi -c $'20,30d\nwq\n' \{} \; |
파일에 대해서 20~30행을 제거하는 명령이다.
예약된 종료코드
0
정상종료.
1
광범위한 일반적 에러
let "var1 = 1/0"
"divide by zero"같은 잡다한 에러
2
bash 문서에 명시되어 있는 쉘 내장명령어의 오사용
거의 보기 힘들고 보통은 디폴트로 1번 종료 코드로 나타남
126
실행 불가능한 명령어의 구동
퍼미션 문제거나 실행 허가가 없는 명령어
127
"command not found"
$PATH 문제거나 오타일 가능성 있음
128
exit에 잘못된 인자 넘김
exit 3.14159
exit는 0에서 255사이의 정수만 받음
128+n
치명적 에러 시그널 "n"
kill -9 스크립트의 $PPID
$?는 137 (128 + 9)을 리턴
130
스크립트가 Control-C에 의해 종료됨
Control-C 는 치명적 에러 시그널 2번(130 = 128 + 2, 바로 위 참고)
255
종료 상태 범위 초과
exit -1
exit는 0에서 255사이의 정수만 받음
저자가 제안하는 방법은 사용자 정의 종료 코드를 64 - 113(성공시 0도
포함)으로 제한해서 C/C++ 표준을 따르는 것입니다. 이렇게 해도 사용자는
여전히 50개의 코드를 쓸 수 있는 있기 때문에 나중에 스크립트의 문제를 좀 더
깔끔하게 해결할 수 있습니다.
지역화(Localization)
지역화된 쉘스크립트는 시스템의 로케일에 따라 정의된 언어로 텍스트를
출력하게 해준다.
코드: |
#!/bin/bash # localized.sh E_CDERROR=65 error() { printf "$@" >&2 exit $E_CDERROR } cd $var || error $"Can't cd to %s." "$var" #문자열들을 $""로 묶어준다. read -p $"Enter the value: " var bash$ bash -D localized.sh "Can't cd to %s." "Enter the value: " # -D옵션은 스크립트를 수행하지 않고 $뒤에서 큰따옴표로 묶인 문자열을 # 보여준다. bash$ bash --dump-po-strings localized.sh #: a:6 msgid "Can't cd to %s." msgstr "" #: a:7 msgid "Enter the value: " msgstr "" # --dump-po-strings 옵션은 -D와 닮았지만 gettext의 "po" 포맷을 사용한다. # ko.po 파일 내용. #: a:6 msgid "Can't cd to %s." msgstr "%s 디렉토리로 옮겨갈 수 없습니다." #: a:7 msgid "Enter the value: " msgstr "값을 넣으세요: " # msgfmt실행. bash$msgfmt -o localized.sh.mo ko.po # localized.sh.mo 파일을 /usr/local/share/locale/ko/LC_MESSAGES 디렉토리에 # copy. # 이제 스크립트에 첫부분에 다음의 두줄을 추가. # TEXTDOMAIN과 TEXTDOMAINDIR 변수는 전체 환경으로 export되어야 한다. TEXTDOMAINDIR=/usr/local/share/locale TEXTDOMAIN=localized.sh |
error string
: bad interpriter
이 에러는 쉘스크립트파일이 UNIX형식이 아닌 DOS 형식일때 이러한 에러가
발생한다. 주의할 것.
생각해 볼 문제
코드: |
test -d package || ( echo 'Wrong working directory.'; exit 1 ) [ -d package ] || ( echo 'Wrong working directory.'; exit 1 ) |
위 문장이 어떻게 동작할 것인가. 단순하게 생각할때는 package라는 디렉토리가
존재한다면 문장이 출력되지 않을 것이다. 그리고 package라는 디렉토리가 존재하지
않는다면 문장을 출력하고 스크립트를 종료하게 될 것이다. 하지만 문장을 출력하고
종료되지 않는다. '('와 ')'로 묶인곳이 실행되면서 현재 shell이 새롭게 하나
생성되어서 실행된다. 따라서 exit를 수행할때는 새롭게 생성된 shell이 종료되고
현재 shell은 종료되지 않는다. 따라서 exit로 종료되지 않고 계속 수행되게 된다.
따라서 다음과 같이 수정한다.
코드: |
# if문 대체하기. if [ -d package ];then echo 'Wrong working directory.' exit 1 fi # 문장 블럭 사용하기. # 주의할 점은 'exit 1' 다음에 ';'가 들어가 주어야 한다. [ -d package ] || { echo 'Wrong working directory.'; exit 1; } # 그냥 '('와 ')'를 없애기 # '||'와 ';'중에 '||'가 우선순위가 더 높아서 의도한 대로 수행된다. # 하지만 블럭의 구분이 모호해진다. 따라서 문장 블럭을 사용하는것이 좋을 것 # 같다. [ -d package ] || echo 'Wrong working directory.'; exit 1 |
임시파일의 생성
$$ : 임의의 난수를 발생한다.
이 숫자값은 현재 shell에서는 계속동일한 값을 가진다.
코드: |
#!/bin/sh echo "temp file" > temp_$$ rm -f temp_$$ |