【問題と解答】第18回ニンニク入れますかシェル芸勉強会

Pocket
LINEで送る

問題だけのページはこちら: https://blog.ueda.asia/?p=6877
過去問はこちら: https://blog.ueda.asia/?page_id=684

オープニングスライド(悪い冗談)

問題で使うファイル等

今回からGitHubに置くようにしました。ファイルは

https://github.com/ryuichiueda/ShellGeiData/tree/master/vol.18

にあります。

クローンは以下のようにお願いします。

$ git clone https://github.com/ryuichiueda/ShellGeiData.git

環境

今回はLinuxで解答例を作りましたので、BSD系、Macな方は以下の表をご参考に・・・。

Mac,BSD系 Linux
gdate date
gsed sed
tail -r tac
gtr tr
gfold fold

Q1

次のファイルは1列目がキー、2列目が値ですが、「オトン」と「オカン」の両方の値があるキーを探してください。

$ cat text 
001 オトン
001 オトン
001 アカン
002 オカン
003 オトン
003 ヤカン
003 オカン
004 オカン
005 オトン
005 ミカン
005 アカン

解答

値がオトンとオカンのレコードを抽出してuniqで1列目が重複しているレコードを探します(解答例の出力の2列目は無視で)。

$ grep -e オトン -e オカン text | sort -u | uniq -w 3 -d
003 オカン

Q2

次の2つのファイルについて、aだけにあるレコード、bだけにあるレコード、両方にあるレコードを分類して、

$ cat a 
谷保
鹿島田
分倍河原
川崎
$ cat b
分倍河原
谷保
登戸
南多摩

次のような出力を作ってください。

a 鹿島田
a 川崎
b 登戸
b 南多摩
c 谷保
c 分倍河原

解答

commを使ってみたかっただけです。

$ comm <(sort a) <(sort b) | sed 's/^/\t/' |
sed 's/\t\t\t/c /' | sed 's/\t\t/b /' | sed 's/\t/a /' | sort
a 鹿島田
a 川崎
b 登戸
b 南多摩
c 谷保
c 分倍河原
###別解###
$ grep '' a b | awk -F: '{print $2,$1}' |
awk '{a[$1]=a[$1]$2}END{for(k in a){print a[k],k}}' |
sed 's/ab/c/' | sort
a 鹿島田
a 川崎
b 登戸
b 南多摩
c 谷保
c 分倍河原

Q3

次の3つのファイルについて、それぞれ書いてある数字の合計値を求めましょう。

$ cat a
1 2
3 4 5
$ cat b
1 2 3

$ cat c
7
8
9

解答

どうやってファイル名と値の2列のデータにするかが鍵。

$ grep -o "[0-9]*" * |
awk -F: '{x[$1]+=$2}END{for(k in x){print k,x[k]}}'
a 15
b 6
c 24
###Tukubaiを使うと楽。###
$ grep -o "[0-9]*" * | tr : ' ' | sm2 1 1 2 2
a 15
b 6
c 24

Q4

次のデータについて、

$ cat cross
_abcdef
a_x____
b______
c______
d______
e______
f___x__

次のような出力を作ってください。

a-b
f-d

つまり、xのついている場所の縦軸と横軸の記号を出力するワンライナーを考えてください。

解答

ベタにAWKを使うか、Tukubaiを使うか。

$ sed 's/./& /g' cross |
awk 'NR==1{split($0,a," ")}
/x/{for(i=1;i<=7;i++){if($i=="x"){print $1 "-" a[i]}}}'
###Tukubai使用###
$ sed 's/./& /g' cross | unmap num=1 |
awk '/x/{print $1 "-" $2}'

Q5

次のテキストから空白行の重複だけ除去してください。つまり、2行以上の空白行を1行にまとめてください。

あ
あ




い
い

う

え



お お
お
お

解答

文字のある行にだけ番号をつけてuniqすればよいですね。

$ grep -n '' text | sed 's/.*:$//' | uniq | sed 's/.*://'
あ
あ

い
い

う

え

お お
お
お
###別解###
$ awk '$1{print NR,$0}!$1' text | uniq | sed 's/^[0-9]* //'
###ebanさんを始めオプションを知っている人の答え(恐れ入りました)###
$ cat -s text

Q6

チェスボードの画像ファイルを作ってください。ウェブサイトから画像をパクるのは最近いろいろ問題となっているのでやめましょう。以下は例です。解像度は任意で構いません。

chess

解答

PGM形式で画像を作るのが一番簡単です。

$ yes '0 1 0 1 0 1 0 1' |
head -n 8 | sed '1~2s/0 1/1 0/g' | cat <(echo "P2 8 8 1") - > a.pgm
###AWKを使う場合###
$ seq 1 64 | awk '{print ($1 + int1)NR-1)/8%2}' |
xargs -n 8 | awk 'BEGIN{print "P2",8,8,1}{print}' > a.pgm

pgmが見れない。あるいは8×8ピクセルだとヤダという場合はImageMagickで変換を。

$ convert -scale 400 a.pgm a.png

Q7

次のファイルには1組だけ同じ文字が含まれていますが、何行目と何行目にあるでしょうか?

$ cat chinese_characters 
㔀㔁㔂㔃㔄㔅㔆㔇㔈㔉㔊㔋㔌㔍㔎㔏
㔐㔑㔒㔓㔔㔕㔖㔗㔘㔙㔚㔛㔜㔝㔞㔟
㔠㔡㔢㔣㔤㔥㔦㔧㔨㔩㔪㔫㔬㔭㔮㔯
㔰㔱㔲㔳㔴㔵㔶㔷㔸㔹㔺㔻㔼㔽㔾㔿
㕀㕁㕂㕃㕄㕅㕆㕇㕈㕉㕊㕋㕌㕍㕎㕏
㕐㕑㕒㕓㕔㕕㕖㕗㕘㕙㕚㕛㕜㕝㕞㕟
㕠㕡㕢㕣㕤㕥㕦㕧㕨㕩㕪㕫㕬㕭㕮㕯
㕰㕱㕲㕳㕴㕵㕶㕷㕸㕹㕺㕻㕼㕽㕾㕿
㖀㖁㖂㖃㖄㖅㖆㖇㖈㖉㖊㖋㖌㖍㖎㖏
㖐㖑㖒㖓㖔㖕㖖㖗㖘㖙㖚㖛㖜㖝㖞㖟
㖠㖡㖢㖣㖤㖥㖦㖧㖨㖩㖪㖫㖬㖭㖮㖯
㖰㖱㖲㖳㖴㖵㖶㖷㖸㖹㖺㖻㖼㖽㖾㖿
㗀㗁㗂㗃㗄㗅㗆㗇㗈㗉㕐㗊㗋㗌㗍㗎
㗐㗑㗒㗓㗔㗕㗖㗗㗘㗙㗚㗛㗜㗝㗞㗟
㗠㗡㗢㗣㗤㗥㗦㗧㗨㗩㗪㗫㗬㗭㗮㗯
㗰㗱㗲㗳㗴㗵㗶㗷㗸㗹㗺㗻㗼㗽㗾㗿

解答

同じファイルをワンライナーで二回読み込みます。

$ grep -o . chinese_characters | LANG=C sort | 
LANG=C uniq -d | grep -f - -n chinese_characters 
6:㕐㕑㕒㕓㕔㕕㕖㕗㕘㕙㕚㕛㕜㕝㕞㕟
13:㗀㗁㗂㗃㗄㗅㗆㗇㗈㗉㕐㗊㗋㗌㗍㗎

LANG=Cをちゃんと付けないとダメなようです。

###間違い###
$ grep -o . chinese_characters | sort |
uniq -d | grep -f - -n chinese_characters 
1:㔀㔁㔂㔃㔄㔅㔆㔇㔈㔉㔊㔋㔌㔍㔎㔏

Q8

次のファイルの中に、複数回登場する数字の並びがいくつかありますが、その中で最長のものはどれでしょうか?例えば「23」という数字の並びは4つありますが、それより長い数字の列で、2回以上登場するものが存在します。

$ cat number 
8264611130023148519839960536022802096895154738213681101003238003191122723922378922942503388843815799

解答

どうやって数字の並びを全通り出力するかがミソです。以下の出力のように003と922が正解です。

$ cat number |
awk '{for(j=1;j<length($1);j++)for(i=1;i<=length($1)-j+1;i++){print substr($1,i,j)}}' |
sort | uniq -d | awk '{print length($1),$1}' | sort -k1,1n
...
2 99
3 003
3 922
Pocket
LINEで送る

脚注   [ + ]

1. NR-1)/8