「第33回シェル芸勉強会 大阪サテライト」の復習

2018年1月27日に開催された「第33回シェル芸勉強会 大阪サテライト」に参加しました。

ディレクトリの操作を中心とした問題が出されましたが、メイン会場の講師である上田隆一(@ryuichiueda)さんの

過去最高の酷さです
今回は解けません

という警告の通り、凶悪ともいえる高難度の回となりました。


午後の部:

問題と解答例:

動画:

A1:

$ yes 💩 | head -n10 | xargs | tr ' ' '/' | xargs -I@ bash -c "mkdir -p @"
$ tree
.
└── 💩
    └── 💩
        └── 💩
            └── 💩
                └── 💩
                    └── 💩
                        └── 💩
                            └── 💩
                                └── 💩
                                    └── 💩

10 directories, 0 files

1問目ということで、使っている絵文字を除いては穏当な問題でした。

大まかな手順は、

  1. yes 💩 | head -n10で「💩」を10ヶ得る
  2. xargsを利用してスペース区切りで1行に並べる
  3. tr ' ' '/'で区切り文字をスペースから「/」に置換
  4. 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. ファイル名に「/」は使えないので「%」に置換
  2. ファイルを作成
  3. ファイル更新時間でソートできるように、次行の処理の前に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. ファイル名を、そのファイルの中身を名前に持つ新規ファイルにリダイレクト
  2. ファイルの中身に「/」が含まれている場合は1.が失敗するので、sedで最後の「/」以降を削除してディレクトリ名を生成し、新規ディレクトリを作成
  3. 2.が成功した場合は、1.と同様に新規ファイルを作成
  4. 元のファイルを削除

このコマンドラインをパイプで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万個に減らして解答してみました。

手順としては、

  1. seq-wオプションを付けて実行し、「0000」から「9999」までを得る
  2. ワイド文字を処理できるように-Cオプションを付けたperlで1行ごとに次の処理を行う:
    1. 入力文字列を空文字列で分割し、それぞれの数字に16進数で「30A1」を加算したものをchr関数で文字に変換後、変数$sに連結して格納
    2. 変数$sを出力
    3. 次行での処理のために変数$sを空文字列でリセット
  3. 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("*")でディレクトリ内の全ファイル名のリストを取得し、そのリストの要素ごとに次の処理

  1. lengthでファイル名の長さを取得し、変数$lenに代入
  2. 特殊変数$_に格納されたリストの要素1ヶの値をファイル名(安全のため末尾にヌル文字を追加)としてファイルを開き、ファイルハンドル$fileに結びつける
  3. ファイルハンドル$fileに対して変数$lenの値を出力(ファイルへの書き込み)
  4. ファイルハンドル$fileを閉じる

を行っています。


LT大会:

動画:

Amazon Dash Hack

Dasherを使い、Amazon Dash Buttonでシェルコマンドを実行できるようにするという内容です。

おうちハックから危険シェル芸まで色々応用が利きそうです。

Contributions Graph 芸

いわゆる「GitHubの芝生」っぽいものをシェル芸でターミナル上に出力するという内容です。

ちなみに、私のContributions Graphはほぼ真っ白です。

Dockerを使ったクライアントハイパーバイザー

Dockerでデスクトップ仮想化を行い、イミュータブルな開発環境を手に入れるという内容です。

自分の気に入った作業環境を一から作るのは楽しいとはいえ結構面倒くさいものがあるので、どのマシンでも同じ環境を手早く構築できるというのはなかなか便利そうです。

macOS 濁点問題にシェル芸で挑んだ話

Unicode正規化の正規化形式がmacOS(NFD)とその他のOS(NFC)で異なり、ファイル名などで混乱が生じるため、濁点が2文字で表現(例えば「か」+「゛」)されているかシェル芸で検出できるようにするという内容です。

蛇足ですが、印刷業界ではmacOS率が高いため、発注先から支給される版下のファイル名が「○○テ゛ータ」のようになっていることがよくあります。


参考リンク:


Thanx:

Written on February 11, 2018