bash에서 사용할 수 있는 간단한 스칼라 커맨드 유틸리티를 소개한다. 



scala REPL을 그대로 사용하고 간단하게 사용할 수 있다. 



#!/bin/sh


exec scala "$0" "$@"

!#


print("given parameter : " )

args.foreach(arg => print(arg + " "))

println

println("given parameter number : " + args.length)


if (args.length >= 2 || args.length < 1) {

  println("wrong parameter, only 1 parametr")

  sys.exit

}


println("Hello " + args(0) + "!")



실행한 코드는 다음과 같다. 


 ./scalahello world

given parameter : world

given parameter number : 1

Hello world!




가장 실수한 부분은 sys.exit였다. scala 실행할 때 종료하면 sys.exit하듯이 스크립트에서도 사용하면 된다. 



[~/temp] scala

Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_101).

Type in expressions for evaluation. Or try :help.


scala> sys.exit

[~/temp]





REPL은 좀 제약이 많아서 될 수 있으면 코드베이스가 더 나은 것 같긴 하지만, 필요하다면 스크립트도 괜찮은 방법일 수 있다. 


Posted by '김용환'
,


hbase 0.94에서 hbase shell을 사용해서 테이블 추가/삭제를 진행하는 예시이다.




$ hbase shell


16/12/16 14:20:08 WARN conf.Configuration: hadoop.native.lib is deprecated. Instead, use io.native.lib.available

HBase Shell; enter 'help<RETURN>' for list of supported commands.

Type "exit<RETURN>" to leave the HBase Shell

Version 0.94.15-cdh4.7.1, rUnknown, Tue Nov 18 08:51:37 PST 2014



table1을 생성한다.


> create 'table1', 'columnfamily1'

=> Hbase::Table - table1




생성된 테이블이 존재하는지 확인한다. 


> list 'table1'

TABLE

table1



table1 테이블을 삭제하기 위해 drop를 사용하려면 먼저 disable을 먼저 사용하라고 알린다. 


> drop 'table1'


ERROR: Table table1 is enabled. Disable it first.'


Here is some help for this command:

Drop the named table. Table must first be disabled: e.g. "hbase> drop 't1'"




table1 테이블을 삭제하기 위해 disable과 drop을 실행한다.



> disable 'table1'

0 row(s) in 1.1150 seconds


> drop 'table1'

0 row(s) in 1.0580 seconds


> list 'table1'

TABLE

0 row(s) in 0.0230 seconds





데이터를 테이블에 저장하는 예시를 진행한다.



먼저 간단한 google 테이블을 생성한다. 테이블의 이름은 google이고, 컬럼패밀리는 vi 이다. 



> create 'google', 'vi'

0 row(s) in 1.0600 seconds

=> Hbase::Table - google



테이블에 row를 추가하기 위해 put을 사용한다. 



> put 'google', 'row1', 'vi:make', '1'

0 row(s) in 0.0360 seconds


> put 'google', 'row2', 'vi:make', '2'

0 row(s) in 0.0060 seconds


> put 'google', 'row3', 'vi:make', '3'

0 row(s) in 0.0050 seconds


> put 'google', 'row1', 'vi:get', '1'

0 row(s) in 0.0030 seconds


> put 'google', 'row2', 'vi:get', '2'

0 row(s) in 0.0030 seconds


> put 'google', 'row3', 'vi:get', '3'

0 row(s) in 0.0030 seconds





저장한 데이터가 존재하는지 scan으로 확인한다. 



> scan 'google'

ROW                              COLUMN+CELL

 row1                            column=vi:get, timestamp=1481865972730, value=1

 row1                            column=vi:make, timestamp=1481865868177, value=1

 row2                            column=vi:get, timestamp=1481865976807, value=2

 row2                            column=vi:make, timestamp=1481865882636, value=2

 row3                            column=vi:get, timestamp=1481865981007, value=3

 row3                            column=vi:make, timestamp=1481865891477, value=3

3 row(s) in 0.0410 seconds



특정 컬럼 패밀리만 보고 싶다면, scan에 COLUMNS를 추가한다. 


> scan 'google', {COLUMNS => ['vi:make']}

ROW                              COLUMN+CELL

 row1                            column=vi:make, timestamp=1481865868177, value=1

 row2                            column=vi:make, timestamp=1481865882636, value=2

 row3                            column=vi:make, timestamp=1481865891477, value=3

3 row(s) in 0.0850 seconds



해당 커맨드에 LIMIT을 이용해 개수를 지정해서 볼 수 있다.



> scan 'google', {COLUMNS => ['vi:make'], LIMIT => 100}

ROW                              COLUMN+CELL

 row1                            column=vi:make, timestamp=1481865868177, value=1

 row2                            column=vi:make, timestamp=1481865882636, value=2

 row3                            column=vi:make, timestamp=1481865891477, value=3

3 row(s) in 0.0170 seconds


> scan 'google', {COLUMNS => ['vi:make'], LIMIT => 2}

ROW                              COLUMN+CELL

 row1                            column=vi:make, timestamp=1481865868177, value=1

 row2                            column=vi:make, timestamp=1481865882636, value=2

2 row(s) in 0.0090 seconds


> scan 'google', {COLUMNS => ['vi:make'], LIMIT => 1}

ROW                              COLUMN+CELL

 row1                            column=vi:make, timestamp=1481865868177, value=1

1 row(s) in 0.0050 seconds




데이터를 얻으려면 get을 사용하는데, 테이블과 로우 키가 중요하다. 테이블과 컬럼패밀리로 검색하면 값을 얻을 수 없다.


> get 'google', 'vi:make'

COLUMN                           CELL

0 row(s) in 0.0110 seconds



테이블과 로우 키로 데이터를 검색한다. 테이블-로우키로 검색 가능하고, 테이블-로우키-컬럼패밀리로 검색할 수 있다.



> get 'google', 'row1'

COLUMN                                        CELL

 vi:get                                       timestamp=1481865972730, value=1

 vi:make                                      timestamp=1481865868177, value=1

2 row(s) in 0.0340 seconds


> get 'google', 'row1', 'vi:get'

COLUMN                                        CELL

 vi:get                                       timestamp=1481865972730, value=1

1 row(s) in 0.0080 seconds




scan 처럼 get도 COLUMN 단위로 검색할 수 있다. (FILTER, TIMERANGE로도 검색할 수 있다)



> get 'google', 'row1', {COLUMN => [ 'vi:make', 'vi:get' ] }

COLUMN                                        CELL

 vi:get                                       timestamp=1481865972730, value=1

 vi:make                                      timestamp=1481865868177, value=1

2 row(s) in 0.0120 seconds


> get 'google', 'row1', {COLUMN => [ 'vi:get' ] }

COLUMN                                        CELL

 vi:get                                       timestamp=1481865972730, value=1

1 row(s) in 0.0050 seconds





삭제하려면 delete를 사용한다.



> scan 'google'

ROW                              COLUMN+CELL

 row1                            column=vi:get, timestamp=1481865972730, value=1

 row1                            column=vi:make, timestamp=1481865868177, value=1

 row2                            column=vi:get, timestamp=1481865976807, value=2

 row2                            column=vi:make, timestamp=1481865882636, value=2

 row3                            column=vi:get, timestamp=1481865981007, value=3

 row3                            column=vi:make, timestamp=1481865891477, value=3




> delete 'google', 'row1', 'vi:make'

0 row(s) in 0.0120 seconds



scan 해보면 데이터가 삭제된 것을 확인할 수 있다. 



> scan 'google'

ROW                          COLUMN+CELL

 row1                        column=vi:get, timestamp=1481865972730, value=1

 row2                        column=vi:get, timestamp=1481865976807, value=2

 row2                        column=vi:make, timestamp=1481865882636, value=2

 row3                        column=vi:get, timestamp=1481865981007, value=3

 row3                        column=vi:make, timestamp=1481865891477, value=3

3 row(s) in 0.0350 seconds




delete할 때는 테이블-로우키-컬럼패밀리에 맞춰 지워야 한다. delete 테이블-로우키, delete 테이블을 실행시 에러가 발생한다.


> delete 'google', 'row2'

//에러 

> delete 'google'

// 에러





데이터 변경(update)는 put을 그대로 사용한다. 



> get 'google', 'row1'

COLUMN                       CELL

 vi:get                      timestamp=1481865972730, value=1

1 row(s) in 0.0080 seconds



> put 'google', 'row1', 'vi:get', 3

0 row(s) in 0.0120 seconds



> get 'google', 'row1'

COLUMN                       CELL

 vi:get                      timestamp=1481868410482, value=3

1 row(s) in 0.0040 seconds




테이블 삭제하려면, disable -> drop 테이블 과정을 거친다.


여기서는 disable과 enable 테이블을 시도해본 후 drop 테이블을 실행한다.



hbase(main):023:0> disable 'google'

0 row(s) in 1.1310 seconds


disable했기 때문에 scan이나 get하면 DoNotRetryIOException 예외가 발생한다. 


hbase(main):024:0> scan 'google'

ROW                          COLUMN+CELL

ERROR: org.apache.hadoop.hbase.DoNotRetryIOException: google is disabled.

hbase(main):025:0> get 'google', 'row1'
COLUMN                       CELL

ERROR: org.apache.hadoop.hbase.DoNotRetryIOException: google is disabled.


하지만, exists를 실행하면 아직 존재하는지 알 수 있다. 기타 list, decribe를 사용시 제대로 동작 중인지 확인할 수 있다. 


> exists 'google'

Table google does exist

0 row(s) in 0.0250 seconds



> describe 'google'

DESCRIPTION                                                             ENABLED

 'google', {NAME => 'vi', DATA_BLOCK_ENCODING => 'NONE', BLOOMFILTER => false

  'NONE', REPLICATION_SCOPE => '0', VERSIONS => '3', COMPRESSION => 'NO

 NE', MIN_VERSIONS => '0', TTL => '2147483647', KEEP_DELETED_CELLS => '

 false', BLOCKSIZE => '65536', IN_MEMORY => 'false', ENCODE_ON_DISK =>

 'true', BLOCKCACHE => 'true'}

1 row(s) in 0.0460 seconds



> list 'google'

TABLE

google

1 row(s) in 0.0230 seconds



disable 했던 테이블을 다시 사용하려면 enable을 호출한다. 


> enable 'google'

0 row(s) in 1.1450 seconds



> scan 'google'

ROW                          COLUMN+CELL

 row1                        column=vi:get, timestamp=1481868410482, value=3

 row2                        column=vi:get, timestamp=1481865976807, value=2

 row2                        column=vi:make, timestamp=1481865882636, value=2

 row3                        column=vi:get, timestamp=1481865981007, value=3

 row3                        column=vi:make, timestamp=1481865891477, value=3

3 row(s) in 0.0210 seconds




google 테이블을 삭제한다. 



> disable 'google'

0 row(s) in 1.1450 seconds


> drop 'google'

0 row(s) in 1.0570 seconds


Posted by '김용환'
,


hbase 0.94를 사용하고 있다. 


(아주 당연하지만..)

hbase 커맨드 쉘에서는 quote(')의 사용이 반드시 필요한데, 이상하게도 나는 describe 또는 list를 사용할 때면 quote를 사용하지 않으려 하는 습관이 있다. mysql 영향을 받아서 그런 것 같다.



subscribe/list 커맨드를 사용할 때, quote(')를 사용하지 않으면 찾지 못한다. 항상 quote를 사용해야 한다!!!





hbase(main):002:0> list notifications

NameError: undefined local variable or method `notifications' for #<Object:0x3e4eede1>




hbase(main):003:0> list 'notifications'


TABLE

notifications

feed_notifications


2 row(s) in 0.0240 seconds





hbase(main):004:0> list 'not*'


TABLE

notifications

feed_notifications


2 row(s) in 0.0240 seconds



Posted by '김용환'
,



standalone hbase(0.94)에서 table create하다가 hang 발생한 경우를 해결한 사례이다.


create 'xxxx', { NAME => 'c', BLOOMFILTER => 'ROWCOL', VERSIONS => '1', COMPRESSION => 'GZ', TTL => '-1', BLOCKCACHE => 'false'}

(hang)



/var/log/hbase에서 다음 로그가 남겨졌다.



ERROR org.apache.hadoop.hbase.master.handler.CreateTableHandler: Error trying to create the table nfeeds

java.io.IOException: Exception in renameDirForFileSystem

        at org.apache.hadoop.hbase.HBaseFileSystem.renameDirForFileSystem(HBaseFileSystem.java:161)

        at org.apache.hadoop.hbase.master.handler.CreateTableHandler.handleCreateTable(CreateTableHandler.java:184)

        at org.apache.hadoop.hbase.master.handler.CreateTableHandler.process(CreateTableHandler.java:127)

        at org.apache.hadoop.hbase.executor.EventHandler.run(EventHandler.java:175)

        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)

        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)

        at java.lang.Thread.run(Thread.java:745)



원인은 hbase 디렉토리에 동일한 테이블의 이름을 가진 디렉토리가 있는데, 특정 이유(권한 또는 기타 등등) rename이 되지 않으면서 에러가 발생한다. 수동으로 작업한다.


cd /tmp/hbase-hbase/hbase

rm -rf xxxx/



다시 table 생성을 시도하지만 생성되지 않는다. zookeeper에 테이블 정보가 남아 있다. zk에서 해당 테이블을 삭제한다. 


[zk: 장비:2181(CONNECTED) 4] ls /hbase/table

[xxxx]

[zk: 장비:2181(CONNECTED) 4] delete /hbase/table/xxxx



다시 table 생성을 시도하지만 생성되지 않는다. hbase 내부 캐시가 있는 듯 하다. 재시작한 후, 테이블 생성을 할 수 있었다.


sudo service hbase-master restart 



Posted by '김용환'
,

[hbase] rowkey 검색하기

hbase 2016. 12. 15. 17:54


hbase 테이블에서 검색 필터를 사용해 특정 rowkey를  검색할 수 있다.



필터 타입은 아래 링크에 있다. 

https://www.cloudera.com/documentation/enterprise/latest/topics/admin_hbase_filtering.html 



예시는 다음과 같다. 

scan '.META.',{FILTER=>"PrefixFilter('feed')"}


scan 'xxxx', {COLUMNS => ['f'], FILTER => "SingleColumnValueFilter('abc','drug',=,'binary:avil')",STARTROW => "1000", ENDROW => "2000"}




만약 모든 rowkey를 얻으려면 다음 커맨드를 사용한다. 


count '.META.', { INTERVAL => 1 }

Posted by '김용환'
,



테스트용으로 사용하는 오래된 cloudera hbase 0.94와 apache phoenix 3.2를 연동하려 했지만, 실패한 내용을 적는다. 


추후 기회가 오면 최신 버전으로 다양하게 테스트를 잘 하는 것이 좋을 것 같다. ㅠㅠ



아래 링크를 참조했다. http://doc.mapr.com/display/MapR/Using+Apache+Phoenix+on+HBase



apache phoenix의 pheonix 3.2 라이브러리를 hbase에 라이브러리에 복사하고, hbase를 재시작했다. (모든 region 서버에 다 넣고 재시작해야 한다..)


$ sudo cp  phoenix-3.2.2/hadoop2/common/*.jar /usr/lib/hbase/lib/

$ ls -al /usr/lib/hbase/lib/

$ sudo service hbase-master restart



phoenix를 실행하려고 했더니. 에러가 난다. 


$ ./hadoop2/bin/sqlline.py  localhost:2181:/hbase

오류: 기본 클래스 sqlline.SqlLine을(를) 찾거나 로드할 수 없습니다.



아래처럼 대충 수정해놓고 실행했다.


$ mv ./hadoop2/phoenix-3.2.2-client-hadoop2.jar ./hadoop2/phoenix-3.2.2-client.jar

Connecting to jdbc:phoenix:localhost:2181:/hbase

.....


정상작동하는 것처럼 보이지만, shell 이 뜨지 않는다... 뜨헉!

하지만, hbase 로그 상으로는 특별한 것은 보이지 않다. 



hbase shell에 들어가 list 커맨드를 날리니. 아무 테이블도 보이지 않았다!


앱에서 다음과 같은 Exception을 발생시키고 있다!!




org.apache.hadoop.hbase.client.NoServerForRegionException: No server address listed in .META. for region xxxx,,1481780733642.719b5b9f72b8c7af2f338ef0b23d76c9. containing row

java.lang.RuntimeException: org.apache.hadoop.hbase.client.NoServerForRegionException: No server address listed in .META. for region xxxx,,1481780733642.719b5b9f72b8c7af2f338ef0b23d76c9. containing row

at org.apache.hadoop.hbase.client.HTableFactory.createHTableInterface(HTableFactory.java:38) 

at org.apache.hadoop.hbase.client.HTablePool.createHTable(HTablePool.java:265) 

at org.apache.hadoop.hbase.client.HTablePool.findOrCreateTable(HTablePool.java:195) 

at org.apache.hadoop.hbase.client.HTablePool.getTable(HTablePool.java:174) 




hbase를 재시작하다가 에러가 발생한다. 



ERROR org.apache.hadoop.hbase.regionserver.handler.OpenRegionHandler: Failed open of region=xxx,,1481780315502.2d152bb821024643f5959e9ea132246d., starting to roll back the global memstore size.

java.lang.IllegalStateException: Could not instantiate a region instance.

        at org.apache.hadoop.hbase.regionserver.HRegion.newHRegion(HRegion.java:4247)

        at org.apache.hadoop.hbase.regionserver.HRegion.openHRegion(HRegion.java:4427)

        at org.apache.hadoop.hbase.regionserver.handler.OpenRegionHandler.openRegion(OpenRegionHandler.java:335)

        at org.apache.hadoop.hbase.regionserver.handler.OpenRegionHandler.process(OpenRegionHandler.java:101)

        at org.apache.hadoop.hbase.executor.EventHandler.run(EventHandler.java:175)

        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)

        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)

        at java.lang.Thread.run(Thread.java:745)

Caused by: java.lang.reflect.InvocationTargetException

        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)

        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)

        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)

        at java.lang.reflect.Constructor.newInstance(Constructor.java:526)

        at org.apache.hadoop.hbase.regionserver.HRegion.newHRegion(HRegion.java:4244)

        ... 7 more

Caused by: java.lang.NullPointerException

        at org.apache.hadoop.hbase.regionserver.RegionCoprocessorHost.loadTableCoprocessors(RegionCoprocessorHost.java:163)

        at org.apache.hadoop.hbase.regionserver.RegionCoprocessorHost.<init>(RegionCoprocessorHost.java:155)

        at org.apache.hadoop.hbase.regionserver.HRegion.<init>(HRegion.java:502)





.META. 테이블의 모든 rowkey를 하나씩 수동으로 deleteall로 날리고, 다시 테이블을 생성해서 문제를 해결했다. 


deleteall '.META.', 'xxxx,,1481780862570.b48ea78f2b093372649de89574bcb1a7.'


create 'xxxx', { NAME => 'c', BLOOMFILTER => 'ROWCOL', VERSIONS => '1', COMPRESSION => 'GZ', TTL => '-1', BLOCKCACHE => 'false'}




Posted by '김용환'
,


오래된 버전의 cloudera hbase, pheonix를  다운받을 수 있는 장소 URL..




- cloudera hbase 다운받는 위치


https://repository.cloudera.com/artifactory/public/org/apache/hbase/hbase/




- apache pheonix 다운 받는 위치


https://archive.apache.org/dist/phoenix/




- cloudera hadoop 다운 받는 위치


https://repository.cloudera.com/artifactory/public/org/apache/hadoop/hadoop-hdfs/

'scribbling' 카테고리의 다른 글

ndbench  (0) 2017.01.11
bash shell의 원어를 처음 알게 되었다. Bourne-again shell이라니...  (0) 2016.12.16
[펌] webpagetest 설치 가능  (0) 2016.12.09
elasticsearch 5.0 릴리즈되다.  (0) 2016.11.01
[펌] 마르코프 체인  (0) 2016.10.20
Posted by '김용환'
,




CentOS Linux Server Cookbook 2번째 판을 CentOS 7 리눅스 서버 쿡북이라는 책으로 번역했고 2016년 12월에 발간된다. 어머니가 계신 병실에서 어머니를 응원하며 이 책을 번역했다. 


리눅스 관련 책을 늘 번역하고 싶은 마음이 있었는데, 다행히 번역과 출간까지 진행될 수 있어서 기뻤다. 

리눅스가 너무 어려웠지만, 리눅스를 공부하면서 리눅스를 좋아하게 된 것 같다. 


init.d과 xinetd의 매력을 빠진지 오래 되지 않았는데. 벌써 확 바뀐 CentOS 7.. 한국의 대부분의 회사는 안정성과 관리 때문에 Centos를 쓰게 되는데 CentOS 7의 내용은 반드시 공부할 만하다. 사실 상 CentOS 2에 해당될 정보로 인터페이스가 많이 바뀐 것 같아서, 이번에 책 번역을 진행하게 되었다. 


개발자에게도 좋은 책이고,(한 때는 CentOS의 여러 응용 프로그램을 담당했던 적이 있었기 때문에..) 시스템 운영자/시스템 엔지니어에게 좋은 책이 되지 않을까 싶다.

 


<원서>


https://www.amazon.com/CentOS-Linux-Server-Cookbook-Second/dp/1785887289



<번역>





http://www.yes24.com/24/goods/33674464?scode=032&OzSrank=10


http://acornpub.co.kr/book/centos7



번역자의 책 설명

센트OS 6보다 훨씬 좋아진 센트OS 7의 설치 및 기본 설정 방법, 소프트웨어 패키지 관리 방법을 소개한다. 또한 파일 시스템과 서버에 안전하게 접근하도록 관리하는 방법과 파일, 프린터, 네트워크의 DHCP와 같은 다양한 자원 공유 시스템 설정 방법을 설명한다. FTP 서비스, 자체 DNS 서버 구축, 데이터베이스 서버 실행 방법, 메일과 웹 서비스를 제공하는 기능 등의 고급 주제를 다룬다. 마지막으로 SELinux를 상세하게 설명하며, 도커(Docker) 운영 시스템 가상화로 작업하는 방법과 나기오스(Nagios)로 IT 인프라 장비를 모니터링하는 방법을 설명한다.


번역자


코드를 좋아하는 개발자지만, 리눅스 운영체제도 좋아한다. 리눅스 커널 프로그래밍을 하면서 리눅스와 조금씩 친해지기 시작했는데, 코드로는 어렵게 진행되지만 리눅스에서는 쉽게 할 수 있는 작업들을 알게 되면서 특히 리눅스를 좋아하게 됐다. 다양한 리눅스 배포판(레드햇 리눅스, 센트OS, 우분투, 페도라 리눅스 배포판)을 사용할 기회를 얻으면서 다양한 지식을 쌓게 됐다.
네이버에서 개발자로 일하면서 1,000여 대의 센트OS 서버 시스템을 관리했고, 리눅스 커널 /tcpdump/ 자바 분석 툴을 통해 애플리케이션의 성능 문제와 장애를 해결했으며, 아파치 서버/메일 서버/배포 서버/ 모니터링 서버/캐시 서버/Mysql DB 등의 구축 및 운영 업무를 해왔다. 이런 경험을 통해 안정적인 센트OS를 기반으로 다양한 기술들을 배우고 적용할 수 있었다.
하지만 센트OS 7에서 새로운 도전에 직면하게 됐다. 센트OS 7은 기존과는 달라진 명령어를 갖고 있다. 새로운 설치자, 시스템 관리 서비스 기능, 방화벽 데몬, 리눅스 컨네이너 기능(도커(docker) 지원), SELinux, 방화벽 관리 시스템, 새로운 파일 시스템이 추가됐는데, 생각보다 배우고 익혀야 할 내용이 많았다. 그때 마침 이 책을 보게 됐고, 센트OS 7에 대한 많은 지식을 얻을 수 있었다. 기존에 센트OS 6을 잘 아는 개발자/시스템 운영자뿐 아니라, 센트OS 7을 처음 접하는 개발자/시스템 운영자에게 이 책은 센트 OS 7의 길라잡이가 될 것이다.
 



Posted by '김용환'
,

[scala] 쉘 실행하기

scala 2016. 12. 13. 20:24



 scala에서 프로세스를 실행하려면 scala.sys.process._을 import하고, 문자열에 .! (또는 !)을 실행한다. (마치 regular expression처럼..) 


import scala.sys.process._
val result = "ls -al /Users/samuel.kim/dev".!
println(result)

결과는 다음과 같다. 


total 48

drwxr-xr-x   8 samuel.kim  staff   272 12 11 01:02 .

drwxr-xr-x+ 64 samuel.kim  staff  2176 12 12 18:20 ..

-rw-r--r--   1 samuel.kim  staff     8 12 12 21:48 a

-rw-r--r--   1 samuel.kim  staff     8 12 11 01:02 b

..

0


주의할 점은 !는 ProcessBuilder 클래스의 메소드로서 리턴 값은 Int이다. 즉, 결과를 출력하고 리턴 값으로 0만 받는 형태이다. 


def ! : Int


전체 결과를 String으로 얻으려면, !!을 사용한다. 


import scala.sys.process._
val result = "ls -al /Users/samuel.kim/dev".!!
println(result)


결과는 다음과 같다.


total 48

drwxr-xr-x   8 samuel.kim  staff   272 12 11 01:02 .

drwxr-xr-x+ 64 samuel.kim  staff  2176 12 12 18:20 ..

-rw-r--r--   1 samuel.kim  staff     8 12 12 21:48 a

-rw-r--r--   1 samuel.kim  staff     8 12 11 01:02 b

..



!!의 리턴 값은 String이다. 

def !! : String





그리고, List로 저장할 수 있다. 

import scala.sys.process._
val result = List("ls -al /Users/samuel.kim/dev".!!)
result.foreach(println)
println(result.size)

결과는 다음과 같다. 


total 48

drwxr-xr-x   8 samuel.kim  staff   272 12 11 01:02 .

drwxr-xr-x+ 64 samuel.kim  staff  2176 12 12 18:20 ..

-rw-r--r--   1 samuel.kim  staff     8 12 12 21:48 a

-rw-r--r--   1 samuel.kim  staff     8 12 11 01:02 b

..

1






파이프라인을 사용하면, 에러가 발생한다.

import scala.sys.process._
val result = List("ls -al /Users/samuel.kim/dev | wc -l".!!)
result.foreach(println)


ls: a: No such file or directory

ls: grep: No such file or directory

ls: |: No such file or directory




다음처럼 커맨드 단위로 잘개 쪼개고 #을 파이프에 붙여야 한다. 


import scala.sys.process._
val result = List(("ls -al /Users/samuel.kim/dev" #| "wc -l ").!!)
result.foreach(println)

결과는 다음과 같다. 





이번에는 >>이라는 표준 출력(stdout) append를 사용한다. #|처럼 #>>을 사용하며, #>> 다음에는 new File()을 생성한다. 


import java.io.File
import scala.sys.process._
val result = ("ls -al /Users/samuel.kim/dev" #| "wc -l " #>> new File(s"/Users/samuel.kim/dev/a.txt")).!
println(result)
val result2 = ("cat /Users/samuel.kim/dev/a.txt".!!)
println(result2)


결과는 다음과 같다.


0

       8



스칼라에서는 표준 입력/출력을 처리할 수 있다.


#<   Redirect STDIN


#>   Redirect STDOUT

#>>  Append to STDOUT


#&&  Create an AND list

#!!  Create an OR list



자세한 내용은 scala.sys.process.ProcessBuilder api를 참조한다.





이를 바탕으로 함수를 만들 수 있다. 


def get(filter: String) = {
val command = s"ls -al /Users/samuel.kim/dev" #| s"grep $filter "
println(command)
command
}

val result = get("xx").!!
println(result)

결과는 다음과 같다.


 ( [ls, -al, /Users/samuel.kim/dev] #| [grep, xx] ) 

drwxr-xr-x   9 samuel.kim  staff   306 Dec  5 21:16 xx





scala는 쉘의 리턴값이 정상이 아니면 Exception이 발생한다. Try를 써주면 Exception 대신 Failure로 처리해 준다.


def get(filter: String) = {
val command = s"ls -al /Users/samuel.kim/dev" #| s"grep $filter "
println(command)
command
}

val result = Try(get("bbb").!!)
println(result)

결과는 다음과 같다.


 ( [ls, -al, /Users/samuel.kim/dev] #| [grep, bbb] ) 

Failure(java.lang.RuntimeException: Nonzero exit value: 1)

Posted by '김용환'
,


scala에서 클래스를 생성하는 방법이다.


class Person {
}
new Person()


class Person(id: Int)
new Person(1)


클래스는 this라는 보조 생성자로 객체를 생성할 수 있다. 


그리고, Option 타입을 사용하면 풍부한 생성이 가능하다. 


하지만, case class를 사용하여 new 객체를 하지 않도록 할 때 일부 코드에서는 컴파일 에러가 발생한다. 

case class Person(id: Int, name: String, department: Option[String]) {
def this(id: Int) = this(id, "no-name")

def this(id: Int, name: String) = this(id, name, Some("no-name"))
}

new Person(1, "matt")
new Person(2)
new Person(3, "samuel", Some("develop"))

//Person(1) // compile error
//Person(2, "jack") // compile error
Person(3, "samuel", Some("develop"))


보조 생성자의 한계가 보이기 때문에, 이때 동반자 객체(companion object)를 활용한다.


이전에 컴파일 에러가 발생했던 부분이 사라지고 더욱 Rich하게 사용할 수 있다. 




case class Person(id: Int, name: String, department: Option[String]) {
def this(id: Int) = this(id, "no-name")

def this(id: Int, name: String) = this(id, name, Some("no-name"))
}

object Person {
def apply(id: Int) = new Person(id)

def apply(id: Int, name: String) = new Person(id)
}

new Person(1, "matt")
new Person(2)
new Person(3, "samuel", Some("develop"))


Person(1)
Person(2, "jack") Person(3, "samuel", Some("develop"))





Posted by '김용환'
,