더 많은 정보를 담은 리눅스 교재를 배포했습니다. 아래의 페이지에서 리눅스 교재를 받아가세요.

https://reakwon.tistory.com/233

 

리눅스 프로그래밍 note 배포

티스토리에 리눅스에 관한 내용을 두서없이 여지껏 포스팅했었데요. 저도 제 포스팅을 찾기가 어렵기도 하고 티스토리에서 코드삽입을 하게 되면 이게 일자로 쭉 쓰여져있는 x같은 현상이 생겨

reakwon.tistory.com

AWK(Aho Weinberger Kernighan)

리눅스의 어떤 다른 명령어보다 명령어 이름이 매우 직관적이지 않은 명령어입니다. 이 명령어를 개발한 사람들의 이름 약자(Aho  Weinberger Kernighan)이기 때문에 mkdir(make directory), rm(remove) 같은 의미를 축약하여 만든 명령어와는 명령어 이름이 좀 그렇습니다. 이 명령어를 읽을때는 주로 오크라고 읽습니다. AWK는 유닉스에서 개발된 스크립트 언어로 텍스트가 저장되어 있는 파일을 원하는 대로 필터링하거나 추가해주거나 기타 가공을 통해서 나온 결과를 행과 열로 출력해주는 프로그램입니다. 엄청나게 막강하고 다양한 기능을 담고 있기 때문에 여기서는 어떻게 사용하는지에 대해서만 알아보도록 하겠습니다.

그 전에 간단하게 기본 용어만 짚고 넘어가도록 합시다. 아래는 하나의 텍스트 파일에 기록된 내용을 보여주고 있습니다. 여기서 각 단어들은 공백으로 구분되어 집니다. 각 줄(line)은 레코드(Record)라고 칭합니다. 그리고 그 안에 각각의 단어들이 필드(Field)가 되겠습니다.

 

AWK에서는 레코드가 $0, 그리고 $1, ..., $N 은 필드를 나타내는 열을 나타냅니다. 우리들이 사용할 파일은 위 내용은 아니고 아래의 내용을 담고 있습니다. 

 

file : awk_test_file.txt

name    phone           birth           sex     score
reakwon 010-1234-1234   1981-01-01      M       100
sim     010-4321-4321   1999-09-09      F       88
nara    010-1010-2020   1993-12-12      M       20
yut     010-2323-2323   1988-10-10      F       59
kim     010-1234-4321   1977-07-17      M       69
nam     010-4321-7890   1996-06-20      M       75

 

awk의 기본 사용법은 패턴(pattern)액션(action)을 정의하여 입력으로 주어진 파일의 데이터를 가공하여 출력합니다. 이제부터 예를 볼 겁니다. 패턴과 액션 중 하나만 사용하여도 무관합니다.

awk pattern {action} 

 

1. 열(Column)만 출력하기

각 $1, $2, $3 ... 은 열에 대응한다고 했었죠. 그리고 $0는 레코드에 대응한다고 했습니다. 여기서 이름만 모두 출력하겠다고 한다면 아래와 같이 awk 명령을 수행하면 됩니다.

awk '{ print $1 }' ./awk_test_file.txt
name
reakwon
sim
nara
yut
kim
nam

 

여러개의 열도 출력 가능합니다.

 awk '{ print $1,$2 }' ./awk_test_file.txt
name phone
reakwon 010-1234-1234
sim 010-4321-4321
nara 010-1010-2020
yut 010-2323-2323
kim 010-1234-4321
nam 010-4321-7890

 

여기서 awk의 기본적인 action은 print이며 모든 열을 전부 출력합니다.

2. 특정 pattern이 포함된 레코드 출력

다음의 awk 명령은 rea라는 문자열이 포함된 레코드를 출력해주는 명령입니다. 

 awk '/rea/' ./awk_test_file.txt
reakwon 010-1234-1234   1981-01-01      M       100

 

3. 출력의 내용 첨가

awk는 print에 문자열을 추가하여 출력물의 내용에 문자열을 추가할 수 있습니다. 만약 이름을 명시적으로 나타내기 위해 "name : " , 그리고 휴대폰 번호를 명시적으로 나타내려고 "phone : " 를 추가해서 출력하고 싶다면 아래의 awk 명령을 사용하면 됩니다.

awk '{ print ("name : " $1, ", "  "phone : " $2) }' ./awk_test_file.txt

아래 출력에서 맨 윗줄은 무시하시면 됩니다.

name : name , phone : phone
name : reakwon , phone : 010-1234-1234
name : sim , phone : 010-4321-4321
name : nara , phone : 010-1010-2020
name : yut , phone : 010-2323-2323
name : kim , phone : 010-1234-4321
name : nam , phone : 010-4321-7890

 

4. 특정 Record를 검색하기 - if 구문

4-1. ~이상 , ~ 이하의 레코드 출력

만약 위의 파일에서 점수가 80점 이상인 사람들의 레코드를 알고 싶다면 어떻게 하면 좋을까요? 이거는 pattern을 써야할까요, action을 써야할까요? action에서는 if 구문이 존재합니다. 그래서 이렇게 사용하면 80점 이상인 record를 출력할 수 있습니다.

awk '{ if ( $5 >= 80 ) print ($0) }' ./awk_test_file.txt
name    phone           birth           sex     score
reakwon 010-1234-1234   1981-01-01      M       100
sim     010-4321-4321   1999-09-09      F       88

 

혹은 아래와 같은 구문으로도 사용할 수도 있습니다.

awk '$5 >= 80 { print $0 }' ./awk_test_file.txt

 

4-2. 남자의 데이터만 출력하기

역시 if 구문으로 지정할 수 있는데 단어 자체가 같은 것을 비교하려면 쌍따옴표(")해주면 됩니다. 

awk '{ if ( $4 == "M" ) print ($0) }' ./awk_test_file.txt
reakwon 010-1234-1234   1981-01-01      M       100
nara    010-1010-2020   1993-12-12      M       20
kim     010-1234-4321   1977-07-17      M       69
nam     010-4321-7890   1996-06-20      M       75

 

4-3. 다중 조건을 활용하여 남자이면서 80점인 레코드 출력하기

여러 조건을 걸고 싶다면 어떻게 할까요? 다른 프로그래밍 언어와 유사하게 AND(&&) 조건과 OR(||) 조건을 활용할 수 있습니다. 여기서는 위의 두 조건을 && 묶어서 사용하면 됩니다. ||을 사용하면 OR조건으로 출력됩니다.

awk '{ if ( $4 == "M" && $5 >= 80) print ($0) }' ./awk_test_file.txt
reakwon 010-1234-1234   1981-01-01      M       100

 

5. 내장 함수

awk에는 여러가지 내장함수들이 존재합니다. 단어의 길이를 알아내려면 length함수, 단어의 부분단어를 추출하려면 substr함수를 사용할 수 있습니다. 아래의 awk 명령은 이름의 길이와 이름의 3글자까지 출력을 하는 명령입니다.

 awk '{ print ("name leng : " length($1), "substr(0,3) : " substr($1,0,3)) }' ./awk_test_file.txt

name leng : 4 substr(0,3) : nam
name leng : 7 substr(0,3) : rea
name leng : 3 substr(0,3) : sim
name leng : 4 substr(0,3) : nar
name leng : 3 substr(0,3) : yut
name leng : 3 substr(0,3) : kim
name leng : 3 substr(0,3) : nam

 

이밖에도 모두 소문자로 출력하는 tolower함수, 모두 대문자로 출력하는 toupper 도 있습니다. 문자열을 쪼개는 split함수도 존재합니다. 여러 내장함수들이 있으니 필요시에 따라 사용하면 됩니다. 무엇이 있는지 어떻게 보냐구요? man 1 awk를 보시면 됩니다.

6. 반복문

반복문도 사용할 수 있습니다. C언어를 배우시는 분에게는 너무 익숙한 문법으로 사용할 수 있습니다. 아래는 단지 별의미 없이 for문의 사용법을 보여드리려 print를 2번까지 진행한 것입니다. 

awk '{
for(i=0;i<2;i++)
 print( "for loop :" i "\t" $1, $2, $3)
}' ./awk_test_file.txt
for loop :0     name phone birth
for loop :1     name phone birth
for loop :0     reakwon 010-1234-1234 1981-01-01
for loop :1     reakwon 010-1234-1234 1981-01-01
for loop :0     sim 010-4321-4321 1999-09-09
for loop :1     sim 010-4321-4321 1999-09-09
for loop :0     nara 010-1010-2020 1993-12-12
for loop :1     nara 010-1010-2020 1993-12-12
for loop :0     yut 010-2323-2323 1988-10-10
for loop :1     yut 010-2323-2323 1988-10-10
for loop :0     kim 010-1234-4321 1977-07-17
for loop :1     kim 010-1234-4321 1977-07-17
for loop :0     nam 010-4321-7890 1996-06-20
for loop :1     nam 010-4321-7890 1996-06-20

 

7. BEGIN, END pattern

BEGIN은 awk가 모든 레코드를 돌기 전에 한번 action을 수행하고 END는 모든 레코드를 다 돈 후에 마지막으로 정의한 action이 실행됩니다. 아래의 예에서 어떻게 사용되는지 살펴봅니다.

8. 변수 사용

awk 역시 언어이기 때문에 변수를 사용할 수 있습니다. 만약 우리가 위의 파일에서 총점과 평균을 구하고 싶다면 어떻게 하면 좋을까요? 레코드를 돌면서 각각의 점수를 더하면서 총점을 구하고, 총점을 사람의 수로 나누면 되지요. 그래서 구한 결과는 레코드가 끝난 마지막에 출력하면 되니 아까 이야기했던 END 패턴을 앞에 명시해줍니다. 아래의 awk가 그 명령입니다. 여기서 cnt가 -1부터 시작인 이유는 실제 레코드가 아닌 헤더가 존재하기 때문입니다. 이건 인원으로 볼 수 없죠. (name phone  birth  sex  score)

 awk '
> BEGIN {
>  sum = 0
>  cnt = -1
> }
>
> {
>  sum += $5
>  cnt++
> }
>
> END {
>  avg = sum/cnt
>  print ("sum :" sum ", average :" avg)
> }' ./awk_test_file.txt

 

BEGIN에서는 초기화가 이루어지고, END에서는 결과를 출력해줍니다. 프로그래밍 언어를 배우셨다면 cnt++이나 sum +=$5의 구문이 무엇인지 아실겁니다. 모르시는 분은 구글 서치해보세요. 어렵지 않습니다.

 

이상으로 awk에 대한 사용법과 내용을 추출하는 여러가지 예제를 보았습니다. 깊이 알면 좋지만 저는 그렇게 깊이 알아서 사용할 일은 없어 여기까지만 설명하도록 하겠습니다.

반응형
블로그 이미지

REAKWON

와나진짜

,

AWT Event

Java나 다른 언어에서 Event라는 것은 특정 사건의 발생함을 의미합니다. 사용자가 행동하는 모든 것이 프로그램에서 이벤트로 동작이 될 수 있습니다. 버튼을 마우스로 클릭하거나 키보드로 입력을 받거나, 창을 이동하거나 마우스를 단순히 이동하는 것 등이 Event라고 합니다. 그리고 그 Event가 발생했을때 어떻게 프로그램을 동작시킬지 처리하는 것을 이벤트 핸들링(Event Handling)이라고 합니다. 자바에서 Event가 발생했을때 처리가능하도록 Interface를 정의해두었는데, 그 인터페이스 이름들은 뒤에 ~Listener가 붙게 됩니다. 예를 들어 Mouse에 관한 이벤트 처리 인터페이스는 MouseListener, 그리고 윈도우 창에 관한 이벤트를 처리할 수 있는 인터페이스는 WindowListener입니다. 우리는 Event가 발생했을때 어떻게 다뤄질 수 있는지 WindowListener를 통해서 알아보도록 하겠습니다.

 

EventHandling - 이벤트 처리 방법

이벤트 핸들링 위해 ~Listener라는 인터페이스가 존재한다고 했었죠. 이것을 implements로 상속받아 어떤 이벤트를 처리할지 구현하면 됩니다. 그래서 상속받은 인터페이스가 구현이 되면 그 이벤트에 영향을 받을 Component에 추가해주면 됩니다. 대부분읜 여기서는 WindowListener를 통해서 어떻게 처리하는지 알아본다고 했었죠. 그래서 WindowListener를 implements해보고 내부를 채워보도록 합시다.

WindowHandler 클래스를 만들고 WindowListener를 implements해봅시다.

class WindowHandler implements WindowListener{
}

 

이렇게 되면 WindowHandler에 밑줄이 쫙 그어지는데, 마우스를 가져다대면 아래에 Add unimplemented methods가 생십니다. 이것을 클릭해서 모든 메소드를 구현해줍니다.

 

아래처럼 아직은 메소드가 구현이 되어있지 않은 빈 클래스입니다. 

class WindowHandler implements WindowListener{

	@Override
	public void windowOpened(WindowEvent e) {}
	@Override
	public void windowClosing(WindowEvent e) {}
	@Override
	public void windowClosed(WindowEvent e) {}
	@Override
	public void windowIconified(WindowEvent e) {}
	@Override
	public void windowDeiconified(WindowEvent e) {}
	@Override
	public void windowActivated(WindowEvent e) {}
	@Override
	public void windowDeactivated(WindowEvent e) {}
}

 

AWT의 Frame에 관해서 배우셨다면 Frame의 X버튼을 클릭해도 닫아지지 않습니다. 이유는 우리가 X버튼을 눌렸을때 발생하는 이벤트에 대해서 처리해주지 않았기 때문입니다. 이 이벤트는 이벤트에 관한 정보를 WindowEvent에 담아서  WindowListener의 windowClosing() 메소드를 호출합니다. 아래의 코드를 구현하면 프로그램이 종료됩니다.

	@Override
	public void windowClosing(WindowEvent e) {
		Window w=e.getWindow();	
		w.setVisible(false);	//frame을 invisible	
		w.dispose();			//메모리 제거
		System.exit(0);					//시스템 종료
	}

 

WindowEvent에는 이벤트가 발생한 Component를 알 수 있습니다. 여기서는 단일 Frame을 사용하기 때문에 Frame을 구분할 필요가 없지만 만약 여러 프레임을 사용해서 같은 WindowHandler를 사용한다면 getComponent()를 통해서 Frame을 구분할 수 있습니다. 이벤트 클래스는 ComponentEvent를 상속받기 때문에 WindowEvent객체 뿐만 아니라 ComponentEvent를 상속받은 모든 Event클래스는 getComponent()를 통해서 객체를 구분할 수 있습니다.

해당하는 Window객체를 getWindow() 메소드로 가져올 수 있습니다. 

이벤트 리스너가 구현이 되었다면 이제 컴포넌트에 연결을 지어야겠죠? 해당 컴포넌트의 add~Listener()  메소드가 그 역할을 합니다. 여기서는 addWindowListener() 메소드입니다.

	public static void main(String[] ar) {
		Frame frame=new Frame("reakwon");
		frame.setSize(500,300);		//크기 설정
		frame.setLocationRelativeTo(null);	//중앙위치
		
		frame.addWindowListener(new WindowHandler());	//이벤트 설정
		
		frame.setVisible(true);	//Frame visible
	}

 

그리고 난 후에 프로그램을 실행시켜보고 X버튼을 누르면 프로그램이 종료되는 것을 확인할 수 있습니다. 

 

단순히 종료시키는 것이 아니라 "정말 종료하시겠습니까?" 대화창을 열어 확인해볼 수도 있겠죠. 여러분이 응용하기 나름입니다.

이벤트 리스너를 연결하는 방법

1. Component가 Listener를 implements하여 자신을 add하는 방법

이벤트를 연결하는 것중에는 아예 이벤트 리스너를 해당 Component가 상속받는 형태로 구현할 수도 있습니다. 아래의 방식처럼 말이죠.


class MyFrame extends Frame implements WindowListener{

	public MyFrame(String title) {
		super(title);
		this.setSize(500,300);
		this.setLocationRelativeTo(null);
		this.addWindowListener(this);	//WindowListener를 이곳에서 구현
		this.setVisible(true);
	}
	@Override
	public void windowOpened(WindowEvent e) {}
	@Override
	public void windowClosing(WindowEvent e) {	
		this.setVisible(false);	//frame을 invisible	
		this.dispose();			//메모리 제거
		System.exit(0);					//시스템 종료
	}
	@Override
	public void windowClosed(WindowEvent e) {}
	@Override
	public void windowIconified(WindowEvent e) {}
	@Override
	public void windowDeiconified(WindowEvent e) {}
	@Override
	public void windowActivated(WindowEvent e) {}
	@Override
	public void windowDeactivated(WindowEvent e) {}
}




 

 

2. 익명 클래스(Anonymouse class) 방식으로 연결

또는 익명 클래스로 바로 리스너를 구현해서 집어넣는 방법으로도 이벤트 리스너 연결이 가능합니다.

class MyFrame extends Frame{

	public MyFrame(String title) {
		super(title);
		this.setSize(500,300);
		this.setLocationRelativeTo(null);
		this.addWindowListener(new WindowListener() {
			@Override
			public void windowOpened(WindowEvent e) {}
			@Override
			public void windowClosing(WindowEvent e) {
				
				MyFrame.this.setVisible(false);	//frame을 invisible	
				MyFrame.this.dispose();			//메모리 제거
				System.exit(0);					//시스템 종료
			}
			@Override
			public void windowClosed(WindowEvent e) {}
			@Override
			public void windowIconified(WindowEvent e) {}
			@Override
			public void windowDeiconified(WindowEvent e) {}
			@Override
			public void windowActivated(WindowEvent e) {}
			@Override
			public void windowDeactivated(WindowEvent e) {}
		});	
		this.setVisible(true);
	}
	
}

 

2.1익명 클래스시 자주하는 실수

1. 익명 클래스 방식으로 구현할때 주의해야할 점은 this가 WindowListener 자체를 가리키는 객체를 말합니다. 그래서 Component를 가리키게 하려면 앞에 클래스명을 명시적으로 알려줘야합니다.

2. 아래의 코드가 에러가 발생하는 이유는 무엇일까요? 

	public MyFrame(String title) {
		//...생략...//
		int count=0;
		this.addWindowListener(new WindowListener() {
		
			@Override
			public void windowClosing(WindowEvent e) {
				count++;
                //...생략...//
			}
			
		});	
	}

 

저는 분명 count가 하나 증가하기를 기대하고 코드에 넣었는데, 빨간 밑줄인 에러가 발생합니다. 마우스를 가져다 대보니 final을 쓰라고 하네요.

 

왜 일까요? 여러분은 두가지를 간과하고 계실 겁니다. 

첫번째는 코드가 순차적으로 실행될 것이라는 믿음, 그리고 두번째 지역변수는 함수가 끝나면 정리된다는 것을 까먹은 것이죠. 위 이벤트 리스너의 실행은 이벤트가 발생했을때의 수행하는 것이지, 순차적 실행이 아닙니다. 그리고 생성자가 종료된 후에는 count라는 지역변수는 메모리에 없습니다. 그래서 증가시킬 수가 없죠. 

해결방법은 지역변수의 count를 final로 선언하여 값을 변경하지 않는것이고, 값을 변경하길 원한다면 아예 클래스(여기서는 MyFrame)의 멤버 변수로 선언하는 방법입니다.

지금까지 이벤트의 개념과 이벤트 핸들링을 하는 방법을 설명했는데, 이것을 일일히 설명하는 것보다는 아예 프로그램을 만들면서 그때그때 설명하는 것이 더 기억에 많이 남고 재밌을 것 같습니다. 그래서 시간이 된다면 프로그램 자체를 만들면서 설명해보도록 하겠습니다.

반응형
블로그 이미지

REAKWON

와나진짜

,