「第33回シェル芸勉強会 大阪サテライト」の復習
2018年1月27日に開催された「第33回シェル芸勉強会 大阪サテライト」に参加しました。
ディレクトリの操作を中心とした問題が出されましたが、メイン会場の講師である上田隆一(@ryuichiueda)さんの
過去最高の酷さです
今回は解けません
という警告の通り、凶悪ともいえる高難度の回となりました。
午後の部:
問題と解答例:
動画:
A1:
$ yes 💩 | head -n10 | xargs | tr ' ' '/' | xargs -I@ bash -c "mkdir -p @"
$ tree
.
└── 💩
└── 💩
└── 💩
└── 💩
└── 💩
└── 💩
└── 💩
└── 💩
└── 💩
└── 💩
10 directories, 0 files
1問目ということで、使っている絵文字を除いては穏当な問題でした。
大まかな手順は、
yes 💩 | head -n10
で「💩」を10ヶ得るxargs
を利用してスペース区切りで1行に並べるtr ' ' '/'
で区切り文字をスペースから「/」に置換xargs -I@ bash -c "mkdir -p @"
でディレクトリを一気に作成
になっています。
A2:
$ cowsay 'Eat American beef' | perl -nlE 's|/|%|g;open($fh,">",$_);sleep 1' && ls -ltr | tr '%' '/'
合計 0
-rw-r--r-- 1 mitsuhisa mitsuhisa 0 1月 31 02:15 ___________________
-rw-r--r-- 1 mitsuhisa mitsuhisa 0 1月 31 02:15 < Eat American beef >
-rw-r--r-- 1 mitsuhisa mitsuhisa 0 1月 31 02:15 -------------------
-rw-r--r-- 1 mitsuhisa mitsuhisa 0 1月 31 02:15 \ ^__^
-rw-r--r-- 1 mitsuhisa mitsuhisa 0 1月 31 02:15 \ (oo)\_______
-rw-r--r-- 1 mitsuhisa mitsuhisa 0 1月 31 02:15 (__)\ )\/\
-rw-r--r-- 1 mitsuhisa mitsuhisa 0 1月 31 02:15 ||----w |
-rw-r--r-- 1 mitsuhisa mitsuhisa 0 1月 31 02:15 || ||
paste
を使った当日の解答は出力こそ正しいですが、今回のテーマである「不穏なディレクトリ」とはそぐわないので、改めて解答してみました。
cowsay
の出力を一行ずつに分け、それをファイル名としたファイルを作成するためにperl -nlE 's|/|%|g;open($fh,">",$_);sleep 1'
というPerlワンライナーを仕込んでいますが、内容としては、
- ファイル名に「/」は使えないので「%」に置換
- ファイルを作成
- ファイル更新時間でソートできるように、次行の処理の前に1秒スリープする
という具合になっています。
こうして得られたファイル群をタイムスタンプの古い順にソートし、「%」を「/」に置換し直せばOKです。
A3:
$ echo {,k,s,t,n,h,m,y,r,w}{a,i,u,e,o} n | sed 's/si/shi/;s/ti/chi/;s/tu/tsu/;s/hu/fu/;s/ /\n/g' | uconv -x hiragana | awk '{print NR==37||NR==39?" ":NR>=47&&NR<=49?"":$1}' | xargs -n5 | tr -d ' ' | xargs touch && ls
あいうえお かきくけこ さしすせそ たちつてと なにぬねの はひふへほ まみむめも や ゆ よ らりるれろ わをん
平仮名50音の文字を得る方法が当日には思いつかず、宿題として解くことにしました。
bash
のブレース展開を使うと50音を訓令式ローマ字表記で得ることができますが、どうやらuconv
はヘボン式でないと正しく平仮名に変換してくれないため、あらかじめsed
で置換しておきます。
こうして整形したローマ字のデータをuconv -x hiragana
に与えると1行につき1文字ずつ平仮名の出力を行いますので、不要な文字を全角スペースに置換あるいは削除し、xargs -n5
を使い1行につき5フィールドに並べた後tr -d ' '
でスペースを削除したものをファイル名としてxargs touch
に与えるとファイルの設置が完了します。
A4:
$ cat a b c d e
* *
2017/01/26
~
-
--
$ ls | xargs -n1 -I@ echo 'echo @ > "$(cat @)" || mkdir -p "$(cat @ | sed "s%/[^/]\+$%%")" && echo @ > "$(cat @)" ; rm @' | sh 2>/dev/null
$ tree
.
├── * *
├── -
├── --
├── 2017
│ └── 01
│ └── 26
└── ~
2 directories, 5 files
xargs
で解答の条件を満たすコマンドラインを生成するのが非常に難しい問題でした。ちなみに、当日の解答は誤りでした。
xargs
で生成し、後続のsh
に与えるコマンドラインecho @ > "$(cat @)" || mkdir -p "$(cat @ | sed "s%/[^/]\+$%%")" && echo @ > "$(cat @)" ; rm @
は次のような内容になっています。なお、「@」の箇所にls
で得たファイル名が入っています。
- ファイル名を、そのファイルの中身を名前に持つ新規ファイルにリダイレクト
- ファイルの中身に「/」が含まれている場合は1.が失敗するので、
sed
で最後の「/」以降を削除してディレクトリ名を生成し、新規ディレクトリを作成 - 2.が成功した場合は、1.と同様に新規ファイルを作成
- 元のファイルを削除
このコマンドラインをパイプでsh
に渡せば終了です。なお、エラー出力は/dev/null
に捨てています。
A5:
$ ls -b | wc -l
3
ls
の-b
オプションを使えば簡単に解くことができました。
-b, –escape
表示不可能な文字の場合に C 形式のエスケープ文字を表示する
ちなみに、ファイル名は次のようになっています。
$ ls -b
\n\n\n\n
\n\n\n\n\n\n\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
A6:
$ seq -w 0 9999 | perl -C -nlE 'for(split("")){$_+=0x30A1;$s.=chr};say $s;$s=""' | xargs touch
$ ls | wc -l
10000
当日に解くことができませんでしたので、宿題として解答しました。
問題文では生成するファイル数が100万個になっていますが、iノードが枯渇する可能性があるので1万個に減らして解答してみました。
手順としては、
seq
に-w
オプションを付けて実行し、「0000」から「9999」までを得る- ワイド文字を処理できるように
-C
オプションを付けたperl
で1行ごとに次の処理を行う:- 入力文字列を空文字列で分割し、それぞれの数字に16進数で「30A1」を加算したものを
chr
関数で文字に変換後、変数$s
に連結して格納 - 変数
$s
を出力 - 次行での処理のために変数
$s
を空文字列でリセット
- 入力文字列を空文字列で分割し、それぞれの数字に16進数で「30A1」を加算したものを
xargs
で1項目ごとにtouch
してファイルを生成
になっています。
なお、ファイル名を片仮名でなく平仮名にしたい場合は「0x30A1」を「0x3041」に、漢字にしたい場合は「0x4E00」に置き換えてください。
A7:
$ seq -w 0 9999 | tr '0-9' '\020-\x31' | xargs touch
$ ls -b | wc -l
10000
$ ls -b | head
\020\020\020\020
\020\020\020\021
\020\020\020\022
\020\020\020\023
\020\020\020\024
\020\020\020\025
\020\020\020\026
\020\020\020\027
\020\020\020\030
\020\020\020\031
この問題文でも生成するファイル数が100万個になっていますが、A6と同じ理由で1万個に減らしています。
見えない字でファイル名を生成するために、seq
で得たそれぞれの数字をtr
で適当なASCII制御文字に置換した後、xargs touch
すればOKです。
A6よりも難度は低いのですが、当日に解答できなかった程度には難しかったです。
A8:
perl -E '@files=glob("*");for(@files){$len=length;open($file,">","$_\0");say $file $len;close $file}'
Perlワンライナーで解いてみました。
glob("*")
でディレクトリ内の全ファイル名のリストを取得し、そのリストの要素ごとに次の処理
length
でファイル名の長さを取得し、変数$len
に代入- 特殊変数
$_
に格納されたリストの要素1ヶの値をファイル名(安全のため末尾にヌル文字を追加)としてファイルを開き、ファイルハンドル$file
に結びつける - ファイルハンドル
$file
に対して変数$len
の値を出力(ファイルへの書き込み) - ファイルハンドル
$file
を閉じる
を行っています。
LT大会:
動画:
Amazon Dash Hack
- 発表者: MSR(@msr386)さん
Dasherを使い、Amazon Dash Buttonでシェルコマンドを実行できるようにするという内容です。
おうちハックから危険シェル芸まで色々応用が利きそうです。
Contributions Graph 芸
- 発表者: so(@3socha)さん
いわゆる「GitHubの芝生」っぽいものをシェル芸でターミナル上に出力するという内容です。
ちなみに、私のContributions Graphはほぼ真っ白です。
Dockerを使ったクライアントハイパーバイザー
- 発表者: くんすと(@kunst1080)さん
Dockerでデスクトップ仮想化を行い、イミュータブルな開発環境を手に入れるという内容です。
自分の気に入った作業環境を一から作るのは楽しいとはいえ結構面倒くさいものがあるので、どのマシンでも同じ環境を手早く構築できるというのはなかなか便利そうです。
macOS 濁点問題にシェル芸で挑んだ話
- 発表者: 小原一哉(@KoharaKazuya)さん
Unicode正規化の正規化形式がmacOS(NFD)とその他のOS(NFC)で異なり、ファイル名などで混乱が生じるため、濁点が2文字で表現(例えば「か」+「゛」)されているかシェル芸で検出できるようにするという内容です。
蛇足ですが、印刷業界ではmacOS率が高いため、発注先から支給される版下のファイル名が「○○テ゛ータ」のようになっていることがよくあります。
参考リンク:
Thanx:
- 大阪サテライト:
- 主催: くんすと(@kunst1080)さん、so(@3socha)さん
- 会場: フェンリル株式会社 大阪本社様
- メイン会場:
- 講師: 上田隆一(@ryuichiueda)さん