Stimulator

機械学習とか好きな技術話とかエンジニア的な話とかを書く

2018年のxonshrc

- はじめに -

この記事はXonsh Advent Calendar 2018 - Qiita最終日の記事です。

私のxonshrc、業務用のコマンドとか含めると大体1500行くらいあるんでgithubで公開するんじゃなくて一部ずつ切り取ってここで紹介しようかなと思いました。

なんか作る時の参考になれば幸いです。


 

- 基本的な設定 -

以下に過去の大まかな設定が書いてあります。
xonshのPROMPTにdatetimeを表示する - Stimulator

環境変数

基本的な設定は大体以下のようになっています。

$EDITOR = '/usr/local/bin/vim'
$VISUAL = '/usr/local/bin/vim'
$VI_MODE = False
$COMPLETIONS_CONFIRM = True
$IGNOREEOF = True
$INDENT = "    "
$CASE_SENSITIVE_COMPLETIONS = False
$HISTCONTROL = "ignoredups"
$XONSH_AUTOPAIR = False
$AUTO_CD = True
$XONSH_SHOW_TRACEBACK = True
$SUPPRESS_BRANCH_TIMEOUT_MESSAGE = True
$UPDATE_COMPLETIONS_ON_KEYPRESS = True
def get_git_user():
    return '{BLUE}' + $(git config user.name).strip() + ' {INTENSE_GREEN}{hostname}{WHITE} ( {YELLOW}"{cwd}" {WHITE}) {INTENSE_RED}$ '
$PROMPT = get_git_user
$LS_COLORS="di=34:ln=35:so=32:pi=33:ex=31:bd=46;34:cd=43;34:su=41;30:sg=46;30:tw=42;30:ow=43;30"
$XONSH_HISTORY_SIZE = (3000, 'commands')
XONSH_HISTORY_MATCH_ANYWHERE = True
$PTK_STYLE_OVERRIDES={
 "completion-menu": "bg:ansiblack ansiwhite",
 "completion-menu.completion": "bg:ansiblack",
 "completion-menu.completion.current": "bg:ansiwhite ansiblack",
 "scrollbar.background": "bg:ansibrightblack",
 "scrollbar.arrow": "bg:ansiblack ansiwhite bold" ,
 "scrollbar.button": "bg:ansiblack",
 "auto-suggestion": "ansibrightblack",
 "aborting": "ansibrightblack",
 }

前年との違いで言えば以下辺りが大きい気がします。

  • get_git_userによってPROMPTにgit user nameを出すようにした
  • PTK_STYLE_OVERRIDESによってstyleを上書きした

githubの所持ユーザ名が増えて、関係ないリポジトリにvaaaaanquishでpushしてしまうみたいな事が2回起こったので確認するようにしました。
後述しますが、gitユーザ変える関数とかも作っています。
またPTK_STYLE_OVERRIDESは以下のissueでも発言した通り、目がチカチカするのを抑えるために必須です。
Change auto_completer's color style · Issue #2840 · xonsh/xonsh · GitHub

キー入力ごとに更新されるCOMPLETIONSですが、iTerm2のcolorと合わせて調整しています。

f:id:vaaaaaanquish:20181221000811p:plain
補完の色合いとか
xonshの補完はzsh風にもできるんですが、私はこちらに慣れてしまいました。

 

aliases

エイリアスは別にgithubでxonshrcで検索して好きなだけ改造すればと思いますが一応省略して外観を。

if platform.system() == 'Darwin':
    aliases["lt"] = "colorls --tree"
    aliases["l"] = "colorls -ltr --sf"
    aliases["la"] = "colorls -la"
else:
    aliases['ls'] = "ls --color=auto"
    aliases["l"] = "ls -lh"
    aliases["la"] = "ls -lha"
    aliases['free'] = "free -h"
    aliases['f'] = 'free -h'
    aliases['wf'] = 'watch free -h'
aliases['ee'] = "exit"
aliases["v"] = "vim"
aliases["vi"] = "vim"
aliases["vx"] = "vim ~/.xonshrc"
aliases["vv"] = "vim ~/.vimrc"
aliases["vs"] = "vim ~/.ssh/config"
...(gitとか色々etc)

MacLinuxで分けれるように書いてあります。
あと "ee" でexitして、zshrcでもbashrcでもに "x" でxonsh起動するようにしています。xonshrcもすぐ開けるようにしておけば、xonsh本体の開発にコントリビュートする時に便利です。

 

xontrib

xontribは以下に落ち着いています。zコマンド病みつき。

xontrib load z
xontrib load readable-traceback
$READABLE_TRACE_STRIP_PATH_ENV=True
$READABLE_TRACE_REVERSE=True
xontrib load coreutils

最近はxonshもptkも開発が早く、まだptkのスピードに追いついてないxontribが見受けられます。
まあ書き直せばいいだけですが、自分は今の所こんな感じです。
(readable-tracebackもpython3.7で動かない情報があるので時間見つけて直します…)

他のxontribはこのへんに
Xontribs — xonsh 0.8.8 documentation
xonshにおけるxontribの紹介 - Stimulator


 

- ライブラリ周り -

ライブラリの管理はpipでやってます。
その他import周りを工夫しておくことでより便利に扱う事ができます。

import, xontrib_load時の自動install

ライブラリの管理について以下記事で書いていますが、import、xontrib load時にライブラリがない場合、自動でpip installするようにしてあります。
Python moduleがない場合に自動でpip installする - Stimulator


以下のようにすることで、新しい環境でひとまずxonshだけ動かしたいという時に、xonshrcを送るだけで対応できます。

import importlib
import builtins
from xonsh.xontribs import xontrib_context, update_context
import xonsh.xontribs

def _import(name, module, ver=None):
    try:
        globals()[name] = importlib.import_module(module)
    except ImportError:
        try:
            import subprocess
            cmd = "sudo pip install -I {}".format(name)
            subprocess.call(cmd, shell=True)
            globals()[name] = importlib.import_module(module)
        except:
            print("can't import: {}".format(module))
_import('pexpect','pexpect')

def _update_context(name, ctx=None):
    if ctx is None:
        ctx = builtins.__xonsh__.ctx
    if not hasattr(update_context, "bad_imports"):
        _update_context.bad_imports = []
    modctx = xontrib_context(name)
    if modctx is None:
        import subprocess
        cmd = "sudo xonsh -c 'xpip install -I xontrib-{}'".format(name)
        subprocess.call(cmd, shell=True)
        remodctx = xontrib_context(name)
        if remodctx is None:
            _update_context.bad_imports.append(name)
            return ctx
        return ctx.update(remodctx)
    return ctx.update(modctx)
xonsh.xontribs.update_context = _update_context

xonshアップグレードでPATHが見えなくなったりした時にも発動するので割と重宝しています。
実際削除

f:id:vaaaaaanquish:20181202210419p:plainf:id:vaaaaaanquish:20181202210543p:plain
実際にライブラリなくてもインストールされる様子

 

importの遅延ロード

以下の記事で書いていますが、xonshrcでは起動が遅くなるのでimportしないけど、xonsh実行時に逐一import書く必要がないようにlazy_moduleを利用しています。
Pythonモジュールの遅延import - Stimulator

import importlib
from xonsh.lazyasd import lazyobject

lazy_module_dict = {
    'pd': 'pandas',
    'np': 'numpy',
    'requests': 'requests',
    'sci': 'scipy',
    'plt': 'matplotlib.pyplot',
    'Path': 'pathlib.Path',
        }
for k,v in lazy_module_dict.items():
    t = "@lazyobject\ndef {}():\n    return importlib.import_module('{}')".format(k, v)
    exec(t)

色々追加したり消したりしましたが、今は利用頻度高いけどimportにちょっと時間がかかる系統が残り上記で落ち着いています。

f:id:vaaaaaanquish:20181221125657p:plain
遅延numpy

- 認証周り -

基本的にパスワード入力をpexpectで処理しています。大体以下にも書いています。
xonshにおけるpexpectを利用した対話コマンド自動化 - Stimulator
ssh等は2要素認証等も含めて自動でやるようにしています。

 

1passwordを利用したpassword, one-time-passwordの取得

私は、ワンタイムパスワードも利用したい事を理由に全てのパスワード管理に1password、それらをshell上で利用するため1password-cliを使っています。
1Password command-line tool: Full documentation

import pexpect

# masterパスワードをstrで取得する(自分で書いて)
masterp=get_master_pasword()

# 1password-cliから1password.appへの認証
def _pass_auth():
    # 1pass auth
    p = pexpect.spawn("op signin my.1password.com --output=raw")
    while 1:
        i=p.expect([
            r'.*(Enter the password for).*',
            pexpect.EOF, pexpect.TIMEOUT], timeout=3)
        if i==0:
            p.sendline(masterp)
            p.sendline('')
        elif i in [1,2]:
            break
    return str(p.before.strip())[2:-1]


# サービスxのパスワードを取得
def _get_p(x):
    # get_pass
    $op_key = _pass_auth()
    p = $[echo $op_key | op get item @(x) | jq '.details.fields[] | select(.designation=="password").value']
    return p
aliases['getp'] = _get_p


# サービスxのワンタイムパスワードを取得
def _get_op(x):
    # get one time password
    print('auth 1password...')
    $op_key = _pass_auth()
    print('get one time pass...')
    p = $(echo $op_key | op get totp -v @(x))
    return p.strip()
aliases['getop'] = _get_op

masterパスワードをstrで取得する部分はファイル読み込みとかで自身で書いて欲しいです。
これでコマンドで、パスワードやワンタイムパスワードを取得できます。

$ getp yahoo
hoge1234

$ getop yahoo
987123

これをpbcopyコマンドとかに送ればクリップボードから直接ペーストできます。
最近の悩みは_pass_authが若干遅い事です。(多分認証キャッシュの機能で解消できるけどやってない)

 

sshの自動化

上記のパスワード取得の仕組みを利用しながらssh周りも自動化しています。
サーバの接続の際にも踏み台などでパスワード、ワンタイムパスワードの入力が必須な作業環境で便利です。

import pexpect

# masterパスワードをstrで取得する(自分で書いて)
masterp=get_master_pasword()

# 画面サイズ
import curses
curses.setupterm()
term_lines = int(curses.tigetnum("lines"))
term_cols = int(curses.tigetnum("cols"))

# ssh認証
def _ssh_pex(p):
    while 1:
        i = p.expect([
            r".*(Enter passphrase for key).*",
            r".*(Are you sure you want to continue).*",
            r".*(Verification code).*",
            pexpect.EOF, pexpect.TIMEOUT], timeout=3)
        if i==0:
            print('enter passphrase.')
            p.sendline(masterp)
            p.sendline('')
        elif i==1:
            print('continue: yes.')
            p.sendline('yes')
            p.sendline('')
        elif i==2:
            print('[auth]')
            otp = _get_op('yahoo')
            p.sendline(otp)
            p.sendline('')
        elif i in [3,4]:
            break
    p.interact()

こんな感じで書いておけば上手くいくと思います。
ワンタイムパスワードの期限がきれても_get_opリトライしてくれるので便利です。

これを利用してsshやscp周りを拡張しています。

import pexpect

def _ssha(x):
    if '/' in x[0]: x[0]=x[0].replace('/','')
    p = pexpect.spawn("ssh " + x[0]) #, encoding='utf-8')
    p.setwinsize(term_lines,term_cols)
    # p.logfile = sys.stdout
    _ssh_pex(p)
aliases["ssha"] = _ssha

def _scpa(x):
    if '/' in x[0]: x[0]=x[0].replace('/','')
    p = pexpect.spawn("scp -r " + x[0] + ' ' + x[1])
    _ssh_pex(p)
aliases["scpa"] = _scpa

なんかたまに入るゴミのために変な処理が入ってますが、大体これでなんとかなると思います。

これとconfig組み合わせてワンタイムパス必須なサーバへの接続も自動化しています

local $ ssha gpu001
[auth]
auth 1password...
get one time pass...[123456]
bastion server...
fowarding port [8888]

gpu001.server.hoge.co.jp $

この辺柔軟に書けるのは良いところだと思ってます。

 

ssh-hostの管理

会社でも趣味でもめちゃくちゃ沢山のhostにsshしたりコマンド飛ばしたりします。
そのため~/.ssh/configがぐちゃぐちゃになってしまう問題があったので、xonshコマンドで管理しています。
 
~/.ssh/configを雑にparseして表示、もしくはstrで返すxonsh関数です。
ptkのstyleを利用して、colorで出したり出さなかったりしています。

from prompt_toolkit import print_formatted_text
from prompt_toolkit.styles import Style
inquirer_style = Style.from_dict({
    'qa': '#5F819D',
    'qu': '#FF9D00',
    'dp': '#000'
})
def _get_host(color=False):
    all_text = ''
    text = ''
    for x in $(cat ~/.ssh/config).split('\n'):
        if 'LocalForward' in x:
            text += ', ' + x.strip().split(' ')[1]
        if 'HostName' in x:
            text += ', ' + x.strip().split(' ')[1]
        elif 'Host ' in x:
            if text!='':
                all_text += text + '\n'
            text = x.split(' ')[1]
    all_text += text + '\n'
    if not color:
        all_d = []
        for x in all_text.split('\n'):
            for i,y in enumerate(x.split(', ')):
                if i==0:
                    all_d.append(('class:qu', y))
                if i==1:
                    all_d.append(('', ', '))
                    all_d.append(('class:qa', y))
                    if len(x.split(', '))==2:
                        all_d.append(('','\n'))
                if i==2:
                    all_d.append(('', ', '))
                    all_d.append(('class:qp', y))
                    all_d.append(('','\n'))
        print_formatted_text(FormattedText(all_d),
                style=inquirer_style)
        return
    return all_text
aliases['host']=_get_host

こんな感じでhost一覧出したりgrepしたりpecoしたりしています。
f:id:vaaaaaanquish:20181221120604p:plain


これを利用して~/.ssh/configも読み書きできるようにしておくと、柔軟にお仕事できます。
一部切り抜いて紹介すると以下。

# _get_host()してparse
def _ssh_host_to_dic():
    host = {}
    for x in _get_host(True).split('\n'):
        rows = x.split(', ')
        if len(rows)>=2:
            host[rows[0]]={'s':rows[1], 'p':None}
        if len(rows)==3:
            host[rows[0]]['p']=rows[2]
    return host

# config生成
def _w_host(host):
    # 中身はよしなに
    with open('~/.ssh/config', 'w') as f:
        f.write('HostKeyAlgorithms +ssh-dss\n')
        f.write('AddKeysToAgent yes\n')
        f.write('host *\n')
        f.write('    ForwardAgent yes\n')
        for k,v in host.items():
            f.write('\nHost {}\n'.format(k))
            f.write('    HostName {}\n'.format(v['s']))
            if k != 'hoge':
                f.write('    IdentityFile ~/.ssh/id_rsa\n')
                f.write('    ProxyCommand ssh hoge.co.jp ncat %h %p\n') # springboard
                f.write('    IdentitiesOnly yes\n')
                if v['p'] is not None:
                    f.write('    LocalForward {} localhost:{}\n'.format(v['p'],v['p'])) #LocalForward

import argparse
def _update_host(args):
    host = _ssh_host_to_dic()
    parser = argparse.ArgumentParser()
    parser.add_argument('name')
    parser.add_argument('-s')
    parser.add_argument('-p')
    args = parser.parse_args(args)
    if args.name not in host.keys():
        host[args.name]={'s':None, 'p':None}
    if args.s is not None:
        host[args.name]['s'] = args.s
    if args.p is not None:
        host[args.name]['p'] = args.p
    _w_host(host)
aliases['uh'] = _update_host

ちょっと切り抜きなので雑ですが、こんなん作っとけば以下でconfig更新したりしてサーバが増減しても安心です。

$ cat ~/.ssh/config
HostKeyAlgorithms +ssh-dss
AddKeysToAgent yes

$ uh hogehoge -s hoge.server -p 9920
$
$ cat ~/.ssh/config
HostKeyAlgorithms +ssh-dss
AddKeysToAgent yes
host *
    ForwardAgent yes

Host hoge
    HostName hoge.server
    IdentityFile ~/.ssh/id_rsa
    IdentitiesOnly yes
    LocalForward 9920 localhost:9920

あとは、自身が欲しいままにconfig_generatorを作っておけば安心。
Tipsですが、ちょっと複雑なコマンド作るときはargparseしておくと --help でオレオレdiscription出せるのでいい感じに使えます。

 

gitアカウントの管理

gheとかgitのcliは充実してますが、自分は以下だけ。
いくつかあるgitアカウント間違えないように変更しやすくしています。

# git chenge
def g_change(account):
    account = account[0]
    if account=='vanquish': account='vaaaaanquish'
    git config --global user.name f"{account}"
    if account=='hoge':
        git config --global user.email hoge@company.jp
        print(f'change:{account}')
    elif account=='vaaaaanquish':
        git config --global user.email 6syun9@gmail.com
        print(f'change:{account}')
    else:
        print(f'check account name:{account}')
aliases['gac']=g_change

gitacコマンドで複数のアカウントを切り替えて、上記「 環境変数」の項目で書いたようなスクリプトでshellのPROMPTに表示しています。

hoge $
hoge $ gac vanquish
shukawai $

多分もっと優勝できると思ってます。

 

- 移動、操作 -

移動はもっぱらzとpecoを使っています。
GitHub - peco/peco: Simplistic interactive filtering tool
GitHub - rupa/z: z - jump around

xontrib-zを導入しても良いですし、以下のように書いても良い感じに使えると思います。

def z():
    lines = open($DIR_HIST_PATH[0]).read().rstrip("\n").split("\n")
    return("\n".join([p for p, c in Counter(lines).most_common()]))

 

履歴の取得

以下の記事で紹介されているものを利用しています。
Xonshを快適にするptk(Python Prompt Toolkit) - Qiita
以下の記事と書きましたが、書いたのは元同僚で私にxonshを薦めてきた諸悪の根源です。

import os
import json
from collections import OrderedDict

def get_history(session_history=None, return_list=False):
    hist_dir = __xonsh__.env['XONSH_DATA_DIR']
    files = [ os.path.join(hist_dir,f) for f in os.listdir(hist_dir)
              if f.startswith('xonsh-') and f.endswith('.json') ]
    file_hist = []
    for f in files:
        try:
            file_hist.append(json.load(open(f))['data']['cmds'])
        except:
            pass
    cmds = [ ( c['inp'].replace('\n', ''), c['ts'][0] )
                 for cmds in file_hist for c in cmds if c]
    cmds.sort(key=itemgetter(1))
    cmds = [ c[0] for c in cmds[::-1] ]
    if session_history:
        cmds.extend(session_history)
    # dedupe
    zip_with_dummy = list(zip(cmds, [0] * len(cmds)))[::-1]
    cmds = list(OrderedDict(zip_with_dummy).keys())[::-1]
    cmds = reversed(cmds)
    if return_list:
        return cmds
    else:
        return '\n'.join(cmds)

後述するkeybindとpecoの連携によって、過去のコマンド実行をあいまい検索できるようにしています。

 

dirの保存

移動したディレクトリは、on_chdirで発火する関数を作成しファイルでざっくり管理しています。

# file save dir
$DIR_HIST_PATH = "~/.dirhist"
@events.on_chdir
def add_to_file(olddir, newdir, **kw):
    with open($DIR_HIST_PATH[0], 'a') as dh:
        print(newdir, file=dh)

これも後述のkeybindとpecoの連携部分で最近行ったdirをpecoで利用したいがためです。
普段はこれをもうちょっと拡張して、特定の作業ディレクトリに行きやすくなったりするようにしています。

 

keybindings

キーバインドです。
何でもかんでもpecoに流しています。

from prompt_toolkit.keys import Keys
from prompt_toolkit.filters import (Condition, IsMultiline, HasSelection, ViInsertMode)


@events.on_ptk_create
def custom_keybindings(bindings, **kw):

    # ctrl+vで入力中の単一、複数行コマンドをvimで開く
    @bindings.add('c-v')
    def edit_in_editor(event):
        event.current_buffer.tempfile_suffix = '.py'
        event.current_buffer.open_in_editor(event.cli)

    # ctrl+rで過去の実行コマンドをpeco
    @bindings.add('c-r')
    def select_history(event):
        sess_history = $(history).split('\n')
        hist = get_history(sess_history)
        selected = $(echo @(hist) | peco)
        event.current_buffer.insert_text(selected.strip())

    # ctrl+sでssh_config内のhost先をpeco
    @bindings.add('c-s')
    def select_ssh(event):
        hosts = _get_host(True)
        selected = $(echo @(hosts) | peco)
        if selected:
            event.current_buffer.insert_text('ssha ' + selected.strip().split(', ')[0])

    # ctrl+fで今いるディレクトリのfileをpeco
    @bindings.add('c-f')
    def select_file(event):
        r = lambda x: './'+x if os.path.isdir(x) else x
        files = '\n'.join([r(x.split(' ')[-1]) for x in $(ls -l).split('\n')])
        selected = $(echo @(files) | peco)
        event.current_buffer.insert_text(selected.strip())

    # ctrl+dで過去移動したディレクトリをpeco
    @bindings.add('c-d')
    def _z(event):
        selected = $(echo @(z()) | peco)
        cd_cmd = "cd " + selected.strip()
        event.current_buffer.insert_text(cd_cmd)

    # ctrl+tで翻訳コマンドを入力
    @bindings.add('c-t')
    def _engs(event):
        event.current_buffer.insert_text('t ')

    ....

利用してるkeybinding、実際はもっとあるんですが、業務に関わる部分が多かったものは削っています。

f:id:vaaaaaanquish:20181221135842g:plain
sshやcommandをpecoであいまい検索

tコマンドは後述しますがGoogle翻訳としてよく使っているので書いときました。


 

- other -

大体上記のような環境でやっていってますが、他に結構使ってる便利なお手製コマンドを書いておきます。

 

google-translation

仕事で英語圏メンバーとMTGを毎日してますが、英語力低いので翻訳が手元に欲しかったりします。
GitHub - soimort/translate-shell: Command-line translator using Google Translate, Bing Translator, Yandex.Translate, etc.

keybindingで ctrl+t でt を入力、あとは日本語か英数字か正規表現で判定してGoogle翻訳かけるというコマンドを作っています。
VivaldiなるブラウザのWebパネルにもGoogle翻訳をセットしていますが、Shellだとそのままpbcopyでクリップボードに流したり、英語か日本語かの判定文自分で雑にかけるので何だかんだ結構こちらを使っています。

import re

jap = re.compile('[あ-んア-ン一-鿐]')
def _eng(x):
    if len(x)==0: return
    x = ' '.join(x)
    if jap.search(x) is None:
        y = $(trans en:ja @(x))
    else:
        y = $(trans ja:en @(x))
    return y
aliases['t'] = _eng
vaaaaanquish $ t 翻訳
翻訳
(Hon'yaku)

translation

翻訳 の定義
[ 日本語 -> English ]

名詞
    translation
        翻訳, 訳書, 翻訳物, トランスレーション
    deciphering
        解読, 翻訳

翻訳
    translation

最初はDMM英会話の「英語でなんていうknow」なるサービスがすごくよくてスクレイピングするコマンドを作って多用していましたが、慣用句以外なら単語さえわかればconversationに盛り込めるくらいにはなってきたので今はこれだけに落ち着いています。

ドキュメントやコード内の謎の英語とか調べるのにも重宝しています。

 

画像の表示

ディレクトリ内の画像から雑にsampleして、iterm2経由で表示するやつです。
画像処理なんかをやってる時に、「あ〜、このtrainディレクトリって何入ってんだっけ」となるので使ってます。

import matplotlib.pyplot as plt
import xontrib.mplhooks
import numpy as np
import cv2
import random
from mimetypes import guess_extension


def _imgls(path):
    fig, ax = plt.subplots(3, 3, sharex='col', sharey='row')
    fig.set_size_inches(10, 10)
    d=[]
    for x in os.listdir(path):
        if x.split('.')[-1] in ('jpe', 'jpeg', 'png'):
            d.append(path+x)
    if len(d)>9:
        d = random.sample(d,9)
    for i, imgid in enumerate(d):
        col = i % 3
        row = i // 3
        img = cv2.imread(imgid)
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        ax[row, col].imshow(img)
    xontrib.mplhooks.display_figure_with_iterm2(fig)
aliases['imgls'] = _imgls

f:id:vaaaaaanquish:20181221175030p:plain
ディレクトリ内の画像をshell上でsample

以下あたりを参考に作っていて、基づいて少し変更すればLinuxやwinでも動きます。
Pythonでyahoo画像検索した結果をimgcatに流して表示してURLをクリップボードにコピーするやつ - Stimulator
Xonshでmatplotlibグラフをコンソールにインライン描画してメモリ状況を観察する - Stimulator

これが結構便利なので割と使います。


 

おわりに

1つ1つ記事にも出来ましたが、Xonsh Advent Calendar 2018 - Qiitaがまさかの大盛況で助かりました。

一旦業務部分を切り取ってとりあえず書いたので、今後もうちょっとこの記事は更新されると思います。
業務的なところでは、社内サーバの起動から、GPUクラスタGPUを監視してリソース奪い取るコマンド、勤怠や日報の自動化、社内チャットへの投稿、社内のMTGルームの取得…など全部適当に書いたコマンドで何とかしています。(なかなか切り分けできてないので見せられないですが)


サッと書いたスクリプトがxonshrcに入り常用してしまうという1年だったため、結構rc自体が汚く肥大化しています。

今現在も、xonshrcのリファクタリングとか、コマンドのhistory backendをAWSに乗せるとか、起動の高速化とかをやっていきたいと思ってるので2019年も更にがんばっていきたいと思います。


それではhappy xonsh year。