본문 바로가기

서버운영 (TA, ADMIN)/리눅스

[리눅스] 쉘을 이용한 프로그래밍

유닉스 프로그램을 만들 때 빠질 수 없는 쉘 프로그램에 대해 배워보도록 하겠습니다. 쉘프로그래밍은 많은 개발들이 그 중요성을 간과하고 넘어가지만 쉘 프로그램 만드는 법을 제대로 익히고 사용하면 비용과 시간을 많이 절약할 수 있는 아주 유용한 방법입니다.


외국에서 유닉스 프로그램을 만드는 사람들과 자주 같이 일을 해보면 쉘 프로그램을 많이 만들고 사용하는 것을 볼 수 있는데 그들에게 쉘 프로그램은 다른 기타 언어나 툴킷들과 동등한 수준의 툴로 인식되는 것 같았습니다. 다시 말해 쉘 프로그램이 문제를 해결하는데 적합할 것 같으면 그냥 그걸 쓴다는 것입니다. 그런 다음 C나 자바로 만든 다른 모듈들과 함께 그걸 사용하는 것입니다.


이번 포스팅에서는 쉘프로그램에서 사용하는 문법들을 먼저 소개한 다음 이를 이용하여 프로그램을 작성하는 법을 배우겠습니다. 마지막으로 만들어진 쉘 프로그램을 C/C++ 코드에서 호출하여 사용하는 방법을 배웁니다.



01. 쉘 문법

쉘프로그램은 인터프리터 방식으로 구동이 됩니다. 쉘 프로그램은 유닉스에서 제공하는 명령어(인터페이스)를 그대로 사용하기 때문에 활용하기에 따라 버그가 적고 강력한 프로그램을 작성할 수 있는 도구가 됩니다.


쉘 프로그램을 만든다는 것은 명령어의 조합과 배치로 이루어진 스크립트(script)를 만든다는 것과 동일한 의미입니다. 예전에 도스에서 배치 파일을 만드어본 사람은 쉘 스크립트가 그와 유사하다는 것을 알 수 있을 텐데 쉘 스크립트 작성을 쉘 프로그래밍이라 부를 수 있는 것은 다음과 같은 특징 때문입니다.


쉘 스크립트는 사용자가 편집하고 구동하고 테스트할 수 있는 환경을 가지고 있으며 일반 프로그래밍 언어와 같이 제어구조를 가지고 있습니다. 여기서 제어구조란 프로그램의 플로우를 제어할 수 있는 if 구문, 구문을 반복할 수 있는 while 구문 등을 제공하고 있다는 말입니다. 그리고 변수를 만들고 조작할 수 있는 환경을 제공하고 있습니다.


쉘 문법을 정리하기 전에 쉘 스크립트로 작성된 프로그램을 구동해 보면서 그 필요성을 먼저 깨달아 보겠습니다. 필요성을 느껴야 공부할 때 힘과 의욕이 생깁니다. 작성하고자 하는 프로그램은 다음과 같습니다.


  1. 디스크에서 각각의 분할된 영역이 몇 %나 사용되었는지 확인하는 프로그램이다.
  2. 세부적인 내용은, 먼저 인수로 입력받은 분할영역에 대해 사용량 %를 표시한다.
  3. 부가적인 정보로 사용량이 90%를 넘었으면 "심각한 상태"라고 알려주고, 70% 이상이면 "주의요망 상태", 그 이하면 "양호한 상태"라고 화면에 표시한다.


위의 프로그램을 C 또는 C++로 작성해도 될 것입니다. 필요하면 자바나 기타 다른 언어를 이용하면 될 것입니다. 하지만 쉘에서 이런 프로그램을 만들 수 있는 좋은 명령어를 이미 제공하고 있습니다. "df" 명령어가 그것인데 df 명령어를 수행하면서 "-k"라는 옵션을 활용하면 우리가 원하는 내용을 일부 얻을 수 있습니다. 먼저 "df -k" 프로그램을 실행시켜 보겠습니다.


[root@dev-web ~]# df -k

Filesystem              1K-blocks    Used Available Use% Mounted on

/dev/mapper/centos-root  52403200 5053796  47349404  10% /

... ... ... ...


그러면 위와 같은 내용이 화면에 나타날텐데 이 내용을 분석하면 '/'로 분할된 영역이 있고 이 영역은 총 52403200 * 1024 블록으로 이루어져 있습니다. 현재 5053796 * 1024 블록이 사용중이고, 47349404 * 1024 블록은 사용가능한 영역입니다. 이제 이 부분을 활용하여 프로그램을 만든다면 인수로 "/" 영역을 입력받은 후, 총 블록 수와 사용된 블록 수를 얻은 뒤, "사용블록수/총블록수 * 100"으로 사용 %를 계산합니다.


그런 다음 사용 %가 90%를 넘었는지, 아니면 70%를 넘었는지 또는 그 이하인지를 확인한 후, 현재의 사용 %와 상태를 화면에 출력하면 됩니다. 이제 이러한 내용이 입력된 쉘 스크립트를 작성하면 다음과 같습니다.


#!/bin/sh

usage=`df -k $1 | /bin/awk '{ rem = 0 } { n += 1 } { a = $3 } { b = $4 } \
n == 2 { rem = int(a/(a+b) * 100); print rem} \
END { }'`

if [ $usage -ge 90 ]
then
    echo "DISK($usage) - 심각한 상태"
elif [ $usage -ge 70 ]
then
    echo "DISK($usage) - 주의요망 상태"
else
    echo "DISK($usage) - 양호한 상태"
fi


쉘 스크립트 작성이 끝났으면 다음과 같이 파일이 실행 가능하도록 접근 권한을 변경한 후 테스트를 해보겠습니다.


[root@dev-web shell_programming]# sh DiskUsageCheck.sh

DISK(9) - 양호한 상태


다른 언어를 이용하여 프로그램을 작성한 것보다 한결 깔끔하고 편하게 프로그램이 작성되었습니다. 이와 동일한 프로그램을 다른 기타 언어로 만들어본 독자들은 쉘 프로그램의 편리성에 공감을 할 것입니다. 그럼, 이제 이러한 쉘 프로그램을 어떻게 만들고 상요하는지 하나씩 배워보겠습니다.



변수


쉘 문법에서 먼저 익혀야 하는 것이 변수를 만들고 이용하는 방법입니다. 쉘에서의 변수의 사용목적은 다른 기타 언어에서의 목적과 틀리지 않습니다. 원하는 데이터를 변수에 담고 그를 가공 및 활용하면 되는 것인데 다만 쉘에서의 변수 선언이나 활용이 기타 언어와 다른 면이 있습니다. 


쉘에서는 변수를 선언할 때 그냥 변수명을 적어줍니다. 예를 들어 name이라는 변수를 만들고 싶으면 그냥 'name'이라고 적어서 변수를 선언하면 됩니다. 변수를 선언할 때 변수의 타입이나 형을 명시하지도 않습니다. 이렇게 선언된 변수는 문자열을 입력하여 문자열 변수로 활용해도되고 정수형 데이터를 입력하여 계산 및 연산을 위한 변수로 활용해도 됩니다.


변수에 값을 할당할때는 '='기호를 사용하는데, 이때 =사이에 공백을 넣으면 안됩니다. 예를 들어 name이라는 변수에 이름을 입력하려면 다음과 같이 합니다.


name=JAEHSHIN


만일 입력하려는 이름에 공백이 포함되어 있으면 다음과 같이 할당합니다.


name="JAE H SHIN"


만일 인수로 입력되는 값이 있을 때 이를 변수로 받는 방법은 '$숫자'입니다. 즉, $1은 첫번째 인수의 내용이 되고 $2는 두 번째 인수의 내용이 됩니다. 변수에 할당된 내용을 확인하기 위해 화면에 출력할 때는 'echo' 명령어를 사용하면 됩니다. 예를 들어, 위에서 name이라는 변수에 "JAE H SHIN"이라는 문자열을 할당했는데 이를 화면에 출력하려면 다음과 같이 합니다.


echo $name


이제 지금까지 나열한 내용을 직접 확인하기 위해 스크립트를 만들고 실행을 시켜보겠습니다. 먼저 아래와 같은 스크립트를 작성하겠습니다.


#!/bin/sh

name="JAE H SHIN"
echo $name

echo First: $1, Second $2


스크립트를 보면 1번째 라인에서 어떤 쉘을 이용하여 이 프로그램을 실행시킬 것인지를 지정하고 있는데 이는 스크립트가 실행되면 /bin/sh 프로그램 하에서 실행됨을 의미합니다.  그리고 /bin/sh 프로그램이 종료됨과 동시에 쉘스크립트의 실행도 끝나며 이 속에서 사용된 변수들도 모두 해제가 됩니다.


6번째 라인을 보면 쉘 스크립트를 실행하면서 입력된 첫번째 인수와 두번째 인수를 화면에 출력하는 것을 볼 수 있습니다. 이제 직접 테스트를 통해 그 결과를 확인해 보겠습니다. 


[root@dev-web shell_programming]# ./ShellTest1.sh Shell Programming

JAE H SHIN

First: Shell, Second: Programming


결과를 통해 첫번째 인수인 'Shell'이 $1에 할당되고 두 번째 인수인 'Programming'이 $2에 할당되었음을 확인할 수 있습니다. 변수에 값이 할당되지 않아도 특별한 문제가 발생되지는 않습니다. 만일 널(null)로 초기화를 시켜둔 뒤, 나중에 값을 할당하려면 그냥 다음과 같이 합니다.


name=


변수를 활용하면서도 와일드 문자를 활용할 수 있습니다. 예를 들어 현재 디렉토리에 "DiskUsageCheck.sh", "ShellTest1.sh" 파일이 있다고 가정을 하고 filename이란 변수에 "*.sh"를 할당한 후 echo를 이용하면 다음과 같은 결과가 나옵니다.


[root@dev-web shell_programming]# filename=*.sh

[root@dev-web shell_programming]# echo $filename

DiskUsageCheck.sh ShellTest1.sh

[root@dev-web shell_programming]# echo "$filename"

*.sh


변수를 이용하여 스트링의 일부를 변경할 수도 있습니다. 예를 들어 option이라는 변수에 '1'을 입력한 후 ls -"$option"을 사용하면 ls -l과 동일한 문장이 됩니다. 이 기능을 이용하면 명령어로 사용할 문장을 완성한 후 이를 실행하는 프로그램을 작성할 수 있습니다. 지금까지 언급한 내용을 바탕으로 ShellTest2.sh 파일을 작성하고 구동을 해보겠습니다. 이 파일의 내용은 다음과 같습니다.


#!/bin/sh

option=
First=$1
option=$First
ls -"$option"

스크립트 파일의 3번 라인을 보면 option이라는 변수를 초기화하고 있음을 알 수 있습니다. 그리고 4번째 라인에서 First라는 변수에 첫 번째 인수를 할당한 후 이를 option 변수에 할당하는 것을 볼 수 있습니다. 그리고 마지막 6번째 라인에 ls 명령어를 사용하면서 option에 할당된 내용을 활용하는 것을 볼 수 있습니다.


스크립트 파일에 실행권한을 부여한 후 실행을 지켜보겠습니다. 이때 'l' 문자를 인수로 부여하면 다음과 같은 결과가 나타납니다.


[root@dev-web shell_programming]# sh ShellTest2.sh l

total 24

-rwxr--r--. 1 root root 339 Oct  9 12:55 DiskUsageCheck.sh

-rw-r--r--. 1 root root 367 Oct  9 12:56 DiskUsageCheck.sh.blog

-rwxr-xr-x. 1 root root  67 Oct  9 13:26 ShellTest1.sh

-rw-r--r--. 1 root root  95 Oct  9 13:27 ShellTest1.sh.blog

-rwxr-xr-x. 1 root root  56 Oct  9 14:06 ShellTest2.sh

-rw-r--r--. 1 root root  84 Oct  9 14:07 ShellTest2.sh.blog


스크립트 파일 내부에서 개발자가 자의적으로 변수를 제거할 수 있습니다. 이때 사용되는 것이 'unset' 키워드 입니다. unset과 제거하고자 하는 변수를 함께 적어주면 이후로는 해당 변수를 활용할 수 없게 됩니다. 예를 들어 다음과 같은 코드를 만들었다고 해보겠습니다.


name="JAE H SHIN"

unset name

newname=$name


이 코드에서 newname 변수에는 Null이 할당됩니다. 즉 "newname=$name"이라는 문장은 "newname="이라는 문장과 동일한 문장이 됩니다. 따라서 echo를 이용하여 $newname을 출력해봐도 화면에는 아무것도 출력이 되지 않습니다.


C 언어에서 변수와 상수를 만들 수 있듯이 쉘에서도 상수를 만들 수 있습니다. 즉, 값을 임의로 바꿀 수 있는 변수와는 달리 한번 값을 할당한 후 변경없이 사용하는 상수를 선언할 수 있다는 것입니다. 이때 사용되는 키워드는 'readonly'인데 readonly로 선언된 변수는 이후로는 값을 변경할 수 없는 상수가 됩니다.


예를 들어 다음과 같이 사용하도록 합니다.


name="JAE H SHIN"

readonly name


readonly만 쓰고 변수명을 따로 쓰지 않으면 읽기전용으로 된 상수들은 모두 화면에 표시가 됩니다. 예를 들어 다음과 같은 문장을 만들고


readonly name

readonly address

readonly phone

readonly


실행해보면 화면에 다음과 같은 내용이 출력됩니다.


읽기전용 address

읽기전용 name

읽기전용 phone


쉘 스크립트 내에서는 사용하는 변수에 사용자가 스크립트 내에 임의로 만든 변수뿐만 아니라 쉘 전체가 사용하고 있는 환경변수도 사용할 수 있습니다. 예를 들어 DISPLAY라는 환경변수를 다음과 같이 선언했다고 가정해보겠습니다.


$ export DISPLAY=192.168.8.101:0.0


또는 C 쉘에서


$ setenv DISPLAY 192.168.8.101:0.0


이때 스크립트 내에서 "echo $DISPLAY"라는 문장을 실행시키면 설정한 환경변수 값이 출력됩니다. 변수에 값을 할당할 때 명령어 입력줄에서 입력받은 내용을 할당하는 방법이 있습니다. 즉, 스크립트가 실행되는 중간에 변수에 할당할 내용을 사용자로부터 입력받은 후 그 내용을 할당하는 방법입니다.


이때 사용되는 키워드는 'read'인데 read와 변수를 함께 적으면 그 라인이 실행될 때 사용자로부터 입력할 것을 요청하게 됩니다. 이 방법은 프로그램을 작성할 때 아주 유용하게 활용할 수 있습니다. 다음의 예를 통해 확인해보겠습니다.


파일속에 찾고자 하는 문장이 있는지 grep을 활용하는 프로그램을 작성한다.

프로그램을 실행하면 -l 옵션을 이용할 것인지 -s 옵션을 이용할 것인지 사용자에게 묻는다.

어떤 문장을 검색하고 싶은지 사용자에게 묻는다.

옵션과 검색문장을 이용하여 grep을 실행시키고 그 경과를 화면에 출력한다.


이러한 내용을 바탕으로 작성된 Grep.sh 파일은 다음과 같습니다.

#!/bin/sh

echo "grep에 사용될 옵션 입력, -l 또는 -s를 입력해 주세요."
read option1
echo "검색하고자 하는 문장을 입력해 주세요."
read option2
grep $option1 $option2 *

프로그램을 실행해보겠습니다. 이때 -s 옵션을 이용하고 "sh"라는 문장을 찾는다고 하면 다음과 같은 내용이 출력될 것입니다. (grep 명령어 옵션 상세 참고 : http://so-blog.net/2016/03/24/grep/ )


[root@dev-web shell_programming]# ./Grep.sh

grep에 사용될 옵션 입력, -l 또는 -s를 입력해 주세요.

-s

검색하고자 하는 문장을 입력해 주세요.

sh

DiskUsageCheck.sh:#!/bin/sh

DiskUsageCheck.sh.blog:<pre class="brush:c">#!/bin/sh

Grep.sh:#!/bin/sh

ShellTest1.sh:#!/bin/sh

ShellTest2.sh:#!/bin/sh


위의 코드에서 echo가 실행될 때, 엔터가 실행되어서 다음 라인으로 넘어가는 것을 볼 수 있습니다. 따라서 다음과 같은 문장을 실행시키면 무척 어색하게 됩니다. 


echo "Input Option:"

read option


이때 echo 문장에 \c를 넣어주면 read가 실행될 때 동일한 라인에서 사용자의 입력을 받을 수가 있게됩니다. 즉, 다음과 같이 해주면 됩니다.


echo "Input Option: \c"

read option


쉘에서 변수를 활용할 때 앞에서 설명한 $1, $2처럼 특정 키워드로 활용되는 변수들이 있습니다. 이러한 변수들을 활용하면 보다 유용한 프로그램을 쉽게 작성할 수 있는데 먼저 변수의 이름과 어떤 값을 가지고 있는지 알아보겠습니다.

  • $#: 명령입력 라인에서 입력된 인수의 개수를 가지고 있다.
  • $*: 이 변수는 명령입력 라인에서 입력된 인수 전체의 내용을 포함한다. 즉, $1 ~ $n을 모두 합친것과 같다.
  • $$: 쉘프로그램이 실행되면서 사용된 프로세스의 ID 값을 가진다.
  • $!: 쉘프로그램이 실행시킨 백그라운드 프로세스의 ID 값을 가진다.

이러한 변수들을 활용한 간단한 예제를 보도록 하겠습니다. 이 프로그램은 앞에서 보았던 Grep.sh 프로그램과 유사한 작업을 하는 프로그램인데 사용자로부터 별도의 입력을 받지 않고 필요한 인수들을 한번에 명령어 입력줄에서 기입해야 하는 부분이 틀립니다. 먼저 전체 소스를 보도록 하겠습니다.

#!/bin/sh
echo "grep을 위한 쉘 프로그램이 실행되었습니다."
echo "이 프로그램은 프로세스 ID: $$에서 실행중입니다"
echo "입력하신 인수는 총 $#입니다."
echo "인수들의 전체 내용은 다음과 같습니다."
echo "$*"
echo "입력하신 인수를 이용하여 grep을 백그라운드로 실행합니다."
grep $* * &
echo "grep은 백그라운드 프로세스 ID: $!에서 실행되었습니다."

ShellTest3.sh 프로그램을 "-l sh"라는 인수와 함께 실행을 시켜보겠습니다. 이때 주의해야 하는 것은 grep의 실행 내용이 아니라 각각의 키워드 변수들이 어떤 정보를 가지고 있으며 어떤 내용들을 출력했는지에 주목해야 합니다. 실행 결과는 다음과 같습니다.


[root@dev-web shell_programming]# chmod 755 ShellTest3.sh

[root@dev-web shell_programming]# ./ShellTest3.sh -l sh

grep을 위한 쉘 프로그램이 실행되었습니다.

이 프로그램은 프로세스 ID: 14975에서 실행중입니다

입력하신 인수는 총 2입니다.

인수들의 전체 내용은 다음과 같습니다.

-l sh

입력하신 인수를 이용하여 grep을 백그라운드로 실행합니다.

grep은 백그라운드 프로세스 ID: 14976에서 실행되었습니다.

[root@dev-web shell_programming]# DiskUsageCheck.sh

Grep.sh

ShellTest1.sh

ShellTest2.sh

ShellTest3.sh


특정 키워드 중에는 $? 변수도 있습니다. 이 변수의 경우, 유닉스에서 실행한 프로그램이 종료되면서 리턴한 값을 할당받아 가지고 있습니다. 이때 프로그램이 제대로 종료가 되었으면 0을 리턴하고 그외의 값을 리턴한 경우에는 에러나 문제가 발생한 경우입니다. 예를 들어, 다음과 같은 내용이 쉘스크립트에 포함된 경우에


ls

echo $?


echo로 찍힌 $?의 값이 '0'이면 ls 명령이 제대로 수행되었음을 의미합니다. 이 변수를 활용하면 마지막에 실행한 프로그램이 제대로 실행이 되었는지 확인할 수 있기 때문에 스크립트 내에서 프로그램을 테스트하는데 유용하게 활용할 수 있습니다.


'set' 명령을 이용하면 현재 쉘에서 사용되고 있는 변수들의 리스트를 확인할 수 있습니다. set을 이용하여 나타난 변수들은 대문자를 이용해도 됩니다. 다시 말해 다음과 같이 했을 때 동일한 결과가 나타납니다.


$ echo $user

jshin

$ echo $USER

jshin


여기서 나타난 변수들은 쉘의 환경이나 사용자의 로그인 정보 등 다양한 값들을 가지고 있기 때문에 쉘 프로그래밍에서 무척 유용하게 활용할 수 있는 것들입니다. 다음의 쉘 프로그램을 작성하고 실행해보면서 그 내용을 확인해보도록 하겠습니다.


#!/bin/sh
echo "사용자의 홈 디렉토리: $HOME"
echo "사용자의 쉘 종류: $SHELL"
echo "사용자의 로그인 ID: $LOGNAME"
echo "현재 PATH에 잡힌 경로들: $PATH"
echo "현재 작업 디렉토리: $PWD"


위의 스크립트를 보면 각각의 변수와 그 변수가 가지고 있는 값이 무엇을 의미하는지 알 수 있을 것입니다. 아래의 내용은 ShellTest4.sh 프로그램을 실행한 결과를 나타낸 것입니다.


[root@dev-web shell_programming]# ./ShellTest4.sh

사용자의 홈 디렉토리: /root

사용자의 쉘 종류: /bin/bash

사용자의 로그인 ID: root

현재 PATH에 잡힌 경로들: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/usr/local/bin

현재 작업 디렉토리: /home/unix_system/shell_programming


쉘에서 변수를 사용할 때 변수에 값이 할당된 경우에는 변수가 가진 값을 사용하고 그렇지 않으면 티폴트의 다른 값을 활용하는 방법이 있습니다. 그리고 더 나아가 디폴트로 지정한 내용을 값이 할당되지 않은 변수속에 할당하는 방법도 있습니다. 이러한 내용을 간단히 살펴보도록 하겠습니다.


먼저 변수에 할당된 값이 있으면 그 값을 사용하고 그렇지 않으면 디폴트로 지정한 다른 값을 사용하는 방법은 다음과 같습니다.


${X:-Y}


이 경우에 X라는 변수에 값이 있으면 X를 사용하고 그렇지 않으면 Y를 사용하게 됩니다.


위에서 사용한 ${X:-Y}의 경우 X에 값이 없으면 Y가 사용된다고 했는데 Y가 사용되고 난 뒤에도 X에는 여전히 아무런 값도 할당되지 않습니다. 만일 이때 X에 Y의 값을 할당하고 싶으면 다음과 같이 합니다.


${X:=Y}


만일 X라는 변수에 값이 할당되어 있지 않으면 더 이상 프로그램을 수행하지 않고 에러메시지와 함께 종료하고 싶으면 다음과 같이 합니다.


${X:?에러메시지}


지금 소개한 내용을 이용하면 변수에 값이 할당되지 않아서 발생하는 문제를 해결하는데 도움이 됩니다. 그리고 값이 할당되지 않은 경우에 프로그램을 종료시켜야 할 때도 도움이 됩니다. 그럼 이러한 내용을 바탕으로 작성된 프로그램을 보도록 하겠습니다.


#!/bin/sh
ls ${arg1:--l}

ps ${arg2:=" -ef"}
ps $arg2

grep ${arg3:?" 인수가 비어서 프로그램을 종료합니다."}
ls

위 코드는 아주 간단한 내용이지만 버그가 없는 안정적인 프로그램을 작성하는데 아주 큰 도움을 받을 수 있는 내용들입니다.


특수 문자들의 기능을 해제해야 하는 경우가 있습니다. 예를 들어 name이라는 변수에 "$NAME"이라는 문자열을 그대로 입력하고 싶은 경우가 있을 것입니다. 이런 경우에는 다음과 같이 해주면 됩니다.


name=\$NAME


만일 이때 '\'를 사용하지 않으면 쉘은 NAME이라는 변수가 가진 값을 찾아서 입력하려 할 것이고 NAME에 아무런 값이 없으면 NULL 값이 할당될 것입니다.



제어문


프로그램의 흐름을 제어하는 제어문에는 크게 나눠서 조건문과 반복문이 있습니다. 조건문은 프로그램의 흐름이 조건을 만족하는 구문쪽으로 흘러가도록 만드는 것이고, 반복문은 원하는 횟수만큼 특정 구문이 반복 실행되도록 만드는 것을 의미합니다. 이러한 제어문은 프로그램을 작성하는데 있어 아주 큰 비중을 차지하는데 쉘 프로그램도 예외는 아닙니다.


조건문

쉘에서 명령어들을 순차적으로 실행할 때 '||' 연산자와 '&&' 연산자를 이용하여 연산자 바로 뒤에 위치한 명령어가 조건에 따라 실행되도록 만들 수 있습니다. 이때 '||' 연산자는 앞의 명령어가 실패했을 때에만 연산자 뒤에 위치한 프로그램이 실행되도록 만듭니다. 


외와 반대로 '&&' 연산자는 앞에서 실행한 명령어가 성공적으로 수행되었을 때만 실행되도록 만드는 연산자입니다. 이들 연산자는 쉘 프로그램을 작성할 때 요긴하게 활용이 됩니다. 예를 들어 '||' 연산자의 경우, 명령어의 실행이 실패했을 때 이 내용을 로그로 남기는 작업에 활용할 수 있을 것입니다. 예를 들면 다음과 같습니다.


Run Command1 || echo 첫번째 명령 실행 실패 >> log.txt

Run Command2 || echo 두번째 명령 실행 실패 >> log.txt


위와 같이 작성해 두면 log.txt 파일에 실패한 명령에 대한 로그를 쉽게 작성할 수 있을 것입니다. 아래와 같이 실패가 예상되는 명령에 대한 보완 명령 실행에 활용할 수도 있을 것입니다.


ShellTest5.sh || chmod 755 ShellTest5.sh && ShellTest5.sh


위 문장을 간단히 설명하면 ShellTest5.sh 명령(프로그램)을 실행시키면서 그 명령이 실패했을 경우에 이를 해결하는 문장입니다. 즉, 실패가 예상되는 경우 중 하나인 실행 권한을 부여하지 않은 문제를 해결하기 위한 명령이 || 연산자 뒤에 위치합니다. 그리고 그 뒤에는 chmod가 제대로 실행되고 난 뒤, ShellTest5.sh 명령을 한번 더 실행하기 위해 && 연산자 뒤에 명령어 구문이 위치하고 있습니다.


'||', '&&' 연산자도 조건문을 만들고 처리하는데 유용하게 활용되지만, 조건문하면 가장 먼저 떠오르는 것은 if-else 구문일 것입니다. 다른 프로그램 개발언어와 마찬가지로 쉘 프로그램에서도 if-else 구문은 그냥 넘어갈 수 없는 아주 중요한 구문입니다.


쉘에서 제공하는 if-else 구문은 크게 세 가지로 나뉘는데 첫번째가 if가 하나만 있는 경우입니다. 이때는 if 조건을 만족할 때에만 해당 구문이 실행됩니다. 이때 사용되는 문법 형식은 다음과 같습니다.


if [ conditions ]
then
    실행하고자 하는 문장
fi


두번째는 if 조건문가 else 문이 존재하는 경우입니다. 이때는 if조건을 만족할 때에는 if절에 포함된 구문만 실행이 되고 else 절 속의 문장은 실행되지 않습니다. 하지만 if 조건을 만족시키지못할 경우에는 else 절에 포함된 구문이 실행됩니다. else 절 속의 문장이 실행되고 나면 조건문을 빠져나가게 됩니다. 이 구문의 형식은 다음과 같습니다.


if [ conditions ]
then
    실행하고자 하는 문장
else
    조건  불만족시 실행하고자 하는 문장
fi


마지막 세번째 경우는 여러개의 if문이 존재하는 if, else if, ... else 구문입니다.  이 경우에는 if문이 조건을 만족하면 if 문을 실행시키는 것은 다른 경우와 동일하다. 하지만 if 문이 조건을 만족시키지 못하는 경우, 그 다음에 있는 else if 문 속의 조건을 다시 조사하는 것이 다른 경우와 틀립니다.


만일 else if 문 속의 조건이 맞으면 else if 문 속의 구문이 실행이 되고 맞지 않으면 또다시 다음에 있는 else if 문 속의 조건을 조사하는 작업을 시작합니다. 만일 if나 else if 문 중 하나가 조건이 맞아서 실행이 되고 나면 그 뒤에 있는 else if 구문은 실행이 되지 않습니다.


이 구문형식의 문법은 다음과 같습니다.


if [ conditions ]
then
     첫 번째 조건 만족시 실행 문장
elif [ conditions ]
then
    두 번째 조건 만족시 실행 문장
else
    모든 조건문이 실행되지 않았을 때 실행 문장
fi


이번에는 if를 이용한 조건문의 예를 직접 보도록 하겠습니다. 다음의 예는 쉘 프로그램을 실행시키면서 함께 입력된 인수의 개수와 내용을 화면에 표시하는 프로그램입니다.


이때 프로그램을 실행하면서, 조건문을 이용하여 인수가 두 개인 경우 또는 하나인 경우 또는 그 외의 경우에 대해 체크한 후, 조건에 따라 화면에 출력하는 내용을 달리합니다.


#!/bin/sh

if [ $# -eq 2 ]
then
    echo "인수는 두개이며 내용은 <$1>, <$2> 입니다."
elif [ $# -eq 1 ]
then
    echo "인수는 한개이며 내용은 <$1>입니다."
else
    echo "인수는 하나도 없거나 너무 많습니다."
fi


위의 예제 프로그램을 보면 인수의 개수를 체크하면서 "-eq" 연산자를 이용한 것을 볼 수 있습니다. -eq는 C에서 사용하는 "=="과 동일한 의미를 가진 것으로 좌우의 값이 똑같은지를 체크하는 연산자입니다. 이렇게 조건문에서 사용되는 연산들에는 다음과 같은 것들이 있습니다.


  • 문자열 체크
    [ stringName ] - 문자열이 널(Null) 인지 체크, Null이 아니면 참
    [ -n stringName ] - 문자열의 사이즈가 0 이상인지 체크, 0 이상이면 참
    [ -z stringName ] - 문자열의 사이즈가 0인지 체크, 0이면 참
    [ stringNameA = stringNameB ] - A 문자열과 B 문자열이 같은지 체크, 같으면 참
    [ stringNameA != stringNameB ] - A 문자열과 B 문자열이 다른지 체크, 다르면 참

  • 숫자 대소 관계 체크
    [ intA -ge 100 ] - 숫자 A가 100보다 크거나 같은지 체크, 100 이상이면 참
    [ intA -gt 100 ] - 숫자 A가 100보다 큰지 체크, 100이 넘으면 참
    [ intA -le 100 ] - 숫자 A가 100보다 작거나 같은지 체크, 100 이하이면 참
    [ intA -lt 100 ] - 숫자 A가 100보다 작은지 체크, 100 미만이면 참

  • 파일체크
    [ -r filename ] - 해당 파일이 읽기 가능한지 체크
    [ -w filename ] - 해당 파일이 쓰기 가능한지 체크
    [ -x filename ] - 해당 파일이 실행 가능한지 체크
    [ -s filename ] - 해당 파일의 사이즈가 제로 이상인지 체크
    [ -d filename ] - 해당 파일이 디렉토리 파일인지 체크
    [ -f filename ] - 해당 파일이 보통 파일인지 체크
    [ -h filename ] - 해당 파일이 링크 파일인지 체크

  • 조건문의 결합
    [ 조건문A -a 조건문B ] - 조건문 A와 조건문 B가 모두 참인지 체크, -a는 AND와 동일
    [ 조건문A -o 조건문B ] - 조건문 A와 B 중 참이 하나라도 있는지 체크, OR과 동일


if 조건문을 사용한 예를 하나 더 보면서 if문을 마무리 하겠습니다. 다음의 예는 인수로 파일 이름을 입력받은 후, 해당 파일이 읽기 가능하면 파일 속의 내용을 화면에 출력합니다. 만일 해당 파일이 존재하지 않으면 해당 파일을 하나 생성합니다. 이번 예제에서는 if 문속에 if 문이 하나더 존재하는 다중 if 문이 사용되었습니다.


#!/bin/sh

if [ $# -eq 1 ]
then
    if [ -r $1 ]
    then
        cat $1
    else
        echo "존재하지 않는 파일이군요."
        echo "그럼, $1 파일을 직접 만들어 봅시다."
        echo "데이터를 입력한 후 Ctrl-C를 누르세요..."
        cat > $1
    fi
else
    echo "파일이름을 넣지 않았습니다. 다시 실행해 주세요."
fi

위의 프로그램을 직접 코딩하고 테스트해보면 인수로 입력한 파일이 없을때는파일을 직접 만드는 것을 볼 수 있을 것입니다. 그리고 파일을 만들면서 사용자가 입력한 내용이 새로 생성된 파일 속에 입력되는 것을 확인할 수 있을 것입니다.


지금까지 조건문에서 가장 많이 활용되고 있는 if 구문을 살펴보았는데, 쉘에서 조건문을 위해 if 문 외에 case 구문도 제공하고 있습니다. 눈치가 빠른 독자는 case 문이라고 했을 때 대충 짐작을 했을 텐데 case 문은 바로 일반 언어에서 제공하고 있는 switch 문과 동일한 기능을 제공하는 구문입니다.


case문은 아래와 같은 형식을 가지고 있습니다.


case $변수명 in
문장1)
    첫 번째 명령어 ;;
문장2)
    두 번째 명령어 ;;
문장3)
    세 번째 명령어 ;;
*)
    Default 명령어 ;;
esac


위의 형식에서 case 키워드 바로 뒤에 있는 변수가 가지고 있는 값이 문장1과 동일하면 첫번째 명령어가 수행되고 문장2와 동일하면 두 번째 명령어가 수행됩니다. 만일 문장1~3 중에 일치하는 문장이 없으면 Default 명령어가 실행됩니다. Default 명령어를 제거하고 싶으면 '*'조건과 Default 명령 부분을 제거하면 됩니다. case 문은 마지막에 'esac'를 기술하면서 구문이 종료하게 됩니다. 그러면 case 문의 예제를 보도록 하겠습니다.


#!/bin/sh

case $1 in
ls)
    ls ;;
ps)
    ps ;;
pwd)
    pwd ;;
*)
    echo "인수를 넣지 않았거나 존재하지 않는 명령어입니다.";
esac


case 문을 끝내기 위해 사용한 esac는 case를 거꾸로 적은 글자입니다. 위의 예제를 실행시켜보면 인수로 ls를 입력하면 ls 명령어가, ps를 입력하면 ps 명령어가, pwd를 입력하면 pwd 명령어가 각각 실행됩니다. 만일 그 외의 인수를 넣거나 또는 인수를 넣지않으면 echo에 입력한 문장이 화면에 나타나게 됩니다.



반복문

반복문은 정해진 구간의 구문을 정해진 조건이 만족되는 동안 반복해서 실행하는 구문을 뜻합니다. 다른 기타 언어에서와 마찬가지로 이 구문은 무척 중요한 구문으로 쉘에서도 반복문을 수행하는데 어려움이 없도록 여러 종류의 명령을 제공하고 있습니다.


먼저 반복문을 위한 구문중 하나인 while 문을 보도록 하겠습니다. while 문은 조건을 만족하는 동안 내부에 가지고 있는 구문(do와 done사이)을 반복해서 실행하는 문으로 다음과 같은 형식을 갖고 있습니다.


while [ 조건 ]
do
    명령어 구문
done


while 문의 간단한 예를 보도록 하겠습니다. 이 예제는 쉘 프로그램을 실행하면서 같이 입력한 인수들을 하나씩 실행하는 프로그램입니다.


#!/bin/sh
while [ $# -gt 0 ]
do
    echo "< $1 명령 실행 >"
    $1
    shift
done

아래는 위의 프로그램의 실행 예입니다.


[root@dev-web shell_programming]# ./WhileTest.sh ls pwd ps

< ls 명령 실행 >

caseTest.sh        DiskUsageCheck.sh.blog  IfTest1.sh       IfTest2.sh.blog     ShellTest2.sh       ShellTest3.sh.blog  ShellTest5.sh       WhileTest.sh

caseTest.sh.blog   Grep.sh                 IfTest1.sh.blog  ShellTest1.sh       ShellTest2.sh.blog  ShellTest4.sh       ShellTest5.sh.blog  WhileTest.sh.blog

DiskUsageCheck.sh  Grep.sh.blog            IfTest2.sh       ShellTest1.sh.blog  ShellTest3.sh       ShellTest4.sh.blog  test

< pwd 명령 실행 >

/home/unix_system/shell_programming

< ps 명령 실행 >

  PID TTY          TIME CMD

12606 pts/0    00:00:00 bash

17635 pts/0    00:00:00 WhileTest.sh

17637 pts/0    00:00:00 ps


위의 예를 보면 shift 명령어를 사용한 것을 볼 수 있습니다. shift 명령어는 인수로 들어온 내용을 하나씩 옮겨가는 기능을 합니다. 즉, shift가 한번 실행되면 $1은 $0가 되고 $2는 $1이 됩니다. 따라서 인수만큼 shift가 실행된 후에는 while 속의 조건문이 거짓이 되고 do~done 사이의 구문은 반복실행을 멈추게 됩니다.


While 구문과 대비되는 반복문에는 Until 구문이 있습니다. While 구문이 조건이 참인동안 반복 실행하는 것과 달리, Util 구문은 조건이 거짓인 동안만 해당 구문을 반복 실행합니다. 그리고 조건이 참이 되는 순간 Until 구문은 종료가 됩니다. 위에서 사용했던 WhileTest.sh 예제를 Until 구문에 맞게 바꾸면 다음과 같습니다. 예제 코드를 보면 조건문이 변경된 것을 알 수 있습니다.


#!/bin/sh
until [ $# -le 0 ]
do
    echo "< $1 명령 실행 >"
    $1
    shift
done


While과 Until 구문외에 반복문에서 효과적으로 활용할 수 있는 구문으로 For 구문이 있습니다. 쉘에서 제공하는 For 구문 또한 C 언어나 다른 기타 언어에서와 사용목적이 동일하지만 문법 형식에는 약간 차이가 있습니다. For 구문의 형식부터 보면 다음과 같습니다.


for 변수명 in value1 value2 ...
do
    반복 실행 문장
done

위의 구문 형식을 보면 for 키워드 뒤에 '변수명'이 있고 in 키워드 뒤에 값들이 나열되는 것을 볼 수 있는데 for문은 in 뒤에 있는 값이 하나씩 '변수명' 속에 할당이 되면서 값의 할당이 끝날 때까지 do~done 사이의 문장을 반복 실행하게 됩니다. 그리고 문장을 반복 실행하면서 '변수명'에 할당된 값들을 하나씩 활용하거나 하면 됩니다. 위에서 사용했던 예제를 for 문을 이용하여 변경하면 다음과 같습니다.


#!/bin/sh
for variable in $*
do
    echo "< $variable 명령 실행 >"
    $variable
done

위의 예문을 보면 variable이라는 변수를 선언했고, in 키워드 뒤에는 인수로 들어온 모든 문자열($*)을 사용 값으로 선언한 것을 알 수 있습니다. 이제 for 문이 반복 실행되면서 variable에는 $1부터 하나씩 할당이 되고 원하는 작업들이 실행되는 것을 볼 수 있습니다.


쉘에서도 다른 언어처럼 break 문과 continue 문을 제공하고 있는데 이들이 수행하는 작업도 다른 언어의 그것과 동일합니다. 즉, break 문을 만나면 해당 반복 구문의 실행이 중지되면서 반복 구문 밖으로 실행 포인터가 옮겨갑니다. 반면에 continue 구문을 만나게 되면 실행 포인터는 continue 밑의 문장을 실행하지 않고 바로 다음 반복 구문으로 실행을 옮기게 됩니다. 그러면 break와 continue를 활용한 예문을 직접 보도록 하겠습니다.


#!/bin/sh

for variable in $*
do
    if [ $variable = java ]
    then
        echo "이번 에디션에서 자바 언어는 다루지 않습니다."
        continue
    elif [ $variable = quit ]
    then
        echo "Quit을 만나 for문을 종료합니다."
        break
    else
        echo "$variable 언어는 이번 에디션에서 다루는 언어입니다."
    fi
    echo "다음 언어를 체크합니다..."
done


위의 예제를 보면 각각의 인수에 대해 java와 quit이라는 단어가 있는지 체크를 하고 만일 java와 quit이 아니면 "...언어는 이번 에디션에서 다루는 언어입니다."이라는 문장과 "다음 언어를 체크합니다..."라는 문장을 화면에 출력하게 됩니다.


만일 java를 만나게 되면 continue가 되어 "다음 언어를 체크합니다..."라는 문장을 출력되지 않고 바로 다음 반복문이 실행됩니다. 그리고 quit을 만나면 break가 실행되어 for 문을 빠져나가게 됩니다. 위의 예제를 직접 실행해 보면 다음과 같습니다.



[root@dev-web shell_programming]# ./BreakContinue.sh c c++ java shell quit tcl perl

c 언어는 이번 에디션에서 다루는 언어입니다.

다음 언어를 체크합니다...

c++ 언어는 이번 에디션에서 다루는 언어입니다.

다음 언어를 체크합니다...

이번 에디션에서 자바 언어는 다루지 않습니다.

shell 언어는 이번 에디션에서 다루는 언어입니다.

다음 언어를 체크합니다...

Quit을 만나 for문을 종료합니다.


소개되는 예문들은 꼭 코딩 및 실행을 해보기 바랍니다. 그리고 코딩과 실행을 할 때는 기계적으로 실행하고 "어, 되네"하면서 그냥 넘어가지 말고, 이 예제를 어떻게 활용해볼까라는 고민도 하고 변경도 해보면서 넘어가기 바랍니다. 자기 것으로 남지 않으면 아무리 좋은 예문도 나중에 별 도움이 되지 않습니다.


지금까지 제어문의 주요 구문인 조건문과 반복문을 살펴보았는데 마지막으로 도움이 될만한 예제를 하나 만들어 보면서 제어문 부분을 마무리하도록 하겠습니다. 아래의 예제(runCmd)는 여러 개의 쉘 명령어를 한꺼번에 실행하도록 만들어주는 예제입니다. 즉, 다음과 같이 구동을 시킬 수 있습니다.


$ runCmd ls ps

$ runCmd ls -l ps

$ runCmd ls ps -ef

$ runCmd ls -l ps -ef


예제 속에는 쉘 명령어가 옵션이 있는지 여부를 체크하고, 옵션이 있으면 옵션과 함께 명령어를 실행하고 옵션이 없으면 명령어만 실행하는데, 이를 위해 while 문과 다중 if 문 그리고 case 문이 활용되었습니다. 코드는 다음과 같습니다.


#!/bin/sh
commandName=0
commandOption=0
complete=0

while [ $# -gt 0 ]
do
    case $1 in
    -*)
        commandOption=$1 ;
        complete=2 ;
        shift ;;
    *)
        if [ $complete = 0 ]
        then
            commandName=$1
            complete=1
            shift
            if [ $# = 0 ]
            then
                $commandName
            fi
        elif [ $complete = 1 ]
        then
            $commandName
            commandName=$1
            shift
            if [ $# = 0 ]
            then
                $commandName
            fi
        fi ;;
    esac
    if [ $complete = 2 ]
    then
        $commandName $commandOption
        complete=0
    fi
done


C나 기타 언어에서는 반복되는 코드는 함수로 만들어 호출하는 경우가 많습니다. 함수를 만들어 사용하면 보다 구조적인 프로그램을 작성할 수 있는데 쉘에서도 함수를 만들고 사용할 수 있습니다.



함수 작성


별다른 설명 없이도 함수(또는 메소드)를 작성하고 사용하면 좋다는 것은 모두 공감할 것입니다. 다만 함수를 어떻게 효율적으로 잘 만들고 잘 활용하냐가 문제일 뿐입니다. 이번에는 쉘에서 어떻게 함수를 만들고 호출하는지 알아보도록 하겠습니다.


쉘에서 함수를 만들기 위해 사용하는 구문은 다음과 같이 스크립트의 샂ㄱ부에 정의가 되어있어야 합니다.


함수를 사용할 때는 정의된 함수의 이르미만 호출하면 됩니다. 만일 함수에 인자를 넘기려면 함수 이름 옆에 인자로 넘기 ㄹ문자열을 나열해주면 됩니다. 인자와 함께 함수를 호출할 때, 함수내부에서 인자를 사용하는 방법은 쉘 프로그램을 호출하면서 인자를 넘겼을 때 사용하는 방법과 동일합니다. 즉, $1... 등을 사용하면 됩니다. 그럼 간단한 예제를 보면서 함수부분은 마무리하도록 하겠습니다.


아래 예제는 특별한 기능이 없는 함수를 만들고 호출하는 것을 보여주는데 한 함수는 인자가 없이 호출되는 것을 또 다른 함수는 이자와 함께 호출되는 것을 보여주고 있습니다.


#!/bin/sh
withoutArg()
{
    echo "Run withoutArg() Function"
}
whitArg()
{
    echo "Run withArg() Function"
    while [ $# -gt 0 ]
    do
        echo "Func with $1"
        shift
    done
}

whitoutArg
whitArg Jae H Shin


함수를 만들고 사용하는걸 즐겨야 합니다. 또한 함수를 만들때는 재활용할 것을 항상 염두에 두고 보다 완성도 높은 함수를 만드는데 신경을 써야 하고 또한 만들어진 함수는 잘 관리해서 나중에 동일한 코드를 만드느라 시간을 낭비하는 일이 없도록 해야 합니다.



02. 쉘 프로그래밍

지금까지 쉘 프로그래밍을 위한 문법부분을 공부했습니다. 문법부분을 공부하면서 프로그램을 작성하기 위한 방법과 해당 예제들을 살펴보았습니다. 이번에는 1절에서 배운 내용들을 조금 더 심도있게 배우면서 도움이 될만한 명령어 등도 함께 익혀 보도록 하겠습니다.



AWK 사용


쉘 프로그래밍을 하기 위해 코딩을 하면서 아주 유용하게 사용할 수 있는 구문이 awk 구문입니다. awk 구문이 사용된 예제가 이미 앞에서 한번 등장했었는데 바로 다음의 예제였습니다. 쉘 프로그래밍을 소개하면서 가장 첫번째 등장한 예제입니다.


#!/bin/sh

usage=`df -k $1 | /bin/awk '{ rem = 0 } { n += 1 } { a = $3 } { b = $4 } \
n == 2 { rem = int(a/(a+b) * 100); print rem} \
END { }'`

if [ $usage -ge 90 ]
then
    echo "DISK($usage) - 심각한 상태"
elif [ $usage -ge 70 ]
then
    echo "DISK($usage) - 주의요망 상태"
else
    echo "DISK($usage) - 양호한 상태"
fi


위의 프로그램을 보면 'df -k $1' 명령을 수행하고 결과로 나온 메시지를 awk 명령어에게 넘겼습니다. 그리고 awk 명령어가 그 메시지를 이용하여 각종 계산을 수행한 후에 최종 결과 값을 usage라는 변수에 할당했습니다. 그런 다음 if 문에서 usage에 할당된 값에 따라 화면 출력을 달리하고 있습니다.


위의 프로그램을 보면 'usage=~'라고 나온 부분이 있습니다. 이렇게 변수에 값을 할당하면 변수에는 ~ 속에 문장이 할당되는 것이 아니고 ~ 문장이 수행되고 난 뒤의 결과가 할당됩니다.


예를 들어 다음과 같은 문장의 경우


data=`date`


라고 했을 때, data라는 변수에는 "date"라는 문장이 할당되는 것이 아니고 date 명령어가 수행되고 난 뒤의 결과물이 할당됩니다.


즉 아래와 같은 문장을 수행하면,


echo $data


결과는 다음과 같습니다.


2003년 11월 21일 금요일 오전 12시 23분 22초


이러한 것을 "명령어 대치"라고 부릅니다.



여기서 주목하고자 하는 부분은 awk가 사용된 문장입니다. 먼저 간단히 위의 문장을 설명한 후 awk 명령어의 사용법에 대해 자세히 알아보도록 하겠습니다.


예문을 보면 awk를 사용하면서 가장 먼저 rem이라는 변수를 0으로 초기화한 것을 볼 수 있습니다. 그런 다음 df -k에서 출력된 문장을 한 라인씩 읽으면서 3번째 단어와 4번째 단어를 각각 a와 b에 할당을 합니다. 그러다 읽어들인 문장이 문장이 두 번째 문장이면 디스크 사용량을 백분율로 환산하기 위해 "int(a/(a+b)*100)"이라는 연산을 실행합니다. 연산이 끝나면 그 결과 값을 rem 변수에 할당하고 print 합니다.


"df -k $1" 명령을 실행하면 첫번째 줄에는 설명문이 나오고 두번째 문장에는 $1 인수로 들어온 디스크에 대한 정보가 나오는데 3번째와 4번째에 나오는 값은 각각 이미 사용된 양과 사용 가능한 양이 나옵니다.


AWK라는 이름은 개발자들의 이름에서 첫글자를 모아서 만들어졌습니다. 개발자들은 Aho, Weinberger 그리고 Kernighan 이었는데 이들은 패턴 조작 및 연산을 효과적으로 수행할 툴킷을 만들기 위해 노력했고 그 결과물이 바로 AWK입니다. AWK는 명령수행 결과나 파일속의 데이터 내용을 한 라인씩 읽어 들이는 기능과 라인속의 각 단어들을 끊어내고 조작 및 연산할 수 있는 기능등을 제공하고 있습니다.


이러한 기능들로 인해 일반 자료들을 원하는 형태로 가공하여 정보화하는 것에 많은 도움을 받을 수 있습니다. 많은 프로그래머들이 AWK를 이용하여 쉘 프로그램을 작성하는 이유가 바로 여기에 있습니다. 그러면 AWK의 문법 형식부터 살펴보도록 하겠습니다.


AWK는 다음과 같은 형식으로 사용이 됩니다.


파일에서 데이터를 얻어오려면


awk '패턴 {실행문} 패턴 {실행문} ... 패턴 {실행문}' 파일명


파일이 아닌 명령 수행 결과에서 원하는 데이터를 추출하려면 다음과 같이 합니다.


명령수행 | awk '패턴 {실행문} 패턴 {실행문} ... 패턴 {실행문}'


만일 연속된 "패턴 {실행문}" 구문을 여러 라인으로 바꾸려면 다음과 같이 합니다.


패턴 {실행문} \

패턴 {실행문} \

패턴 {실행문} ...


awk에서 사용할 구문을 파일속에 작성해 둔 뒤 awk에서 해당 파일을 사용할 수도 있습니다. 이럴 때는 '-f' 옵션을 준 뒤 명령어가 작성된 파일을 나열해주면 됩니다. 이 기능을 활용하면 마치 함수를 호출해서 사용하는 것과 같은 효과를 얻을 수 있습니다. 즉, 많이 사용될 내용을 파일로 미리 작성해둔 뒤, 필요할 때 해당 파일을 붙여서 사용하면 됩니다.


예를 들어 다음의 문장으로 된 'func1'이라는 파일을 만들었다고 해보겠습니다.


{ printf $1 }


그런 다음, 다음과 같이 실행해주면 됩니다.


awk -f func1 disUsage.sh


그러면 diskUsage.sh 파일 속에 있는 각 라인의 문장들 중, 첫 번째 단어들이 화면에 출력됩니다. 패턴에 들어갈 수 있는 키워드로는 "BEGIN"과 "END" 그리고 "각종 수식"이 들어갈 수 있습니다. 이때 "BEGIN"은 데이터를 읽기 전에 실행하는 문장을 정의할 때 사용합니다. "END" 키워드는 awk가 모든 데이터를 읽은 후에 실행하는 문장을 정의할 때 사용합니다. 그리고 "각종 수식"의 경우, 일반 조건문처럼 수식을 실행한 후 결과가 제로(0)나 널(Null)이 아닐 때 이어서 나오는 문장을 실행하게 됩니다.


수식이나 실행문에는 C 언어 등에서 제공하는 각종 연산자를 대부분 활용할 수 있습니다. 예를 들어, +=, -=, *=, ++, -, ||, &&, ==, <=, != 등의 연산자도 사용할 수 있습니다. 그리고 awk 내부에서도 조건문과 반복문을 그대로 적용할 수 있습니다. 이러한 기능들 때문에 awk 자체를 하나의 작은 개발도구로 인식하기도 합니다.