シェル芸スパルタン演習(2015年末年始回シェル芸勉強会編)
ここ最近、Unixシェルに触る機会が減っており、CLIを使う能力の低下を感じていた。
また、近日中に、第25回シェル芸勉強会の開催が予定されており、参加するにあたり予習をしたいとも考えていた。
そこで、過去に開催されたシェル芸勉強会の問題を活用し、シェル芸力をつける演習をおこなうことにした。
今回は、問題として、2015年末年始シェル芸勉強会の設問を用いた。
実行環境
Arch Linux 4.8.4-1-ARCH
GNU bash 4.3.46
GNU coreutils 8.25-2
GNU diffutils 3.5-1
GNU findutils 4.6.0-2
util-linux 2.28.2-1
grep (GNU grep) 2.26
GNU bc 1.06.95
sed (GNU sed) 4.2.2
gawk 4.1.4
curl 7.50.3
GNU Wget 1.18
t 2.10.0 ( https://github.com/sferik/t )
usp Personal Tukubai 20160402 ( Open usp Tukubaiで代用可能)
シェル芸演習
問題文は、以下のURLから。
http://blog.ueda.asia/?p=4852
模範解答は、こちらから。
http://blog.ueda.asia/?p=4821
問1
重複しているファイルを削除する問題。
画像だけとは言わず、全ファイルを対象にするぞ…!!
まず、findでファイルリストを出力する。
次に、各ファイルに対し、MD5ハッシュ値を出力する。
その後、ハッシュ値でsort, uniqをして、内容が重複しているファイルリストを得る。
最後に、得られたファイルリストを、xargsを経由でrmに渡してやれば、完了。
01 #!/bin/sh
02
03 find . -type f |
04 xargs -n 1 md5sum |
05 awk '{print $2,$1}' |
06 sort -k 2,2 |
07 uniq -f 1 -d |
08 awk '$0=$1' |
09 xargs -n 1 rm
以下、コピペ実行用のワンライナーコード。
本当に実行して、大事な エロ画像 ファイルを消さないように注意。
find . -type f | xargs -n 1 md5sum | awk '{print $2,$1}' | sort -k 2,2 | uniq -f 1 -d | awk '$0=$1' #| xargs -n 1 rm
問2
羽田空港の緯度経度を求める問題。
シェル上だけではどうしようも無いので、とりあえずWebからデータを取得する。
今回は、Google Mapを利用した。
Google Mapから羽田空港の位置を示したHTMLソースを取得し、その中から緯度経度を削り出していけばOK。
01 #!/bin/sh
02
03 curl -sL 'https://www.google.co.jp/maps/place/羽田空港+(HND)/' |
04 grep -o 'cacheResponse([^)]*)' |
05 awk -F, '{print $2,$3}' |
06 tr -d ']'
以下、コピペ実行用のワンライナーコード。
curl -sL 'https://www.google.co.jp/maps/place/羽田空港+(HND)/' | grep -o 'cacheResponse([^)]*)' | awk -F, '{print $2,$3}' | tr -d ']'
問3
任意の級数からネイピア数を求める問題。
ネイピア数を求める級数は沢山あるので、一番計算しやすそう(?)な、次の級数を利用する。
1から順に自然数を延々と出力し、その値を上記の式に当てはめて、計算していく。
計算していく内に、計算結果が0に収束するので、sedを利用して出力を打ち切る。
その後、得られた計算結果を全て足し合わせれば、(できるだけ)正確なネイピア数が得られる。
01 #!/bin/sh
02
03 seq 1 inf |
04 awk 'BEGIN{print "scale=1000"} {printf $0"/(2*("; for(i=1;i<=$0 - 1;i++){printf i" "}; print $0-1 == 0 ? $0"))" : "))"}' |
05 sed 's/ )/)/' |
06 tr ' ' '*' |
07 BC_LINE_LENGTH=0 bc -l |
08 sed '/^0$/q' |
09 tr '\n' '+' |
10 sed 's/\+$/\n/' |
11 BC_LINE_LENGTH=0 bc -l
以下、コピペ実行用のワンライナーコード。
seq 1 inf | awk 'BEGIN{print "scale=1000"} {printf $0"/(2*("; for(i=1;i<=$0 - 1;i++){printf i" "}; print $0-1 == 0 ? $0"))" : "))"}' | sed 's/ )/)/' | tr ' ' '*' | BC_LINE_LENGTH=0 bc -l | sed '/^0$/q' | tr '\n' '+' | sed 's/\+$/\n/' | BC_LINE_LENGTH=0 bc -l
問4
多重にbase64エンコードが掛けられたファイルに対して、ワンライナーで復号する問題。
base64は、デコードに失敗すると終了ステータス1を返す事がポイント。
base64を延々と実行し、失敗したらexit → ファイルをcatすれば良い。
ただし、1つのファイルだけで処理をおこなうのは困難なので、seqで生成した自然数を、作業ファイル名に使うことにする。
解読が終わり、base64のデコードが失敗したら、exitで直前の行番号(=最後のファイル名)を終了ステータスとして返してやるのがコツ。
最後に、特殊シェル変数'$?'を利用して、解読完了したファイルをcatしてやればOK。
01 #!/bin/sh
02
03 wget -q https://blog.ueda.asia/misc/message2015.txt
04 seq 1 inf |
05 awk '{printf "cat message2015.txt"; for(i=1;i<=$0;i++){printf " |base64 -d"}; print " 2>/dev/null > "$0".txt || exit "$0-1""}' |
06 sh
07 cat $?.txt
以下、コピペ実行用のワンライナーコード。
wget -q https://blog.ueda.asia/misc/message2015.txt; seq 1 inf | awk '{printf "cat message2015.txt"; for(i=1;i<=$0;i++){printf " |base64 -d"}; print " 2>/dev/null > "$0".txt || exit "$0-1""}' | sh; cat $?.txt
問5
円周率を精度良く求める問題。
seqで自然数を生成し、bcコマンドの変数scaleにその値を設定することで、徐々に精度の高い円周率を出力させる。
01 #!/bin/sh
02
03 seq 1 inf |
04 sed 's/.*/scale=&; 4*a(1)/' |
05 BC_LINE_LENGTH=0 bc -l
以下、コピペ実行用のワンライナーコード。
seq 1 inf | sed 's/.*/scale=&; 4*a(1)/' | BC_LINE_LENGTH=0 bc -l
問6
部分集合を列挙する問題。
…実はこれ、分からなくて解答を見てしまったorz
bashのブレース展開を利用するのが、ポイントなのね。
01 #!/bin/bash
02
03 echo {,a}{,b}{,c}{,d}{,e}
以下、コピペ実行用のワンライナーコード。
echo {,a}{,b}{,c}{,d}{,e}
問7
与えられた数値が完全数であることを示す問題。
手前味噌だけれど、以前Project Eulerでそっくりな問題を解いたので、そちらを参考に。
01 #!/bin/sh
02
03 echo 8128 |
04 awk '{print $0; for(i=1;i*i<=$0;i++){if($0%i==0){print i; if(i!=$0/i && $0!=$0/i){print $0/i}}}}' |
05 awk 'BEGIN{sum=0} NR==1{num=$0} NR!=1{print sum,$0; sum+=$0} END{print "--"; print sum,num}'
以下、コピペ実行用のワンライナーコード。
echo 8128 | awk '{print $0; for(i=1;i*i<=$0;i++){if($0%i==0){print i; if(i!=$0/i && $0!=$0/i){print $0/i}}}}' | awk 'BEGIN{sum=0} NR==1{num=$0} NR!=1{print sum,$0; sum+=$0} END{print "--"; print sum,num}'
14474011154664524427946373126085988481573677491474835889066354349131199152128の証明は、上記と同じコードで出来るけれど…
僕が所持している一番ハイスペックなマシンで、3日間実行してみたけれど、それでも処理が終わらなかった/(^o^)\
問8
Twitterから任意のキーワードを含んだツイートを収集する問題。
…面倒臭いので、 Unixコマンドと親和性の良いTwitterコマンド、tで済ませるw
01 #!/bin/sh
02
03 t search all --long -n 3200 'シェル芸'
以下、コピペ実行用のワンライナーコード。
t search all --long -n 3200 'シェル芸'
雑記
スパルタン演習のラストを飾るこの回は、
ド変態上級者向けの問題ばかりだった。変態的な問題ばかりだけれど、数値計算系の問題は好きなので、個人的にはかなり楽しめた。
ということで、第25回勉強会開催までに間に合わなかったけれど、これにてシェル芸スパルタン演習完了!
全21回のシェル芸勉強会の問題を解いたけれど、これで少しはシェル芸力が上がっているといいなぁ…