GNOME 데스크탑의 사용자에게 Nautilus 프로그램은 아마도 상대적으로 더 자주 사용하는 애플리케이션 중 하나이다. 이는 간단한 그래픽 인터페이스를 사용하여 파일 복사, 이동, 이름 바꾸기 및 과제 검색을 모두 처리한다. 언뜻 보면 Nautilus가 수행할 수 없는 파일 관련 작업이 많지 않은 것처럼 보인다 —일반적으로 쉘 스크립트를 사용하여 수행하는 작업에 대해 생각하기 시작하는 경우를 제외하면 그렇다.
Nautilus 개발자들은 주요 코드 기본을 없애 버리지 않고 새 기능을 추가하는 몇 가지 방법을 제시했다. 가장 간단한 방법은 일반적으로 터미널 프롬프트로부터 수행하는 명령 시리즈를 실행하는 배쉬(bash) 또는 쉘 스크립트를 사용하는 것이다. 이 메소드를 통해 먼저 수행하려는 것을 수행하게 하는 명령을 시도할 수 있다. 독자는 C 스크립트 언어, GnomeBasic, Perl 및 Python을 비롯하여 다른 언어도 사용할 수 있다. 이 기사는 Python 언어를 사용하여 Nautilus로 새 기능을 추가하는 것에 대해 살펴본다. Python 언어와 Python Standard Library에 대해 기초적인 이해를 갖추었다고 가정한다.
Nautilus를 확장하기 위한 첫 번째 방법은 /home에서 .gnome2/nautilus-scripts라는 이름의 특수 디렉토리를 사용하는 것이다. Scripts 메뉴 아래 파일이나 폴더를 마우스 오른쪽 단추로 클릭하면 이 디렉토리에 있는 실행 파일이 모두 표시될 것이다. 여러 파일이나 폴더를 선택하여 동일한 마우스 오른쪽 단추로 클릭하는 방법으로 스크립트에 파일 목록을 전달할 수도 있다.
Nautilus를 통해 스크립트가 호출될 때의 현재 디렉토리와 선택한 파일과 같은 항목이 들어 있는 수많은 환경 변수를 사용할 수 있다. 표 1은 이러한 환경 변수를 보여준다.
표 1. Nautilus 환경 변수
| 환경 변수 | 설명 |
|---|---|
| NAUTILUS_SCRIPT_SELECTED_FILE_PATHS | 선택한 파일에 대해 줄 바꾸기로 구분된 경로(로컬인 경우만 해당) |
| NAUTILUS_SCRIPT_SELECTED_URIS | 선택한 파일에 대해 줄 바꾸기로 구분된 URI |
| NAUTILUS_SCRIPT_CURRENT_URI | 현재 위치 |
| NAUTILUS_SCRIPT_WINDOW_GEOMETRY | 현재 창의 위치 및 크기 |
Python에서 독자는 다음과 같은 os.environ.get 함수로 하나의 호출을 통해 이러한 변수의 값을 얻는다.
selected = os.environ.get('NAUTILUS_SCRIPT_SELECTED_FILE_PATHS,'')
|
이 호출은 줄 바꾸기 문자로 구분된 선택한 모든 파일로 경로가 있는 문자열을 리턴한다. Python은 다음 행을 사용하여 이러한 문자열을 반복 가능한 목록으로 간편하게 변환할 수 있다.
targets = selected.splitlines() |
이제는 중지하고 사용자 상호작용에 대해 논의할 시간이 되었다. 제어가 Nautilus에서부터 스크립트로 전달되면, 그 때부터 스크립트가 수행하는 내용에 대한 제한사항이 없다. 스크립트가 수행하는 내용에 따라 어느 사용자 피드백도 필요하지도 않을 수 있으며, 일부 완료 또는 오류 메시지의 유형의 예외를 사용하여 이는 간단한 메시지 상자로 처리할 수 있다. Nautilus가 gtk 윈도윙 툴킷을 사용하여 쓰여졌기 때문에, 이는 필수는 아니라고 해도 동일하게 수행하기 위한 논리적 선택처럼 보인다. TkInter 또는 wxPython을 간편하게 사용하는 것처럼 할 수 있다.
이 기사의 목적상 gtk를 사용할 것이다. 완료 상태를 통신하기 위해 간단한 메시지 상자를 만드는 것은 코드 몇 행만 필요하다. 가독성 목표를 위해 이러한 코드는 메시지를 생성하는 간단한 함수를 작성하는 경우 가장 잘 맞을 것이다. 이렇게 하는 것은 다음과 같이 총 네 개의 코드 행이 필요하다.
def alert(msg):
dialog = gtk.MessageDialog()
dialog.set_markup(msg)
dialog.run()
|
예제: 선택한 파일 수를 리턴하기 위해 간단한 스크립트 작성하기
첫 번째 예제 프로그램은 이러한 스니펫을 현재 선택한 파일 수를 리턴하는 간단한 스크립트로 결합한다. 이 스크립트는 개별 파일 또는 디렉토리에 대해 작업할 것이다. 독자는 또 다른 Python
라이브러리 함수인 os.walk를 사용하여 각 디렉토리에서 파일의 목록을 순환적으로 빌드할 것이다. 목록 1과 같이 총 38개의 코드 행이
구분선을 포함하여 이러한 작은 유틸리티에 필요한 전부이다.
목록 1. Filecount 스크립트에 대한 Python 코드
#!/usr/bin/env python
import pygtk
pygtk.require('2.0')
import gtk
import os
def alert(msg):
"""Show a dialog with a simple message."""
dialog = gtk.MessageDialog()
dialog.set_markup(msg)
dialog.run()
def main():
selected = os.environ.get('NAUTILUS_SCRIPT_SELECTED_URIS', '')
curdir = os.environ.get('NAUTILUS_SCRIPT_CURRENT_URI', os.curdir)
if selected:
targets = selected.splitlines()
else:
targets = [curdir]
files = []
directories = []
for target in targets:
if target.startswith('file:///'):
target = target[7:]
for dirname, dirnames, filenames in os.walk(target):
for dirname in dirnames:
directories.append(dirname)
for filename in filenames:
files.append(filename)
alert('%s directories and %s files' %
(len(directories),len(files)))
if __name__ == "__main__":
main()
|
그림 1은 파일을 마우스 오른쪽 단추로 클릭하거나 파일 그룹을 선택할 때에 Nautilus에 표시되어야 하는 내용을 보여준다. Scripts 메뉴 옵션은 .gnome2/nautilus-scripts에서 모든 실행 가능한 파일을 표시하고 해당 폴더를 여는 옵션도 제공한다. 이러한 파일 중 하나를 선택하면 해당 스크립트를 실행한다.
그림 1. Nautilus에서 파일 선택하기
그림 2는 Filecount.py 스크립트를 실행한 결과물을 보여준다.
그림 2. Filecount.py 결과물
Nautilus 스크립트 디버깅을 진행하면서 도움이 될 만한 몇 가지 사항이 있다. 우선 Nautilus의 모든 인스턴스를 닫아서 완전히 다시 로드하도록 허용하고 새 스크립트 또는 확장을 찾는 것이다. 다음 명령을 사용하여 이렇게 수행할 수 있다.
nautilus -q |
그 다음 편리한 명령을 통해 환경 설정 또는 프로파일 데이터를 열지 않고 Nautilus를 실행할 수 있다. 이렇게 하면 독자의 스크립트 또는 확장이 우연히 어떤 것을 손상하는 경우에 나중에 나오는 몇 가지 단계에서 해결할 수 있다. 해당 명령은 다음과 같다.
nautilus -no-desktop |
Nautilus에서부터 filecount 유틸리티를 액세스 가능하도록 만들기 위해 남은 유일한 단계는
이를 ~/.gnome2/nautilus-scripts 디렉토리로 복사하여 실행을 허용하도록 파일 모드를 변경하는 것이다. 다음 명령을 사용하여 이렇게 수행할 수 있다.
chmod +x Filecount.py |
두 번째 예제로서 파일 정리 유틸리티를 작성하여 Vim 또는 EMACS와 같이 편집기로 생성되는 임시로 고려할 수 있는 어느 파일이나 찾는다.
동일한 개념은 check 함수를 간단히 수정하여 지정된 어느 파일의 디렉토리나 제거하는 데 사용될 수 있다. 이 코드는
자동 조작(silent operation) 카테고리에 속하며, 이는 실행하고 사용자에게 피드백을 제공하지 않음을 의미한다.
이 스크립트의 주된 기능은 몇 가지 사소한 예외가 있어도 기본적으로 이전 예제와 동일한 것처럼 보인다. 이 코드는 마지막 디렉토리가 횡단될 때까지 기본 함수를 여러 번
호출하는 순환적 개념을 사용한다. 순환을 사용하지 않고 동일한 작업을 완수하는 os.walk 함수를 사용할 수도 있었다. 파일 검사는
check 함수에서 나타나고 물결표(~) 또는 우물정 부호(#)로 끝나고,
우물정 부호로 시작하거나 .pyc 확장자로 끝나는 파일을 찾는다.
이 예제는 Python Standard Library os 모듈이 제공하는 광범위한 함수의 숫자를 자랑한다. 이는 또한 경로 이름과 디렉토리를 조작하고
파일 조작을 수행하는 운영 체제 독립적인 방식의 훌륭한 예제이기도 하다.
목록 2는 이 스크립트에 대한 코드를 보여준다.
목록 2. 정리 스크립트에 대한 Python 코드
#!/usr/bin/env python
import pygtk
pygtk.require('2.0')
import gtk
import os
def check(path):
"""Returns true to indicate a file should be removed."""
if path.endswith('~'):
return True
if path.startswith('#') and basename.endswith('#'):
return True
if path.endswith('.pyc'):
return True
return False
def walk(dirname=None):
selected = os.environ.get('NAUTILUS_SCRIPT_SELECTED_FILE_PATHS', '')
curdir = os.environ.get('NAUTILUS_SCRIPT_CURRENT_URI', os.curdir)
if dirname is not None:
targets = [dirname]
elif selected:
targets = selected.splitlines()
else:
targets = [curdir]
for target in targets:
if target.startswith('file:///'):
target = target[7:]
if not os.path.isdir(target): continue
for dirname, dirnames, files in os.walk(target):
for dir in dirnames:
dir = os.path.join(dirname, dir)
walk(dir)
for file in files:
file = os.path.join(dirname, file)
if check(file):
os.remove(file)
if __name__ == '__main__':
walk()
|
Nautilus를 향상시키는 두 번째 방법은 확장의 작성을 사용하는 것이다. 이 방법은 처음 방법보다는 약간 더 복잡하지만 추가적인 혜택을 제공한다. Nautilus 확장은 파일 표시장치 창에 임베드될 수 있으므로, 이전에 사용할 수 없는 정보로 열을 채울 확장을 쓸 수 있다. 수행해야 할 수 있는 첫 번째 내용 중 하나는 다음 명령으로 python-nautilus 확장을 설치하는 것이다.
sudo apt-get install python-nautilus |
이 명령은 문서와 예제를 비롯하여 필요한 파일을 다운로드하고 설치한다. /usr/share/doc/python-nautilus/examples 디렉토리에서 샘플 코드를 찾을 수 있다. 한 번 설치되면, 이에 대해 프로그래밍하는 Nautilus 클래스와 제공자 세트로 액세스 권한이 있다. 표 2는 목록을 보여준다.
표 2. Nautilus 클래스 및 제공자
| 클래스 또는 제공자 | 설명 |
|---|---|
nautilus.Column | Nautilus column 오브젝트로의 참조 |
nautilus.FileInfo | Nautilus fileinfo 오브젝트로의 참조 |
nautilus.Menu | Nautilus menu 오브젝트로의 참조 |
nautilus.MenuItem | Nautilus menuitem 오브젝트로의 참조 |
nautilus.PropertyPage | Nautilus propertypage 오브젝트로의 참조 |
nautilus.ColumnProvider | Nautilus 열에서 표시되는 출력을 허용한다 |
nautilus.InfoProvider | 해당 파일에 대한 정보를 제공한다 |
nautilus.LocationWidgetProvider | 해당 위치를 표시한다 |
nautilus.MenuProvider | 새 기능을 마우스 오른쪽 단추로 클릭 메뉴로 추가한다 |
nautilus.PropertyPageProvider | 정보를 해당 특성 페이지에 추가한다 |
gnome.org 사이트에 제공되는 예제는 MenuProvider(background-image.py 및
open-terminal.py), ColumnProvider 및
InfoProvider(block-size-column.py) 및 PropertyPageProvider(md5sum-property-page.py)를 사용하여 시연한다.
ColumnProvider는 실행 파일 Python 코드의 13행을 사용하여 Nautilus로 새 행을 도입한다. 이 코드가 한 번 올바른 위치(~/.nautilus/python-extensions)에 지정되고 Nautilus가
다시 시작되면, View > Visible Columns를 클릭할 때 새 옵션이 표시되어야 한다.
Visible Columns 옵션은 보기 유형을 List로 설정했을 때에만 나타난다. 선택란을 선택하여 Block size
열을 사용하면 다음 Python 라이브러리 호출의 결과를 표시한다.
str(os.stat(filename).st_blksize)) |
어느 Python 확장이나 그 기본 패턴은 기존 Nautilus 제공자 기본 클래스를 서브클래스화한 다음에, 적절한 Nautilus 오브젝트를 결과적으로 리턴할 일련의 명령어를 실행하는 것이다. block-size-column.py
예제에서 리턴된 오브젝트는 nautilus.Column이다. name,
attribute, label 및 description을 비롯한 네 개의 매개변수를 다시 Nautilus로 전달해야 한다. 이러한 예제에 대한
Python 코드는 다음과 같다.
return nautilus.Column("NautilusPython::block_size_column",
"block_size",
"Block size",
"Get the block size")
|
새 확장을 코딩하는 것은 지정된 기본 클래스로부터 필요한 정보를 상속하는 것과 연관된다. block-size-column.py 예제에서
nautilus.ColumnProvider 및 nautilus.InfoProvider 둘 다 클래스 정의에서 열거되므로,
새 클래스는 둘 다로부터 상속된다. 그 다음으로 해야 할 일은 기본 클래스 또는 열을 입력할 클래스로부터 어느 메소드나 오버라이드하는 것이다.
get_columns 및 update_file_info 메소드를 오버라이드하여 block-size-column.py 예제에서 이렇게 한다.
정보를 Nautilus 확장으로 전달하는 것은 스크립팅 케이스와는 다르게 작업한다.
Nautilus는 실제로 새 프로세스를 시작하여 스크립트를 실행하고, 다수의 환경 변수를 설정하여 정보를 전달한다. 확장은 Nautilus와 동일한 프로세스에서 실행하며, 그러므로
오브젝트, 메소드 및 속성에 액세스 권한이 있다.
파일에 대한 정보는 file_type,
location, name,
uri 및 mime_type과 같은 항목을 비롯하여 nautilus.FileInfo 오브젝트를 통해 전달된다. 정보를
FileInfo 오브젝트로 추가하려면 add_string_attribute 메소드를 호출해야 한다. 새 속성을 FileInfo 오브젝트로
추가하도록 이어지는 예제에서 이 접근방식을 사용할 것이다.
첫 번째 예제는 PropertyPageProvider 메소드를 사용하여 하나의 파일(및 여러 파일)을 마우스 오른쪽 단추로 클릭한 다음
Properties를 클릭할 때 행 및 문자의 수를 표시한다. 이 확장의 배후에 있는 기본적인 개념은 파일에서 행과 문자의 수를 계수하고
파일 특성 페이지의 새 탭에서 결과를 보고하는 것이다.
확장은 file 오브젝트를 비롯하여 Nautilus 데이터 구조로 직접 액세스 권한을 가진다. 유일하게 해야 할 일은
다음과 같이 urllib.unquote 라이브러리 함수를 사용하여 해당 이름의 압축을 푸는 것이다.
filename = urllib.unquote(file.get_uri()[7:] |
Python 코드의 몇 가지 행은 행 및 문자의 수를 계수하는 주요 작업을 수행한다. 이 예제를 위해 독자는 count
함수를 작성하여 전체 파일을 하나의 큰 문자열로 읽은 다음에 총 문자 수와 줄 바꾸기 문자 수를 계수한다. 특성 페이지가 선택한 파일과 디렉토리의 수를 표시할 수 있기 때문에,
여러 파일을 계수하기 위해 준비해야 한다. 이제 남은 것은 특성 페이지의 새 페이지에 결과를 추가하는 것 뿐이다. 이 예제는 간단한 gtk.Hbox를 작성한 다음에
목록 3과 같이 수집한 정보로 레이블된 수를 입력한다.
목록 3. Linecountextension.py 파일
import nautilus
import urllib
import gtk
import os
types = ['.py','.js','.html','.css','.txt','.rst','.cgi']
exceptions = ('MochiKit.js',)
class LineCountPropertyPage(nautilus.PropertyPageProvider):
def __init__(self):
pass
def count(self, filename):
s = open(filename).read()
return s.count('\n'), len(s)
def get_property_pages(self, files):
if not len(files):
return
lines = 0
chars = 0
for file in files:
if not file.is_directory():
result = self.count(urllib.unquote(file.get_uri()[7:]))
lines += result[0]
chars += result[1]
self.property_label = gtk.Label('Linecount')
self.property_label.show()
self.hbox = gtk.HBox(0, False)
self.hbox.show()
label = gtk.Label('Lines:')
label.show()
self.hbox.pack_start(label)
self.value_label = gtk.Label()
self.hbox.pack_start(self.value_label)
self.value_label.set_text(str(lines))
self.value_label.show()
self.chars_label = gtk.Label('Characters:')
self.chars_label.show()
self.hbox.pack_start(self.chars_label)
self.chars_value = gtk.Label()
self.hbox.pack_start(self.chars_value)
self.chars_value.set_text(str(chars))
self.chars_value.show()
return nautilus.PropertyPage("NautilusPython::linecount",
self.property_label, self.hbox),
|
그림 3은 파일을 마우스 오른쪽 단추로 클릭한 것과 Linecount 탭을 클릭한 것의 결과를 보여준다. 이제 이 기능이 개별 파일 또는 선택한 파일과 디렉토리의 어느 그룹에서나 작업하는 것을 주목하자. 보고된 숫자는 모든 파일에서 모든 행을 표현할 것이다.
그림 3. 파일에서 행 수를 보기 위한 Linecount 탭 클릭하기
마침내, 확장 유틸리티를 변경하여 특성 페이지가 아니라 열을 입력한다. 비록 nautilus.ColumnProvider 및 nautilus.InfoProvider 둘 다로부터
상속하기 위해 해야 하긴 하지만, 코드로의 수정은 매우 사소한 것이다. 또한
get_columns 및 update_file_info도 구현해야 한다.
get_columns 메소드는 count 메소드로 수집되는 정보를 간단히 리턴한다.
count 메소드는 열 제공자 확장에 대한 다른 기술을 사용한다. Python의 readlines 루틴은
파일의 모든 행을 문자열의 목록으로 읽는 데 사용된다. 총 행 수를 계수하는 것은 바로
len(s) 명령문으로 리턴되는 목록에서 요소의 수이다. 파일 유형 검사가 두 예제 모두에 공통적인 것은 아니지만,
실제로 계수할 행이 있는 텍스트 파일을 계수하는 것만 이치에 맞는다. 다음 행으로 허용 가능한 파일 확장 목록을 작성한다.
types = ['.py','.js','.html','.css','.txt','.rst','.cgi'] |
두 번째 목록은 계수되지 않을 예제가 들어있다. 이 예제의 경우 다음 행으로 하나의 파일을 제외시킨다.
exceptions = ['MochiKit.js'] |
이러한 두 개의 목록은 다음 두 개의 코드 행으로 파일을 포함하거나 제외시키는 데 사용된다.
if ext not in types or basename in exceptions:
return 0
|
전체 확장은 실행 가능한 코드의 총 26개 행이 필요하다. 독자는 관심이 있는 파일을 포함하거나 제외시키는 예외와 유형 목록을 수정하려 할 것이다. 목록 4는 완료된 확장을 보여준다.
목록 4. Linecountcolumn 확장에 대한 Python 코드
import nautilus
import urllib
import os
types = ['.py','.js','.html','.css','.txt','.rst','.cgi']
exceptions = ['MochiKit.js']
class LineCountExtension(nautilus.ColumnProvider, nautilus.InfoProvider):
def __init__(self):
pass
def count(self, filename):
ext = os.path.splitext(filename)[1]
basename = os.path.basename(filename)
if ext not in types or basename in exceptions:
return 0
s = open(filename).readlines()
return len(s)
def get_columns(self):
return nautilus.Column("NautilusPython::linecount",
"linecount",
"Line Count",
"The number of lines of code"),
def update_file_info(self, file):
if file.is_directory():
lines = 'n/a'
else:
lines = self.count(urllib.unquote(file.get_uri()[7:]))
file.add_string_attribute('linecount', str(lines))
|
그림 4는 Line Count 열이 사용된 Nautilus 창을 보여준다. 각 개별 파일은 표시된 총 행 수를 보유할 것이다. 독자는 여러 파일의 총계를 얻어야 하는 경우 이 메소드를 사용하여 약간의 계산을 해야 할 것이다.
그림 4. Nautilus 창에서 Line Count 열
Python으로 Nautilus를 확장하는 것은 실제로 간단한 프로세스이다. Python과 해당 Python Standard Library의 매력은 효율적이고 읽기 가능한 코드에 도움이 된다. gnome.org 사이트에서 문서와 예제를 탐색하는 것이 어려울 수는 있지만 불가능한 것은 아니다. Google 검색을 몇 개만 수행하면 추가 예제도 나타날 것이다. 여기의 예제를 통해 특정한 필요에 부합할 Nautilus를 확장하는 방법에 대한 개념을 이해하기를 바란다. Python에서 코딩에 익숙한 경우 아무 문제가 없을 것이다.
교육
-
Nautilus에 대해 자세히 살펴보자.
-
Nautilus-python
확장에 대해 자세히 살펴보자.
-
Python.org에서 Python 자료를 더 찾을 수 있다.
- developerWorks Linux 영역에서는 수백 개의
기술자료 목록과 함께, Linux 개발자와 관리자를 위한
다양한 다운로드, 토론 포럼 및 다른 참고자료를 찾을 수 있다.
- developerWorks
기술 행사 및 웹 캐스트를 통해 다양한 IBM 제품 및 IT 산업 주제에 대한 최신 정보를 얻을 수 있다.
- 무료 developerWorks Live!
briefing을 통해 최신 IBM 제품 및 도구에 대한 정보뿐만 아니라 IT 업계의 최신 경향까지도 빠르게 확인할 수 있다.
- developerWorks
on-demand demos에서는 입문자를 위한 제품 설치 및 설정부터 숙련된 개발자를 위한 고급 기능까지 망라된 다양한 데모를 제공한다.
- Twitter의 developerWorks를 팔로우(follow)하거나 developerWorks에 대한 Linux 트윗(tweet)의 피드를 구독하자.
제품 및 기술 얻기
-
자신에게 가장 적합한 방법으로 IBM 제품을 평가해 보자.
시험판 제품을 다운로드하거나 온라인으로 제품을 사용해 보거나 클라우드 환경에서 제품을 사용하거나
SOA Sandbox에서
SOA(Service Oriented Architecture)를 효과적으로 구현하는 방법을 배울 수 있다.
토론
- My developerWorks 커뮤니티에 참여하자.
개발자가 운영하고 있는 블로그, 포럼, 그룹 및 위키를 살펴보면서 다른 developerWorks 사용자와 의견을 나눌 수 있다.