Marcel은 새로운 셸입니다. 여러 면에서 전통적인 셸과 비슷하지만, 몇 가지는 확연히 다른 방식으로 동작합니다:
- 파이프(Piping): 일반적인 셸은 한 명령의 출력을 다른 명령의 입력으로 전달할 때 텍스트를 파이프로 보냅니다. 반면 Marcel은 문자열 대신 구조화된 데이터를 파이프로 전달합니다.
- Python: Marcel은 Python으로 구현되었으며, 여러 방식으로 Python 기능을 제공합니다. 명령에 로직이 조금 필요하다면 Marcel 안에서 Python으로 자연스럽게 표현할 수 있습니다.
- 스크립팅: Marcel은 스크립팅에 독특한 접근 방식을 취합니다. 물론 marcel 명령 시퀀스를 텍스트 파일에 작성해 실행할 수도 있지만, Marcel은 Python 모듈 형태의 API도 함께 제공합니다. 이 모듈을 임포트하면 일반 Python보다 훨씬 편리하게 스크립트를 작성할 수 있습니다.
Marcel은 GPLv3 라이선스로 배포되는 오픈소스 프로젝트입니다.
Linux에 Marcel 설치하기
Marcel은 Python 3.6 이상이 필요합니다. Linux에서 개발 및 테스트되었으며, macOS에서도 대부분 잘 동작합니다. (Windows 포팅이나 macOS 미비점 개선에 도움을 주고 싶다면 개발팀에 연락해 보세요.)
개인 사용자 계정에 설치하려면 다음 명령을 실행합니다:
# python3 -m pip install marcel
모든 사용자가 사용할 수 있도록 설치하려면(예: /usr/local):
$ sudo python3 -m pip install --prefix /usr/local marcel
설치가 완료되면 marcel 명령을 실행해 정상 작동 여부를 확인하고, marcel 프롬프트에서 version 명령으로 버전을 확인할 수 있습니다:
$ marcel
Marcel 셸 커스터마이징
Marcel은 시작 시 읽어들이는 ~/.marcel.py 파일(수정 시 자동으로 다시 읽음)을 통해 커스터마이징할 수 있습니다. 파일 이름에서 짐작할 수 있듯이, marcel의 커스터마이징은 Python으로 수행됩니다.
가장 먼저 손보고 싶은 부분은 아마 프롬프트일 것입니다. PROMPT 변수에 리스트를 할당하면 됩니다. 예를 들어 현재 디렉터리를 초록색으로 표시하고 그 뒤에 파란색 >를 붙이고 싶다면 다음과 같이 작성합니다:
PROMPT = [
Color(0, 4, 0),
lambda: PWD,
Color(0, 2, 5),
'> '
]
결과는 다음과 같습니다:
이렇게 하면 bash에서 다뤄야 하는 난해한 PS1 설정을 깔끔하게 대체할 수 있습니다. Color(0, 4, 0)는 초록색을 지정하며, 인자는 0~5 범위의 RGB 값입니다. PWD는 현재 디렉터리를 나타내는 환경 변수이고, 앞에 lambda:를 붙이면 프롬프트가 표시될 때마다 평가되는 함수가 됩니다.
~/.marcel.py 파일에서는 Python 모듈도 임포트할 수 있습니다. 예를 들어 marcel 명령에서 math 모듈의 함수를 사용하고 싶다면:
from math import *
이후 해당 모듈의 심볼(예: pi)을 바로 참조할 수 있습니다:
pi가 괄호로 묶여 있는 점에 주목하세요. 일반적으로 marcel은 괄호를 사용해 Python 표현식을 구분합니다. 즉 (pi)는 변수 pi의 값을 가져오는 Python 표현식을 평가합니다. 같은 방식으로 (USER), (HOME) 같은 전통적인 환경 변수나, marcel 네임스페이스의 심볼을 활용하는 유효한 Python 표현식에도 접근할 수 있습니다.
물론 자신만의 심볼을 정의할 수도 있습니다. 예를 들어 ~/.marcel.py에 다음 함수를 정의하면:
def factorial(n):
f = 1
for i in range(1, n + 1):
f *= i
return f
명령줄에서 factorial 함수를 곧바로 호출할 수 있습니다:
Marcel 셸 활용 예제
이제 marcel 셸에서 실제로 유용하게 쓸 수 있는 명령 예제를 살펴보겠습니다.
확장자별 파일 크기 집계
현재 디렉터리를 재귀적으로 탐색하고, 파일을 확장자별(예: .txt, .py 등)로 그룹화한 뒤 각 그룹의 총 파일 크기를 계산해 보겠습니다.
marcel에서는 다음과 같이 처리할 수 있습니다:
ls 연산자는 File 객체 스트림을 생성합니다(-fr는 디렉터리를 재귀적으로 방문하고 파일만 반환한다는 의미입니다).
File 객체는 다음 단계인 map으로 파이프됩니다. map은 가장 바깥쪽 괄호 안에 지정된 Python 함수로, 각 파일을 '확장자 + 크기' 튜플로 매핑합니다(marcel에서는 lambda 키워드를 생략할 수 있습니다).
red(reduce) 연산자는 튜플의 첫 번째 요소(확장자)를 기준으로 그룹화한 뒤 각 그룹 내 크기를 합산합니다. 최종 결과는 확장자 기준으로 정렬됩니다.
호스트 실행 파일과 Marcel 파이프라인
파이프라인에는 marcel 연산자와 호스트 실행 파일을 섞어 사용할 수 있습니다. 연산자 사이에서는 객체가 전달되지만, 연산자와 실행 파일의 경계에서는 marcel이 문자열을 파이프로 전달합니다.
예를 들어 다음 명령은 연산자와 실행 파일을 결합해, 셸이 /bin/bash로 설정된 사용자의 사용자명 목록을 출력합니다.
$ cat /etc/passwd \
| map (line: line.split(':')) \
| select (*line: line[-1] == '/bin/bash') \
| map (*line: line[0]) \
| xargs echo
cat은 Linux 실행 파일입니다. /etc/passwd를 읽어 들이면, marcel은 그 내용을 다운스트림의 map 연산자로 전달합니다.
map의 괄호로 묶인 인자는 각 줄을 : 구분자로 나눠 7-튜플을 만드는 Python 함수입니다. select는 marcel 연산자로, 마지막 필드가 /bin/bash인 튜플만 걸러내는 Python 함수를 인자로 받습니다.
그다음 또 다른 map은 각 튜플에서 사용자명 필드만 남깁니다. 마지막으로 xargs echo가 들어온 사용자명들을 한 줄로 합쳐 stdout에 출력합니다.
Marcel 셸에서 스크립팅하기
Python은 흔히 스크립팅 언어로 불리지만, 실제로는 그 용도에 적합하지 않은 면이 있습니다. 문제는 Python에서 셸 명령이나 외부 실행 파일을 다루는 것이 번거롭다는 점입니다. os.system()은 간단하지만 stdin, stdout, stderr 처리에는 역부족이고, subprocess.Popen()은 강력하지만 사용법이 복잡합니다.
Marcel의 해법은 marcel 연산자를 Python 언어 기능과 통합한 모듈을 제공하는 것입니다. 앞서 본 예제를 다시 살펴보면, 확장자별 파일 크기 합계를 계산하는 Python 코드는 다음과 같습니다:
from marcel.api import *
for ext, size in (ls(file=True, recursive=True)
| map(lambda f: (f.suffix, f.size))
| red('.', '+')):
print(f'{ext}: {size}')
셸 명령은 문법적 관례만 다를 뿐 이전과 동일합니다. 즉 ls -fr은 ls(file=True, recursive=True)로 변환됩니다. map과 red 연산자도 셸 버전처럼 파이프로 연결되어 있습니다. 전체 파이프라인 (ls … red)는 Python 이터레이터를 반환하므로, Python의 for 루프에서 바로 순회할 수 있습니다.
Marcel 셸로 데이터베이스 접근하기
데이터베이스 접근도 marcel 파이프라인에 통합할 수 있습니다. 먼저 설정 파일 ~/.marcel.py에서 데이터베이스 접속 정보를 구성해야 합니다. 예를 들면:
define_db(name='jao',
driver='psycopg2',
dbname='acme',
user='jao')
DB_DEFAULT = 'jao'
이 설정은 psycopg2 드라이버를 사용해 acme라는 Postgres 데이터베이스에 접속하도록 구성합니다. marcel의 연결은 jao 사용자로 이루어지며, 데이터베이스 프로필 이름 역시 jao입니다(DB_DEFAULT는 프로필이 따로 지정되지 않았을 때 사용할 기본 프로필을 지정합니다). 설정이 끝나면 sql 연산자로 데이터베이스를 바로 쿼리할 수 있습니다:
sql 'select part_name, quantity from part where quantity < 10' \ | out --csv --file ~/reorder.csv
이 명령은 part 테이블을 조회하고, 그 결과를 CSV 형식으로 ~/reorder.csv 파일에 저장합니다.
Marcel 셸로 원격 서버 접근하기
데이터베이스 접근과 마찬가지로 원격 접근도 ~/.marcel.py에서 구성할 수 있습니다. 예를 들어 다음은 4노드 클러스터를 정의하는 코드입니다:
define_remote(name='lab',
user='frankenstein',
identity='/home/frankenstein/.ssh/id_rsa',
host=['10.0.0.100',
'10.0.0.101',
'10.0.0.102',
'10.0.0.103'])
이 클러스터는 marcel 명령에서 lab이라는 이름으로 참조할 수 있습니다. user와 identity 매개변수는 SSH 로그인 정보를 지정하고, host 매개변수는 클러스터를 구성하는 노드들의 IP 주소를 지정합니다.
클러스터가 구성되면 모든 노드를 한 번에 조작할 수 있습니다. 예를 들어 클러스터 전체에서 프로세스 PID와 명령줄 목록을 수집하려면:
@lab [ps | map (proc: (proc.pid, proc.commandline))]
이 명령은 (IP 주소, PID, 명령줄) 튜플의 스트림을 반환합니다.
더 자세한 정보는 다음 링크를 참고하세요:
- https://www.marceltheshell.org/
- https://github.com/geophile/marcel
Marcel은 아직 역사가 짧은 프로젝트로 활발히 개발이 진행 중입니다. 기여하고 싶으시다면 개발팀에 언제든 연락해 보세요.