かんちゃんの備忘録

プログラミングや言語処理、ゲームなど知的好奇心のための備忘録(個人の感想)です。

Xonshを使ってみた

【Xonsh Advent Calendar 2017の13日目の記事です。】

Xonshがいいという話を聞いて、これは使ってみないと!と思い使ってみました。

その備忘録です。

Xonshとは

the xonsh shell

~ こちらトム少佐からXonsh地上管制へ ~

XonshはPythonにより動作する、クロスプラットフォームUnixのようなシェル言語であるコマンドプロンプトです。 この言語はPython 3.4+の上位互換で、慣れ親しんだBashやIPythonなどの基本的なシェル命令を追加したものです。 LinuxやMac OSX、Windowsといったメジャーなシステム上で動作します。 Xonshは、普段使いで上級者も初級者も同じように使えるように作られています。

勢いでxonshトップページの冒頭を訳してみましたが、This is major Tom to ground xonshtrolは、デビッド・ボウイスペイス・オディティThis is major Tom to ground controlをもじっているようで、英語力のない自分には理解しがたいです(汗)

とにかく冒頭からは、Pythonで作られた良い感じのシェルだということが分かったので使ってみます。

導入

Python 3.4以上の環境を用意して、pip install xonshでインストールできます。

ログインシェルにする場合は、システムにPython 3.4以上をインストールしておくと安心かと思います。

使ってみた

Tutorial見ながら使ってみて、普段使っているzshとは異なる機能や文法に触れました。 (ノリで記事を書いてみたら、ばんくしさんの1日目の記事と結構かぶってしまいました( ))

環境変数

$+変数名が環境変数となります。また代入は、Pythonと同じように記述します。

username@hostname ~ $ $HOST
'hostname'
username@hostname ~ $ $HOST = "hogefuga"
username@hostname ~ $ $HOST
'hogefuga'

xonshスクリプトから参照したいときは、${}構文を使うことで文字列で参照できます。

username@hostname ~ $ ${"HOME"}
"/home/username"

Python-modeとSubprocess-mode

xonshを使っていると、まるでIPythonのような使い勝手でbash likeなPythonというように思えてきます。 両方の文法が使えるのですが、混在すると曖昧となるため、コマンドと同名の変数名やメソッド名は避けましょうというお話です。

username@hostname ~ $ ls = 44
username@hostname ~ $ ls
44
username@hostname ~ $ del ls
username@hostname ~ $ ls
...

Subprocessとして

$()でコマンドを実行すると、PythonでSubprocessを使ったときのように、コマンドの戻り値が文字列型で返ってきます。

username@hostname ~ $ $(ls)
hoge\nfuga\ndotfiles

$[]という書き方でもSubprocessとして動作させられますが、直に標準出力に出力されるようで、戻り値としてはNoneです。 パイプなどで渡すとエラーとなりました。

username@hostname ~ $ $[ls]|less
hoge
fuga
...
xonsh: For full traceback set: $XONSH_SHOW_TRACEBACK = True
AttributeError: 'NoneType' object has no attribute 'splitlines'
username@hostname ~ $ $[ls] == None
hoge
fuga
...
True

プロセス置換ができない?

bashzshで一時ファイルを作成するかわりに、プロセス置換で作業することが多いです。 プロセス置換はbashzshに搭載されている便利な機能の一つで、引数でファイルを指定するかわりにコマンドの実行結果をファイルとして扱える機能です。 つまり中間ファイルが不要ということです。(どの中間ファイルを使ったか忘れがちですが、これだとコマンドの履歴として記録されます。)

入力をプロセス置換するのは、だいたい2つ以上のファイルを利用するコマンドを使うときです。(ひとつのファイルなら基本的にはパイプで) diffを取る時やjoin、pasteなどで、以下のように使います。

username@hostname ~ $ diff <(sort -nrk1 hoge.txt) <(sort -nrk1 fuga.txt)

Issueをprocess substitutionで検索してみると、現時点(2017/12/10)で一番新しいものはIssue 1307で、プロセス置換は使えないようです。 $(echo Hi Mom > /tmp/mom)/tmp/momを返すので、プロセス置換と似たことができると読めたのですが、v0.6.0では空文字列が返ってきました。

詳しい人、教えてください。

使ってみて感想

シェルの設定ファイルを書かない人や、プロセス置換などを利用しない人にとっては、十分すぎる補完機能やシンタックスハイライトと感じました。

zshから乗り換えるとなると、勝手が違うため覚えることが多いように思います。

Python言語が使えるという利点を活かす利用法を考えたいです。 たぶん、awkperlsedを使うかわりにうまく使えるのだと思います。