今回は関西から、Twitter参加しました。


模範解答は上記の上田さんのブログの方にあるので、ここでは自分の出した変な回答の解説などを……

### Q1. 次のように、画面にバッテンを描いてください。(この出力例の大きさは21×21です。) ``` x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x ```https://twitter.com/kunst1080/status/495441002060980224
https://twitter.com/kunst1080/status/495458118512283649
考え方は以下のようにしました
  • 空白を0、Xを1として数列を作成する
  • できあがった数列を文字列置換する

まず前半 ``` $ (jot 9|sort -rn;echo 0;jot 9) 9 8 7 6 5 4 3 2 1 0 1 2 3 4 5 6 7 8 9 ```9〜0〜9の数列を作成しています。あとでまとめてパイプに渡したいため、両端をカッコでくくっています。
数列を出力するために、FreeBSDなのでjotコマンドを使用していますが、Linux系の人はseqコマンドが使えると思います。どちらもなかったらyesとawkでがんばる感じですかね。 ``` $ (jot 9|sort -rn;echo 0;jot 9) | awk '{printf "%010d\n", 10^$1}' 1000000000 0100000000 0010000000 0001000000 0000100000 0000010000 0000001000 0000000100 0000000010 0000000001 0000000010 0000000100 0000001000 0000010000 0000100000 0001000000 0010000000 0100000000 1000000000 ```さっきの数列に対して、10のN乗を計算しています。また、awkのprintf関数で左ゼロ埋めをしています。
「X」の左側に見えてきましたね・・・ ``` $ (jot 9|sort -rn;echo 0;jot 9) | awk '{printf "%010d\n", 10^$1}' | xargs -I% echo "echo -n %;echo % | rev" echo -n 1000000000;echo 1000000000 | rev echo -n 0100000000;echo 0100000000 | rev echo -n 0010000000;echo 0010000000 | rev echo -n 0001000000;echo 0001000000 | rev echo -n 0000100000;echo 0000100000 | rev echo -n 0000010000;echo 0000010000 | rev echo -n 0000001000;echo 0000001000 | rev echo -n 0000000100;echo 0000000100 | rev echo -n 0000000010;echo 0000000010 | rev echo -n 0000000001;echo 0000000001 | rev echo -n 0000000010;echo 0000000010 | rev echo -n 0000000100;echo 0000000100 | rev echo -n 0000001000;echo 0000001000 | rev echo -n 0000010000;echo 0000010000 | rev echo -n 0000100000;echo 0000100000 | rev echo -n 0001000000;echo 0001000000 | rev echo -n 0010000000;echo 0010000000 | rev echo -n 0100000000;echo 0100000000 | rev echo -n 1000000000;echo 1000000000 | rev ```「さっき作った文字列をrevコマンドで左右反転してくっつけるコマンド」をまず作成しています。paste使ってもよさそうですが…
echo -n は、改行せずにechoするためのオプションです。 ``` $ (jot 9|sort -rn;echo 0;jot 9) | awk '{printf "%010d\n", 10^$1}' | xargs -I% echo "echo -n %;echo % | rev" | sh 10000000000000000001 01000000000000000010 00100000000000000100 00010000000000001000 00001000000000010000 00000100000000100000 00000010000001000000 00000001000010000000 00000000100100000000 00000000011000000000 00000000100100000000 00000001000010000000 00000010000001000000 00000100000000100000 00001000000000010000 00010000000000001000 00100000000000000100 01000000000000000010 10000000000000000001 ```作成したコマンドをshコマンドに食わせて実行しました。どう見てもXになっています。
あとは、1→X、0→スペース の変換をするだけです。 ``` $ (jot 9|sort -rn;echo 0;jot 9) | awk '{printf "%010d\n", 10^$1}' | xargs -I% echo "echo -n %;echo % | rev" | sh | tr '01' ' X' X X X X X X X X X X X X X X X X X X XX X X X X X X X X X X X X X X X X X X ```……なんだか少しお題とは少し違う形をしてますが。。。trコマンドを使って文字の置換をしました。 ``` tr '01' ' X' ```で、'0'を' 'に、'1'が'X'に、まとめて置換できます。
こんな風に、sedを使わなくてもtrコマンドでも置換ができます。
### Q4. 北から順(正確には都道府県番号順)に並べてください。 ``` $ cat pref 鹿児島県 青森県 大阪府 群馬県 ```https://twitter.com/kunst1080/status/495454679942168576
https://twitter.com/kunst1080/status/495455438087794688
WEBの情報を活用して作業をする、という観点の問題でした。
頭を固くせず、使えるものは使いましょうっていうことですね。curlで取ってきた段階だと文字コードがSJISなので、nkf -w でUTF-8に変換します。 ``` $ curl elze.tanosii.net/d/kenmei.htm -s | nkf -w <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <HTML> <HEAD> <META http-equiv="Content-Type" content="text/html; charset=Shift_JIS"> <META http-equiv="Content-Style-Type" content="text/css"> <TITLE>県番号,都道府県名,県庁所在地一覧</TITLE> </HEAD> <BODY> (以下略 ```charset=Shift_JISと書いてあり、元々Shift JISだったと思われますが、nkfでUTF-8になりコンソールで見えるようになっています。で、これにgrepしてほしいデータの行だけを抽出します。ここで ``` grep -of ```を使うとスマートにできます。(@TSB_KZKさんに教えていただきました。)
manによると
  • -o, --only-matching…マッチした行の、マッチした部分だけを出力
  • -f FILE, --file=FILE…検索パターンをファイルから読み込む
ということです。繋げると、 ``` $ curl elze.tanosii.net/d/kenmei.htm -s | nkf -w | grep -of pref 青森県 群馬県 大阪府 鹿児島県 ```
### Q5. 各行の数字を大きい順にソートしてください。 ``` $ cat input A 31 1234 -42 4 B 10 31.1 -34 94 ```https://twitter.com/kunst1080/status/495463901664784384
なんぞこれ……
考え方は以下のようにしました
  • 1列目の名前で1行のデータを持つ中間ファイルを作成する
  • それぞれの中間ファイルをソートしてからくっつける

まずはこのawkの塊ですが、最後のshを外すとこうなります。 ``` $ awk '{for(i=2;i<NF;i++){f[NR]=$1;print "echo",$i">>"$1}}END{for(i=1;i<NR+1;i++){print"(echo",f[i],";sort -n",f[i]")|xargs"}}' input echo 31>>A echo 1234>>A echo -42>>A echo 10>>B echo 31.1>>B echo -34>>B (echo A ;sort -n A)|xargs (echo B ;sort -n B)|xargs ```END句も外してみて確認です。 ``` $ awk '{for(i=2;i<NF;i++){f[NR]=$1;print "echo",$i">>"$1}}' input echo 31>>A echo 1234>>A echo -42>>A echo 10>>B echo 31.1>>B echo -34>>B ```「2列目以降の内容を、1列目の名前のファイルに出力するコマンド」が並びます。
またこのとき、連想配列「f[]」に、「A」と「B」(1列目の値)を退避しています。これはEND句で使用します。END句も合わせると ``` $ awk '{for(i=2;i<NF;i++){f[NR]=$1;print "echo",$i">>"$1}}END{for(i=1;i<NR+1;i++){print"(echo",f[i],";sort -n",f[i]")|xargs"}}' input echo 31>>A echo 1234>>A echo -42>>A echo 10>>B echo 31.1>>B echo -34>>B (echo A ;sort -n A)|xargs (echo B ;sort -n B)|xargs ```となり、「2列目以降の内容を、1列目の名前のファイルに出力するコマンド」と、「1列目の名前のファイルをソートしてxargsで横にするコマンド」が出来上がります。
(sort -n は数字としてソートするオプション)最後に、作成したコマンドをshコマンドに流しこんで実行します ``` $ awk '{for(i=2;i<NF;i++){f[NR]=$1;print "echo",$i">>"$1}}END{for(i=1;i<NR+1;i++){print"(echo",f[i],";sort -n",f[i]")|xargs"}}' input | sh A -42 31 1234 B -34 10 31.1 ```
### Q7. Q6のグラフを次のように縦にしてください。(多少ズレてもよしとします。) ``` * * * * * * * * * * * * * * * * * * * * * * * * 5 3 4 10 2 ```※Q6の入力ファイル ``` $ cat num 5 3 4 10 2 ```https://twitter.com/kunst1080/status/495469327311593472
※pasteコマンドを使いたかったので、この問題はLinux(ZorinOS)で解きました。
考え方は以下のようにしました
  • 1列ずつ出力を作成する(プロセス置換(?)として作成)
  • それらをpasteで横に並べる

まず前半、xargsの手前まで ``` $ cat num |xargs -I% echo "<(yes \\\"\*\\\" | head -%;echo %)" <(yes \"\*\" | head -5;echo 5) <(yes \"\*\" | head -3;echo 3) <(yes \"\*\" | head -4;echo 4) <(yes \"\*\" | head -10;echo 10) <(yes \"\*\" | head -2;echo 2) ```xargsの-Iオプションで標準入力を変数化できるので、それを利用して「棒グラフ1本を生成するプロセス置換」を生成します。
試しに1行目のコマンドだけを実行してみるとこんな感じです ``` $ yes "*" | head -5;echo 5 * * * * * 5 ```さて、次にこれらをxargsで並べます。 ``` $ cat num |xargs -I% echo "<(yes \\\"\*\\\" | head -%;echo %)" | xargs <(yes "*" | head -5;echo 5) <(yes "*" | head -3;echo 3) <(yes "*" | head -4;echo 4) <(yes "*" | head -10;echo 10) <(yes "*" | head -2;echo 2) ```並びましたね。
pasteコマンドに突っ込みたいので、echoで先頭にpasteを付けます。 ``` $ cat num |xargs -I% echo "<(yes \\\"\*\\\" | head -%;echo %)" | xargs echo paste paste <(yes "*" | head -5;echo 5) <(yes "*" | head -3;echo 3) <(yes "*" | head -4;echo 4) <(yes "*" | head -10;echo 10) <(yes "*" | head -2;echo 2) ```これで、「『棒グラフを1本ずつ生成するプロセス置換』を横に結合するコマンド」が出来上がりました。プロセス置換はbashの機能なので、shではなくbashに流し込みます ``` $ cat num |xargs -I% echo "<(yes \\\"\*\\\" | head -%;echo %)" | xargs echo paste | bash * * * * * * * * * * * * * * 2 * 3 * * * 4 * 5 * * * * * 10 ```若干変な出力ですが。。。

以上です。
(なんだか今回まともにできてない気がしなくもないですが……)

今回のシェル芸会で、だんだんとpasteコマンドの使用に慣れてきたような感じです。
便利そうなので、もっと手に馴染ませていきたいところ。


### 次回予告 次回は、大阪で有志を募ってサテライト会場やるかも???