glueで年末年始シェル芸問題集を解いた雑感(Q4まで)
Sat Jan 10 18:52:34 JST 2015 (modified: Mon Aug 19 17:52:24 JST 2019)
views: 2100, keywords:コマンド,glue,GlueLang,シェル芸,グルー言語を作る この記事は最終更新日が5年以上前のものです。
私以外のコントリビュータも出現し、試してくださる方も出て引くに引けない状況のGlueLangですが、ちょっと使った例と、自分で使ってみたフィードバックが必要かなと思い、自分で出題した年末年始シェル芸問題集の問題をGlueLangで解いてみようと思いました。
答えはMac用です。
Q1
今のところPATHをGlueLangに読ませていないので、コマンドを使うときはimportでパスを指定するか、フルパスで呼び出す必要がありますが、xargsに渡したgmd5sumではフルパスでしか指定できない問題が発覚(というよりウスウス気づいてましたが・・・)。あとはオプションをいちいちシングルクォートするのはやはり面倒かなと。
あとは、importの後の空行にスペースを入れるとちゃんとパースしてくれませんね・・・。
import /usr/bin/ as ub
import /usr/local/bin/ as ulb
.find '/Users/ueda/' '-type' 'f' >>=
ub.grep '-i' '\\.jpg$' >>=
ub.sed 's/.*/"&"/' >>=
ub.xargs '-n' '1' '/usr/local/bin/gmd5sum' >>=
ub.sort '-s' '-k1,1' >>=
ub.awk '{if(a==$1){print b;print $0}a=$1;b=$0}' ulb
パスやその他環境変数については、
import PATH
import LANG
というように簡単に読み込めるようにしようかと思います。
オプションについてはハイフンで始まったり数字で始まるものはクォートを省略できる方がいいかも。
Q2
ワンライナーなのでほとんど普通のシェルスクリプトと変わりません。しかし、標準エラー出力の制御を今のところ実装していないというのはやはり使いづらい。
import /usr/local/bin/ as ulb
import /usr/bin/ as ub
.curl 'http://www.flightradar24.com/_json/airports.php' >>=
ub.jq '.' >>= ub.grep '-C' '6' 'HND' ulb
エラーは普通にリダイレクトでとれるようにしておけば良いか・・・。次のような感じで。
.curl 'http://www.flightradar24.com/_json/airports.php' log> '/dev/null' >>= ... ub
ログを見るだけなら上の書式にして、標準エラー出力をGlueLang内で積極的に使うときは、fileの後に二つファイルを並べる方式になると思います。
# ファイルオブジェクトfにcurlの標準出力、errにエラー出力が入る
= ub.curl 'http://www.flightradar24.com/_json/airports.php' file f err
Q3
基本的にはワンライナーをGlueLangの方法に書き換えるだけです。
import /usr/local/bin/ as ulb
import /usr/bin/ as ub
.seq '1' '1000' >>=
ub.awk '{for(i=1;i<=$1;i++){printf("%d ",i)}{print ""}}' >>=
ulb.tr ' ' '*' >>=
ub.sed 's/\\*$/)/' >>=
ub.sed 's:^:1/(:' >>=
ub.bc '-l' >>=
ub.tr '\\n' '+' >>=
ub.sed 's/$/1/' >>=
ub.bc '-l' ub
しかし、もとがワンライナーなので意味不明なのは、GlueLangの宣伝としてはどうなのか??
ということで、別解をいただきましたので、実装してみたいと思います。
2015!の階乗はいろいろ試したけど、結局これだけで済むようだ。#シェル芸 $ clisp -x '(! 2015)' -q
— 本名 (@tamago_girai) January 4, 2015
はい。
/usr/local/bin/clisp '-x' '(! 2015)' '-q'
Q4
このbashのワンライナーを書き直せばよいのですが・・・
uedambp:~ ueda$ a=$(curl http://blog.ueda.asia/misc/message2015.txt) ;
while a=$(echo $a | base64 -D) && echo $a ; do : ; done
・・・さて、whileがないGlueLangでどうやって処理しよう・・・。コマンド作っちゃえ。ということで、次のようなGlueLang用のコマンドを作りました。
uedambp:GlueLang ueda$ cat ./stdcom/loop-serial
#!/bin/bash
# loopserial command
# usage: loopserial <commmand> <args...>
# Multiapply trys the command until the command
# is failed, and outputs the output of the last successful trial.
# version: "Sat Jan 10 16:58:28 JST 2015"
# This command should be rewritten with C/C++.
[ "$#" -lt 1 ] && exit 1
tmp=/tmp/$$
"$@" > $tmp-keep
while [ $? -eq 0 ] ; do
mv $tmp-keep $tmp-out
cat $tmp-out | "$@" > $tmp-keep 2> /dev/null
done
cat $tmp-out
rm -f $tmp-*
exit 0
んで、Glueで使ってみます。
:SHELL_GEI_2015SP ueda$ cat Q4.mac.glue
uedambpimport /usr/bin/ as ub
import /bin/ as b
import /Users/ueda/GIT/GlueLang/stdcom/ as std
.curl 'http://blog.ueda.asia/misc/message2015.txt' >>=
ub.loop-serial '/usr/bin/base64' '-D' std
はい実行。
:SHELL_GEI_2015SP ueda$ ../../glue Q4.mac.glue 2> /dev/null
uedambp:(){: | : &};:
bashで書いているコマンドを実行していることで他力本願極まりないですが、bashで書いたコマンドをCで書き直して、GlueLangの付属コマンドにしてしまうつもりです。GlueLangを作るにあたり、もともと制御構文をなくしたいという動機があったので、これでいいんでないかと思います。
ただ、whileがないと初心者向けにはならないので、ゆくゆくは実装するつもりです。しかし、コマンドを繰り返し実行するパターンにはあまりバリエーションがないので、loop-serialのようなコマンドをいくつか作ってそっちを使うことを推奨したいと考えています。
おわりに
今回は4問しか解きませんでしたが、これだけでも改善点がいろいろ出てきたので、開発に移りたいと思います・・・。やっぱりPATH周りは早くなんとかしたいかなと。
おしまい。