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


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

Q1. 次のように、画面にバッテンを描いてください。(この出力例の大きさは21×21です。)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
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でがんばる感じですかね。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
$ (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」の左側に見えてきましたね・・・

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
$ (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するためのオプションです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
$ (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→スペース の変換をするだけです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
$ (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コマンドを使って文字の置換をしました。

1
tr '01' ' X'

で、'0'を' 'に、'1'が'X'に、まとめて置換できます。
こんな風に、sedを使わなくてもtrコマンドでも置換ができます。

Q4. 北から順(正確には都道府県番号順)に並べてください。

1
2
3
4
5
$ cat pref
鹿児島県
青森県
大阪府
群馬県

https://twitter.com/kunst1080/status/495454679942168576
https://twitter.com/kunst1080/status/495455438087794688
WEBの情報を活用して作業をする、という観点の問題でした。
頭を固くせず、使えるものは使いましょうっていうことですね。curlで取ってきた段階だと文字コードがSJISなので、nkf -w でUTF-8に変換します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
$ 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

1
2
3
4
5
6
7
8

を使うとスマートにできます。(@TSB_KZKさんに教えていただきました。)<br/>
manによると

<ul>
<li>-o, --only-matching…マッチした行の、マッチした部分だけを出力</li>
<li>-f FILE, --file=FILE…検索パターンをファイルから読み込む</li>
</ul>ということです。繋げると、

$ curl elze.tanosii.net/d/kenmei.htm -s | nkf -w | grep -of pref 青森県 群馬県 大阪府 鹿児島県

1
2
3
4
5
6
7

<br/>
 



### Q5. 各行の数字を大きい順にソートしてください。

$ cat input A 31 1234 -42 4 B 10 31.1 -34 94

 1
 2
 3
 4
 5
 6
 7
 8
 9
10

<a href="https://twitter.com/kunst1080/status/495463901664784384">https://twitter.com/kunst1080/status/495463901664784384</a><br/>
なんぞこれ……<br/>
考え方は以下のようにしました

<ul>
<li>1列目の名前で1行のデータを持つ中間ファイルを作成する</li>
<li>それぞれの中間ファイルをソートしてからくっつける</li>
</ul><br/>
まずはこの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

1
2

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

1
2
3

「2列目以降の内容を、1列目の名前のファイルに出力するコマンド」が並びます。<br/>
またこのとき、連想配列「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

1
2
3

となり、「2列目以降の内容を、1列目の名前のファイルに出力するコマンド」と、「1列目の名前のファイルをソートしてxargsで横にするコマンド」が出来上がります。<br/>
(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

1
2
3
4
5
6
7

<br/>
 



### Q7. Q6のグラフを次のように縦にしてください。(多少ズレてもよしとします。)
   * 
   * 
   * 
   * 
   * 
  •  * 
    




5 3 4 10 2

1
2

※Q6の入力ファイル

$ cat num 5 3 4 10 2

 1
 2
 3
 4
 5
 6
 7
 8
 9
10

<a href="https://twitter.com/kunst1080/status/495469327311593472">https://twitter.com/kunst1080/status/495469327311593472</a><br/>
※pasteコマンドを使いたかったので、この問題はLinux(ZorinOS)で解きました。<br/>
考え方は以下のようにしました

<ul>
<li>1列ずつ出力を作成する(プロセス置換(?)として作成)</li>
<li>それらをpasteで横に並べる</li>
</ul><br/>
まず前半、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)

1
試しに1行目のコマンドだけを実行してみるとこんな感じです

$ yes “*” | head -5;echo 5 * * * * * 5

1
2

さて、次にこれらを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)

1
2
3

並びましたね。<br/>
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
2

これで、「『棒グラフを1本ずつ生成するプロセス置換』を横に結合するコマンド」が出来上がりました。プロセス置換はbashの機能なので、shではなくbashに流し込みます

$ cat num |xargs -I% echo “<(yes \"*\" | head -%;echo %)” | xargs echo paste | bash



        • 2
  • 3 * *
  •   4	*	
    

5 * * * * * 10

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19

若干変な出力ですが。。。<br/>
<br/>
以上です。<br/>
(なんだか今回まともにできてない気がしなくもないですが……)<br/>
<br/>
今回のシェル芸会で、だんだんとpasteコマンドの使用に慣れてきたような感じです。<br/>
便利そうなので、もっと手に馴染ませていきたいところ。<br/>
<br/>
<br/>




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