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

Sat Aug 29 16:51:16 JST 2015 (modified: Sat Dec 14 18:35:47 JST 2019)
views: 2789, keywords:CLI,UNIX/Linuxサーバ,勉強会,シェル芸,シェル芸勉強会 この記事は最終更新日が4年以上前のものです。

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

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

第18回シェル芸勉強会スライド from Ryuichi Ueda

問題で使うファイル等

今回から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 + int((NR-1)/8))%2}' |
   xargs -n 8 | awk 'BEGIN{print "P2",8,8,1}{print}' > a.pgm

pgmが見れない。あるいは8x8ピクセルだとヤダという場合は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
ノート   このエントリーをはてなブックマークに追加 
 

prev:【問題】第18回ニンニク入れますかシェル芸勉強会 next:日記: 昨日のシェル芸勉強会のまとめ等

やり散らかし一覧

記事いろいろ