본문 바로가기
Linux_Shell

[쉘프로그래밍] 배열, 정규표현식, grep

by heybrro 2017. 8. 10.

p347 배열

배열명=(원소1 원소2 원소3)

배열선언시 소괄호()를 사용해야하고, 안에 원소 나열시 공백으로 구분한다


${#배열명[*]}  : 배열의 길이

${배열명[$i]}  : 배열의 i번째값

--스크립트

[user@localhost ~]$ cat array.sh

#!/bin/bash

book=(김태용 인연 운명)


book[3]="test"

i=0

while [ $i -lt ${#book[*]} ]

do

echo ${book[$i]}

let i=i+1

done


--실행결과

[user@localhost ~]$ ./array.sh 

김태용

인연

운명

test


선언한 함수를 제거하고싶다면?

unset -f 함수이름


p354. 입출력 리다이렉션 


fork() 와 exec() 차이?

- fork 는 자식쉘을 복사하여 실행. 자식이 작업을 수행하고, 작업이 끝나면 자식은 종료됨

- exec은 현재 로그인쉘의 세그먼트가 ls로 교체되어 수행 후 종료된다. (즉, 자식이 아니라 내가 수행함)

ex)

$ exec ls


p356. exec 사용용도

예)

-- 스크립트 

[user@localhost ~]$ cat mylog.sh

#!/bin/bash

exec 3>mylog     <--파일디스크립터 3번을 정의함, (3번 파일디스크립터를 정의하는데 입력은 mylog 파일로부터 받는다)

echo "exec test start"


who >&3 <-- (who  결과를 3번 파일디스크립터 - 즉 my log - 로 전달)

date >&3         <--  (date  결과도 3번 파일디스크립터 - 즉 my log - 로 전달)

echo "exec end" >&3    <--  (echo 결과도 3번 파일디스크립터 - 즉 my log - 로 전달)


-- 실행결과

[user@localhost ~]$ ./mylog.sh 

exec test start

[user@localhost ~]$ cat mylog

user     tty1         2017-06-22 09:05 (:0)

user     pts/0        2017-06-22 09:31 (:0.0)

user     pts/1        2017-06-22 09:13 (:0.0)

2017. 06. 22. (목) 09:37:46 KST

exec end



Here Documents 에서 쓰이는 특별한 형태  : << 

- 명령창에서는 '입력이 끝난다는 의미로' crtl+D 키를 사용할 수 있으나 (EOF의미)

- 스크립트(*.sh) 에서는 위 키를 사용할 수 없다. 이를 표현하려면   <<SORTEND 와 같이 사용. 여기서 SORTEND는 다른 문자여도 상관없으며 << 가 사용된다는 것이 중요

<<SORTEND  (내용 시작을 의미)

..내용..

 SORTEND (내용 끝을 의미, SORTEND 앞에는 절대 공백이나 탭키를 넣지 않음) 

--스크립트 ( 홈폴더 아래에 있는 사용자명을 읽어온 후 각 사용자에게 hello 라는 내용으로 메일쓰기)--

[user@localhost ~]$ cat mail.sh 

#!/bin/bash

home=$(ls /home)


for homedir in $home

do

username=$homedir

echo $username

mail -s "here doc test" $username <<MAILEND         (mail의 -s 옵션은 제목을 지정함을 의미)

hello

MAILEND    <----절대 앞에 탭키를 넣으면안됨

done


--실행결과-------------------------------------

[user@localhost ~]$ ssh test@localhost     (test 계정으로 로그인 후 메일 확인)

[test@localhost ~]$ mail

Heirloom Mail version 12.4 7/29/08.  Type ? for help.

"/var/spool/mail/test": 4 messages 3 new

    1 user                  Wed Jun 21 10:26  20/629   "hi"

>N  2 user                  Thu Jun 22 09:38  19/620   "mail test"

 N  3 user                  Thu Jun 22 09:51  17/592   

 N  4 user                  Thu Jun 22 09:52  18/615   "here doc test"      (메일이 와있음)



3장. 정규표현식 - 문자열 패턴을 처리하고자할때 사용하는 표현

^        :한 줄의 시작

$        : 한 줄의 끝

[]       : 집합

[^ ]    : not 집합

.        : 임의의 한 문자   (그냥 점이어도 정규표현식으로 고려될 수 있으므로, 문자열로 표현하고자할땐  \.  으로 표현해야 하는 경우가 있다)

 아래 애들은 앞에 반드시 임의의 어떤 문자( ㅁ으로 표현)가 붙는다.

ㅁ*    :  ex) 호호* : '호호'가 0번이상 매치. 0번 이상이라는 점을 기억하라 

             => *은 별로 쓰지 않는게 좋다. 정규표현식에서와 다른부분에서 쓰는 의미가 다르므로 실수할수있다.

ㅁ+    : 앞에 임의의 문가가 옴. 

            ex) 호호+ : '호호'가 1번이상 매치

ㅁ{n}   : 갯수를 정확하게 n번으로 지정

ㅁ{n,}  : 갯수를 정확하게 n이상으로 지정

ㅁ{m1,m2}   : m1번 이상 m2번 이하

 

문자열 패턴을 사용하는 명령어  grep, sed, awk, vi  등에서 문자열 패턴 정의시 정규표현식이 사용가능하다.


4장. grep  

한 줄 마다 읽어와서 문자열패턴이 있는지 조사. 일치하면 출력, 일치안하면 버림

$grep 옵션 문자열패턴 대상파일명


옵션

-l (소문자 엘) : 파일명만 추출하고 싶은 경우. 이 옵션이 설정된 경우, 문자열패턴이 포함되어있는 파일이름만 추출

-E : 확장버전의 정규표현식 문자를 쓰고싶을때

-e : 정규표현식쓰고싶을때

-i : 대소문자 무시하고 찾기

-v : 문자열패턴과 일치하지 않는 줄만 출력

[user@localhost ~]$ grep user /etc/passwd

usbmuxd:x:113:113:usbmuxd user:/:/sbin/nologin

saslauth:x:498:76:Saslauthd user:/var/empty/saslauth:/sbin/nologin

user:x:500:500:user:/home/user:/bin/bash


- root로 시작하는 줄만 찾고싶을때 - 정규표현식 중 ^을 사용한다

[user@localhost ~]$ grep ^root: /etc/passwd

root:x:0:0:root:/root:/bin/bash


user로 시작하는 라인만 찾고싶을때

[user@localhost ~]$ grep ^user /etc/passwd

user:x:500:500:user:/home/user:/bin/bash


- bash로 끝나는 줄만 출력

[user@localhost ~]$ grep bash$ /etc/passwd

root:x:0:0:root:/root:/bin/bash

user:x:500:500:user:/home/user:/bin/bash

test:x:501:501::/home/test:/bin/bash

snoopy:x:502:502::/home/snoopy:/bin/bash


-v옵션을 넣으면 조건과 일치하지않는 줄만 출력

[user@localhost ~]$ grep -v bash$ /etc/passwd

bin:x:1:1:bin:/bin:/sbin/nologin

daemon:x:2:2:daemon:/sbin:/sbin/nologin

adm:x:3:4:adm:/var/adm:/sbin/nologin

lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin

(중략)


- r또는 u로 시작하는 것만찾아라

[user@localhost ~]$ grep ^[ru] /etc/passwd

root:x:0:0:root:/root:/bin/bash

uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin

usbmuxd:x:113:113:usbmuxd user:/:/sbin/nologin

rtkit:x:499:497:RealtimeKit:/proc:/sbin/nologin

user:x:500:500:user:/home/user:/bin/bash


- a부터 n 사이 알파벳으로 시작하는 것만 찾아라

[user@localhost ~]$ grep ^[a-n] /etc/passwd

bin:x:1:1:bin:/bin:/sbin/nologin

daemon:x:2:2:daemon:/sbin:/sbin/nologin

adm:x:3:4:adm:/var/adm:/sbin/nologin

lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin

halt:x:7:0:halt:/sbin:/sbin/halt

(중략)


- a부터 n에 사이에 해당되지않는것. 정규식에서 not을 표현하려면 ^ 이다. 느낌표를 사용하면 안됨

[user@localhost ~]$ grep ^[^a-n] /etc/passwd       <---앞의 ^ 는 시작글자를 의미, 뒤에 ^는 not을 표현함을 유의

root:x:0:0:root:/root:/bin/bash

sync:x:5:0:sync:/sbin:/bin/sync

shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown

uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin

operator:x:11:0:operator:/root:/sbin/nologin

(중략)


-  "네자리문자열:" 로 시작하고  두번째 자리가 o 인 행을 출력

[user@localhost ~]$ grep ^.o..: /etc/passwd

root:x:0:0:root:/root:/bin/bash


(유의) 패턴쓸때 홀따옴표 사용하고 역슬래시포함해야함


o가 연속해서 두번이상나오는패턴

[user@localhost ~]$ grep 'o\{2,\}' /etc/passwd      <---- o{2} 로 쓰면 문자로 인식하기 때문에 특수문자앞에 역슬레시 추가 후 홀따옴표로 묶어줌 

root:x:0:0:root:/root:/bin/bash

lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin

mail:x:8:12:mail:/var/spool/mail:/sbin/nologin

uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin

operator:x:11:0:operator:/root:/sbin/nologin

postfix:x:89:89::/var/spool/postfix:/sbin/nologin

snoopy:x:502:502::/home/snoopy:/bin/bash


- o가 한글자라도 들어가있는 것을 출력

[user@localhost ~]$ grep 'o\+' /etc/passwd    <-- o+ 로 쓰면 문자로 인식하므로 특수문자 앞에 역슬래시붙이고 홀따옴표로 묶어줌

root:x:0:0:root:/root:/bin/bash

bin:x:1:1:bin:/bin:/sbin/nologin

daemon:x:2:2:daemon:/sbin:/sbin/nologin

adm:x:3:4:adm:/var/adm:/sbin/nologin

(중략)


- 대소문자구분없이 포함된 줄을 모두 출력하려면 -i 옵션

[user@localhost ~]$ grep -i linux /etc/*.conf

/etc/dnsmasq.conf:# DHCP vendorclass string includes the substring "Linux"

/etc/dnsmasq.conf:#dhcp-vendorclass=red,Linux

/etc/dnsmasq.conf:# Send options to PXELinux. Note that we need to send the options even

/etc/dnsmasq.conf:# See http://syslinux.zytor.com/pxe.php#special for details.

/etc/dnsmasq.conf:#dhcp-option-force=210,/tftpboot/pxelinux/files/

/etc/dnsmasq.conf:#dhcp-boot=pxelinux.0

(중략)


- 파일명만 출력하려면 

[user@localhost ~]$ grep -il linux /etc/*.conf     <-- 소문자 엘 옵션추가

/etc/dnsmasq.conf

grep: /etc/grub.conf: 허가 거부

grep: /etc/libaudit.conf: 허가 거부

/etc/mtools.conf

(중략)


ps 명령어에 대해 알아보기

- 프로세스 실행자명을 알려면 f 옵션, 내가실행한것뿐만아니라 시스템의 실행중인 모든 프로셋 확인하려면 e 옵션

[user@localhost ~]$ ps -ef | grep crond

root     28223     1  0 02:34 ?        00:00:01 crond

user     33760 32938  0 10:49 pts/1    00:00:00 grep crond


- 위 결과에서 grep crond 만 제외하고 싶다면? 

[user@localhost ~]$ ps -ef | grep crond | grep -v grep

root     28223     1  0 02:34 ?        00:00:01 crond


[user@localhost ~]$ ps -ef | grep test

user     33784 33015  0 10:50 pts/0    00:00:00 ssh test@localhost

root     33785  2237  1 10:50 ?        00:00:00 sshd: test [priv]

test     33790 33785  0 10:50 ?        00:00:00 sshd: test@pts/2 

test     33791 33790  0 10:50 pts/2    00:00:00 -bash

user     33809 32938  0 10:50 pts/1    00:00:00 grep test

- 위 결과에서 test 계정꺼만 보고싶을때는?

[user@localhost ~]$ ps -ef | grep ^test

test     33790 33785  0 10:50 ?        00:00:00 sshd: test@pts/2 

test     33791 33790  0 10:50 pts/2    00:00:00 -bash


sed  : 파일을 열지않고 편집하는 도구

한 줄을 읽고 패턴에 해당되는 내용이면 패턴처리후 출력, 패턴에 포함되지않은애들은 그대로 출력

사용방법

(1) 파일명을 입력하는 경우

sed '패턴 명령' 파일명 > 리다일렉트할새로운파일명


-패턴지정시 두가지방법

(1) /문자열패턴/  ex) sed '/문자열패턴/ 명령' 파일명  

(2) 줄번호          ex) sed '줄번호 명령' 파일명  <--특정 줄에대해 수행


2)  명령어의 수행결과로 편집하는경우

명령어 | sed



- 명령

s : 교체 ex) s/원본단어/바꿀단어/       <-- 맨뒤에 / 슬러시까지 꼭 붙여야함

d : 삭제

A :

a  : \뒤로어펜드


시작라인번호,끝번호

:1,$ s/정규표현식/문자열

중간에 정규표현식을 쓸수있는데, 만약 문자. 을 표현하고자할때는  \. (역슬래시 점을 찍어야한다)


 root로 시작하는 줄이라면 삭제하라

[user@localhost ~]$ sed '/^root/ d' /etc/passwd > noroot

[user@localhost ~]$ head noroot

bin:x:1:1:bin:/bin:/sbin/nologin

daemon:x:2:2:daemon:/sbin:/sbin/nologin

adm:x:3:4:adm:/var/adm:/sbin/nologin

lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin

sync:x:5:0:sync:/sbin:/bin/sync

shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown

halt:x:7:0:halt:/sbin:/sbin/halt

mail:x:8:12:mail:/var/spool/mail:/sbin/nologin

uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin

operator:x:11:0:operator:/root:/sbin/nologin


- root로 시작하는 줄이면 그 안에 bash 키워드를 ksh 로 변경하라

[user@localhost ~]$ sed '/^root/ s/bash/ksh/' /etc/passwd >noroot

[user@localhost ~]$ head noroot 

root:x:0:0:root:/root:/bin/ksh

bin:x:1:1:bin:/bin:/sbin/nologin

daemon:x:2:2:daemon:/sbin:/sbin/nologin

adm:x:3:4:adm:/var/adm:/sbin/nologin

(중략)


- 맨마지막줄($)에 대해 bash를 ksh로 변경

[user@localhost ~]$ sed '$ s/bash/ksh/' /etc/passwd >noroot

[user@localhost ~]$ tail noroot

ntp:x:38:38::/etc/ntp:/sbin/nologin

apache:x:48:48:Apache:/var/www:/sbin/nologin

saslauth:x:498:76:Saslauthd user:/var/empty/saslauth:/sbin/nologin

postfix:x:89:89::/var/spool/postfix:/sbin/nologin

(중략)


- 첫번째줄에서 마지막줄까지 bash 문자열을 ksh 로 변경

[user@localhost ~]$ sed '1,$ s/bash/ksh/' /etc/passwd >noroot

[user@localhost ~]$ grep bash noroot

[user@localhost ~]$ grep ksh noroot

root:x:0:0:root:/root:/bin/ksh

user:x:500:500:user:/home/user:/bin/ksh

test:x:501:501::/home/test:/bin/ksh

snoopy:x:502:502::/home/snoopy:/bin/ksh


- 현재 디렉토리에 있는 파일에 user를 test 로 바꾸기 (모든 user문자열을 바꾸는게 아니라 처음에 발견한 문자열을 1회에 한하여 변경하는듯하다)

[user@localhost ~]$ ls -l *.sh | sed '1,$ s/user/test/'

-rwxr-xr-x. 1 test user 130 2017-06-22 09:24 array.sh

-rwxr-xr-x. 1 test user 153 2017-06-22 10:21 homecheck.sh

-rwxr-xr-x. 1 test user 343 2017-06-22 11:08 logincheck.sh

-rwxr-xr-x. 1 test user 183 2017-06-22 10:10 mail.sh

-rwxr-xr-x. 1 test user  86 2017-06-22 09:37 mylog.sh

--스크립트 (현재 폴더에 있는 쉘 스크립트의 확장자를 txt 로 변경)

for filename in `echo *.sh`

do

        echo $filename | sed '1,$ s/\.sh/.txt/'

done


- 실행결과

[user@localhost ~]$ echo *.sh

array.sh homecheck.sh logincheck.sh mail.sh mylog.sh

[user@localhost ~]$ ./movefile 

array.txt

homecheck.txt

logincheck.txt

mail.txt

mylog.txt



awk : 필드단위로 제어가능

파일명 줄단위로 읽어와서 패턴과 일치하면 액션수행

중괄호(액션정의)를 꼭 써야함

sed보다 훨씬 다양하게 패턴정의 가능

awk '패턴정의 {action}' 파일명 


-문자열패턴

awk '/문자열패턴/ {실행문}' 파일명

ex) awk '/^root/ {print $0}' /etc/passwd


-표현식패턴

awk '표현식 {실행문}' 파일명

ex) awk '$3>=500 {print $1}' /etc/passwd

패턴정의 방법

 /문자열패턴/

 c&자바에서쓰는 표현식  ==, ><,>=, <=

 앞 ~ 뒤 : 앞에 나온 것이 뒤에 나오는 것에 포함되어있습니까?

  앞 !~ 뒤 : 포함되어있지 않습니까?

 'BEGIN {사전처리} 패턴정의 {action} END {사후처리}'    => 괄호안에 연산가능  

ex) BEGIN 용도 : 토탈합 구하기 위한 변수초기화, 제목 사전 출력

ex) END 용도 : 작업끝났을때 그 합을 출력 


액션 정의 방법

print

if()

for

while 

+, -, *, /, % 등  C&JAVA 연산쓸수있고, sin,cos 내장함수도 쓸수있다 (man awk 참조)


변수사용가능

이중따옴표로 문자열임을 명시해야함

$1>100

$1 == "test"         (첫번째 필드 값이 "test" 문자열과 같은지 체크)

$1 == test            (test를 따옴표로 묶지 않으면 문자열로 인식하지 않음. 변수처리함) 


- 예약된 변수

$0 :전체(한줄모두) 프린트

$1 :한줄에서 첫번째필드

$2 :한줄에서 두번째필드


리눅스에서 산술연산(소수점까지)이 필요한경우 awk를 사용하면된다.


필드구분자를 별도로 정의하고싶은 경우 -F 옵션 사용

예) :를 구분자로 사용하고싶다면 -F:   (띄어쓰기 없이 붙여서 사용)

- root문자열로 시작하는 경우 그 줄을 출력

[user@localhost ~]$ awk '/^root/ { print $0 } ' /etc/passwd

root:x:0:0:root:/root:/bin/bash


-구분자: 를 지정

[user@localhost ~]$ awk -F: '/^root/ { print $0 } ' /etc/passwd

root:x:0:0:root:/root:/bin/bash


-구분자: 를 지정하여 첫번째필드와 여섯번째 필드를 출력

[user@localhost ~]$ awk -F: '/^root/ { print $1, $6 } ' /etc/passwd

root /root


- 세번째필드가 user아이디인데 userid 가 500번 이상인 애들만 출력 

[user@localhost ~]$ awk -F: '$3>=500 { print $1, $6 } ' /etc/passwd

user /home/user

test /home/test

snoopy /home/snoopy


-BEGIN, end 패턴

- 맨처음라인에 userlist 문자열 출력, 작업완료후 ==== 문자열 출력

[user@localhost ~]$ awk -F: 'BEGIN { print "userlist" } $3 >= 500 { print $1,$6 } END { print "=======" }' /etc/passwd

userlist

user /home/user

test /home/test

snoopy /home/snoopy

=======


- 작은따옴표안에 있는것을 별도의 파일에 저장해놓고 사용할수있다.  (소문자f 옵션 사용)

[user@localhost ~]$ cat user.awk 

BEGIN { print "userlist" } 

$3 >= 500 { print $1,$6 } 

END { print "=======" }


-실행결과 

[user@localhost ~]$ awk -F: -f user.awk /etc/passwd

userlist

user /home/user

test /home/test

snoopy /home/snoopy

=======


현재로그인되어있는 사용자의 이름을 알고싶다면? (패턴을 생략하면 전체줄 다 해당된다는 뜻)

[user@localhost ~]$ who | awk '{ print $1 }'

user

user

user


중복된 이름을 쓰고싶다면

[user@localhost ~]$ who | awk '{ print $1 }' | sort | uniq

user


test계정이 새로 로그인했다면

[user@localhost ~]$ who | awk '{ print $1 }' | sort | uniq

test

user



(문제) 현재 로그인되어있는 사용자들에게 write명령을 사용하여 메시지를 보내라

[user@localhost ~]$ cat note

오늘 오후 6시에 수업이 종료됩니다

(문제에대한 답)

#!/bin/bash

users=`who | awk '{ print $1 }' | sort | uniq`

for user in $users

do

        write $user <note 2>/dev/null    <---write 명령어를 사용함. 입력값은 note에서 가져옴. 오류메시지는 /dev/null처리

done


(문제)  솔라리스에서는 pkill로 프로세스 강제종료한다. 이와 유사하게 프로세스 이름을 입력받아 kill하는 스크립트 작성하기. (단, 내가 사용하는 프로세스만 삭제)

(참고사항)

ps -e   <-- 나 말고 다른 사용자들이 수행하는 프로세스까지보는법

ps -ef  <-- 누가 이 명령을 수행하고 있는지까지알수있음

(참고사항)

이 쉘을 사용하는 나의 이름을 알아내는 방법

$whoami

$echo $USER

$id -un

(문제에 대한 답)

echo "종료할 프로세스이름"$1

#pid=$(ps -ef | grep $1 | grep -v grep | awk '$1=="user" { print $2 }')       <- "user"문자열을 명시적으로 표현 (문자열)

#pid=$(ps -ef | grep $1 | grep -v grep | grep ^$USER | awk '{ print $2 }')    <- 사전에 grep으로 분리하는 방법 (변수사용)

pid=$(ps -ef | grep $1 | grep -v grep | awk '$1=="'$USER'" { print $2 }')   <- 작은따옴표 분리하는 방법(변수사용)


echo $pid

kill $pid


--실행결과

[user@localhost ~]$ ./killproc.sh sleep

종료됨

[test@localhost ~]$ ps -ef | grep sleep | grep -v grep

user     35461 33015  0 13:48 pts/0    00:00:00 sleep 100

[test@localhost ~]$ ps -ef | grep sleep | grep -v grep | awk '$1==user { print $2 }'   (x) - 변수를 이중따옴표로 묶어야한다

[test@localhost ~]$ ps -ef | grep sleep | grep -v grep | awk '$1=="user" { print $2 }'   (o)

35461


(참고)

- 로그인할때마다 설정하고 싶을때 (내 설정파일만 적용)

~/.bashrc

~/.bash_profile

- 나 말고 모든 사용자에게 같은 설정을 적용하고싶을땐? (관리자모드에서 아래 파일에 내용 추가)

/etc/profile


awk를 사용하여 소수점연산하기

[user@localhost ~]$ fnum1=10.5

[user@localhost ~]$ fnum2=9.7


- 아래 명령어(let, expr) 들은 소수점 연산을 못한다.

[user@localhost ~]$ expr fnum1 + fun2

expr: non-numeric argument

[user@localhost ~]$ let fsum=fnum1+fnum2

bash: let: 10.5: syntax error: invalid arithmetic operator (error token is ".5")


- awk를 사용한다

[user@localhost ~]$ echo $fnum1 $fnum2 | awk '{ print $1+$2 }'      <-- 괄호 안에서 씨언어처럼 사용 

20.2


ex) 반 평균 점수구하기

--실행결과

[user@localhost ~]$ cat score.txt

86

74

100

67

89

93

90


[user@localhost ~]$ awk '{ total=total+$1;count++ } END { print total/count }' score.txt   <-두개이상의 문장나열시; 세미콜론사용 (씨언어 쓰듯이)

74.875


ex) 로그파일을 사용하여 awk시 ~ 물결 사용하기 (~ 뒷부분은 정규식)

(참고) /var/log/messages 파일내용은 아래와 같음 

May 19 14:45:49 localhost kernel: imklog 5.8.10, log source = /proc/kmsg started.

May 19 14:45:49 localhost rsyslogd: [origin software="rsyslogd" swVersion="5.8.10" x-pid="1616" x-info="http://www.rsyslog.com"] start

May 19 14:45:49 localhost kernel: Initializing cgroup subways cpuset


- /var/log/messages 파일에서 세번째필드(시:분:초)정보가 '14:앞자리0~2뒷자리0~9:' 패턴으로 표현되는 라인을 모두 출력하시오.

 [root@localhost log]# awk '$3 ~ /14:[0-2][0-9]:/' messages       <--- 액션 생략하면 모두 출력


- && 로 묶어서 여러조건 지정가능

[root@localhost log]# awk '$1=="May" && $2=="19" && $3 ~ /14:[0-2][0-9]:/' messages


나와 같은 프로세스를 하나더 만드는 기능 (fork)

- myfork.c   ------------------

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>


int main()

{

        if(fork()==0) { // child process

                sleep(3);

                system("echo CHILD"); //프로그램안에서 밖에있는 명령어 실행

                system("ps -l");

                exit (0);

        } else { // parent process

                sleep(8);

                system("echo PARENT");

                system("ps -l");

        }

        return 0;

}                                                                                         

~                                                                                                  

"myfork.c" 18L, 326C 저장 했습니다   


-컴파일

[user@localhost ~]$ gcc -o myfork myfork.c 


-실행결과

[user@localhost ~]$ ./myfork

CHILD

F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD

0 S   500 35495 32913  0  80   0 - 27154 wait   pts/2    00:00:00 bash

0 S   500 35835 35495  0  80   0 -   980 hrtime pts/2    00:00:00 myfork   (부모 프로세스)

  500 35836 35835  0  80   0 -   980 wait   pts/2    00:00:00 myfork    (자식프로세스)

0 R   500 35838 35836  0  80   0 - 27033 -      pts/2    00:00:00 ps

=> 부모와 자식이 모두 슬립상태(S 상태)


PARENT

F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD

0 S   500 35495 32913  0  80   0 - 27154 wait   pts/2    00:00:00 bash

0 S   500 35835 35495  0  80   0 -   980 wait   pts/2    00:00:00 my fork  (부모 프로세스)

1 Z   500 35836 35835  0  80   0 -     0 exit   pts/2    00:00:00 myfork <defunct>  (자식프로세스)

0 R   500 35840 35835  0  80   0 - 27033 -      pts/2    00:00:00 ps

=> 자식프로세스가 현재 좀비상태(Z 상태) : 자식프로세스가 종료된 시점에 좀비프로세스가 된다. 부모가 얘를 깨끗이 지우기전까지 좀비상태이다


 좀비프로세스를 찾아서 그 부모프로세스의 이름을 출력하는 프로그램

(참고)

- 프로세드 상태까지보고싶다면 소문자 엘(l) 옵션 사용 

[user@localhost 바탕화면]$ ps -el

F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD

4 S     0     1     0  0  80   0 -  4839 poll_s ?        00:00:04 init

1 S     0     2     0  0  80   0 -     0 kthrea ?        00:00:00 kthreadd

1 S     0     3     2  0 -40   - -     0 migrat ?        00:00:00 migration/0

1 S     0     4     2  0  80   0 -     0 ksofti ?        00:00:00 ksoftirqd/0


-- 스크립트 

[user@localhost ~]$ cat zoombiecheck.sh 

#!/bin/bash

# 좀비프로세스를 찾아서 그 부모프로세스의 이름을 출력하는 프로그램

ppid=$(ps -el | awk '$2=="Z" { print $5 }')

echo "zombie의 부모들:"$ppid

for pid in $ppid

do

echo $pid

ps -e | awk '$1=='$pid' { print $4 }'

done


-- 실행결과

[user@localhost ~]$ ./myfork

CHILD

F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD

0 S   500 35854 32913  0  80   0 - 27118 wait   pts/1    00:00:00 bash

0 S   500 36020 35854  0  80   0 -   980 hrtime pts/1    00:00:00 myfork

1 S   500 36021 36020  0  80   0 -   980 wait   pts/1    00:00:00 myfork

0 R   500 36023 36021  0  80   0 - 27034 -      pts/1    00:00:00 ps

PARENT

F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD

0 S   500 35854 32913  0  80   0 - 27118 wait   pts/1    00:00:00 bash

0 S   500 36020 35854  0  80   0 -   980 wait   pts/1    00:00:00 myfork

1 Z   500 36021 36020  0  80   0 -     0 exit   pts/1    00:00:00 myfork <defunct>

0 R   500 36045 36020  0  80   0 - 27033 -      pts/1    00:00:00 ps


[user@localhost ~]$ ./yourfork 

CHILD

F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD

0 S   500 35495 32913  0  80   0 - 27154 wait   pts/2    00:00:00 bash

0 S   500 36016 35495  0  80   0 -   980 hrtime pts/2    00:00:00 yourfork

1 S   500 36017 36016  0  80   0 -   980 wait   pts/2    00:00:00 yourfork

0 R   500 36019 36017  0  80   0 - 27034 -      pts/2    00:00:00 ps

PARENT

F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD

0 S   500 35495 32913  0  80   0 - 27154 wait   pts/2    00:00:00 bash

0 S   500 36016 35495  0  80   0 -   980 wait   pts/2    00:00:00 yourfork

1 Z   500 36017 36016  0  80   0 -     0 exit   pts/2    00:00:00 yourfork <defunct>

0 R   500 36042 36016  0  80   0 - 27034 -      pts/2    00:00:00 ps


[user@localhost ~]$ ./zoombiecheck.sh 

yourfork

myfork


fork() 를 호출하면 리턴값으로 자식프로세스의 pid를 전달받는다.

이 함수의 리턴값이 0이면 자식프로세스라는 의미이고, 값이 0이 아니면 자식pid를 전달받은 부모프로세스의 로직이다.

[user@localhost ~]$ cat myfork.c

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>


int main()

{

if(fork()==0) { // child process

sleep(3);

system("echo CHILD"); //프로그램안에서 밖에있는 명령어 실행

system("ps -l");

exit (0);

} else { // parent process

//sleep(100);

wait(NULL);   <-------- 자식이 종료될때 깨어나라는 의미, sleep대신 이 함수를 사용해도됨 

system("echo PARENT");

system("ps -l");

}

return 0;

}



(문제) /var/log  하위 파일들 중 일반파일을 크기순으로 정렬하고, 가장 큰 세개의 파일을 추출하여, 파일 이름과 크기를 추출한다.

(참고) 일반 파일의 크기를 추출하는 법 : ls -l 에서 특정 필드값 추출

(답안)

[user@localhost ~]$ ls -l /var/log | grep ^- | sort -nr -k5 | head -3 | awk 'BEGIN{ print "<filename>\t\t\t<size>"} {print $8, "\t\t\t", $5 }'

<filename> <size>

messages   437841

dracut.log   399323

vmware-tools-upgrader.log   169697


(내가 작성한 답)

[user@localhost ~]$ ls -l /var/log | awk '/^-/ ' | sort -k 5 -r | awk '{ print $5"\t"$8 }' | head -3

437047 messages

399323 dracut.log

169697 vmware-tools-upgrader.log

146876 lastlog

131081 anaconda.syslog

(중략)



(문제) 월,일,시간을 입력받아 /var/log/messages 에 해당하는 시간인 것만 뽑아서 파일로 저장하기 (저장할 파일명:스크립트를 실행한 년월일)

-- 스크립트 

[user@localhost ~]$ cat logsearch.sh 

#/bin/bash

month=(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)

#월 입력

echo "month?(1-12)"

read m

m_month=${month[${m:-6}-1]}   <-- 별다른 값이 없으면 1로 디폴드셋팅

echo $m_month

#일 입력

echo "day?"

read d

m_day=${d:-22}  <-- 별다른 값이 없으면 22로 디폴드셋팅

echo $m_day

#시 입력

echo "hour?"

read h

m_hour=${h:-00}  <-- 별다른 값이 없으면 00로 디폴드셋팅

echo $m_hour


#저장할 파일명은 실행하는 날짜로 하고싶다. "년월일.txt"

filename=`date +%Y%m%d`


#로그파일에서 위에 해당하는 시간인것만 뽑아서 파일에 저장하고싶다

#awk '$1=="'$m_month'" && $2=="'$m_day'" && $3~/"'$m_hour'":[0-5][0-9]:/' /var/log/messages >${filename}.txt  (x)  <---- $3을 ~ 연산자로 비교시 /문자열/ 정의를 사용함. 변수쓸때는 쌍따옴표를 붙이면안됨. 앞에있는 == 은 뒤에 문자열명시인 ""로 변수를 묶어줘야함

awk '$1=="'$m_month'" && $2=="'$m_day'" && $3~/'$m_hour':[0-4][0-9]/' /var/log/messages >${filename}.txt   (o)   <---- $3을 ~ 연산자로 비교시 변수에 쌍따옴표를 안붙이는게 맞음


--실행결과

[root@localhost user]# head 20170622.txt 

Jun 22 00:08:09 localhost dhclient[35146]: DHCPREQUEST on eth1 to 192.168.11.254 port 67 (xid=0x72c83712)

Jun 22 00:08:09 localhost dhclient[35146]: DHCPACK from 192.168.11.254 (xid=0x72c83712)

Jun 22 00:08:09 localhost dhclient[35146]: bound to 192.168.11.128 -- renewal in 724 seconds.

Jun 22 00:08:09 localhost NetworkManager[1927]: <info> (eth1): DHCPv4 state changed renew -> renew

Jun 22 00:08:09 localhost NetworkManager[1927]: <info>   address 192.168.11.128

(중략)



문제 (1) /var/log 디렉토리에서 최근 3일동안 수정되지 않은 일반 파일명과 크기 조사

(참고)find 명령어, 파일의 크기를 알려면 ls -l 사용

find 디렉토리 검색조건:

-name (이름지정)

-mtime 기호n days (수정시각) 

   ex) -mtime -n 3  (최근 1주일 사이에 수정된 파일)

   ex) -mtime +n 3  (최근 1주일 사이에 수정이 안된파일)

-type (파일타입)  

   ex) -type f (regular 파일을 의미)

-exec (파일을 찾아낸 후 처리할 명령을 옵션으로 같이 줄수있음)

   ex) -exec rm {} \;   <--  {} \; 란  find로 찾은 파일들을 전달한다는 의미


(정답)

[root@localhost user]# find /var/log -mtime +3 -type f -exec ls -l {} \; | awk ' { print $5"\t" $8} '

0 /var/log/spice-vdagent.log

0 /var/log/wpa_supplicant.log

100933 /var/log/anaconda.yum.log

109901 /var/log/anaconda.storage.log

0 /var/log/spooler

0 /var/log/btmp

7634 /var/log/cups/access_log

41177 /var/log/gdm/:0.log.4

841 /var/log/gdm/:0-greeter.log.4

31 /var/log/gdm/:0-slave.log.4

828 /var/log/gdm/:0-greeter.log.3

(중략)


문제 (2) 현재 수행 중인 프로세스 중 사용자 user가 수행하고 있는 프로세스의 프로세스명(CMD)와 PID조사

[user@localhost ~]$ ps -ef | grep ^user | awk '{print $2"\t"$8}'

2740 /usr/bin/pulseaudio

2747 /usr/libexec/pulse/gconf-helper

32530 /usr/bin/gnome-keyring-daemon

32540 gnome-session

32548 dbus-launch

32549 /bin/dbus-daemon

32569 /usr/libexec/gconfd-2

32576 /usr/libexec/gnome-settings-daemon

32577 seahorse-daemon

(중략)


문제 (3) c프로그램을 컴파일하는 쉘 프로그램을 작성하기. 단, 파일명이 전달되지 않을때에는 현재 디렉토리의 모든 파일을 파일명이 인자로 전달되는 경우에는 해당하는 파일만 컴파일합니다.

-조건

1. 컴파일해야할 파일은 모두 현재 디렉토리에 있는 것으로 간주한다

--스크립트

[user@localhost shell]$ cat compile.sh 

#!/bin/bash


cfiles=`ls *.c`

for file in $cfiles

do

echo $file

object=$(echo $file | sed 's/\.c//')    <----- sed 문자열패턴에 .c를 공백으로 바꾸는 명령을 주었음. 공백은 //로 표기

# object=${file%*.c}

echo $object

gcc -o $object $file

done

[user@localhost shell]$ 


--실행결과

[user@localhost shell]$ ls -al

합계 84

drwxrwxr-x.  5 user user  4096 2017-06-22 17:48 .

drwxr-xr-x. 27 user user  4096 2017-06-22 17:40 ..

-rw-------.  1 user user 12288 2017-06-22 17:48 .compile.sh.swp

drwxrwxr-x.  2 user user  4096 2017-06-20 15:44 ch01

drwxrwxr-x.  2 user user  4096 2017-06-22 10:08 ch02

drwxrwxr-x.  2 user user  4096 2017-06-22 16:06 ch03

-rwxr-xr-x.  1 user user   165 2017-06-22 17:48 compile.sh

-rwxrwxr-x.  1 user user  7013 2017-06-22 17:48 myfork

-rw-rw-r--.  1 user user   346 2017-06-22 17:40 myfork.c

-rwxrwxr-x.  1 user user  7014 2017-06-22 17:48 project

-rw-rw-r--.  1 user user   346 2017-06-22 17:40 project.c

-rwxrwxr-x.  1 user user  7013 2017-06-22 17:48 sample

-rw-rw-r--.  1 user user   346 2017-06-22 17:40 sample.c

-rwxrwxr-x.  1 user user  7011 2017-06-22 17:48 test

-rw-rw-r--.  1 user user   346 2017-06-22 17:40 test.c



댓글