今回も勉強会にはいけませんでした。金欠であるが故、頼みの綱であるバイトのシフトに入らざるを得なかったのです...
まだ未完ですが、とりあえずということで解答を出します。問題はここで。
Q1
$ find . -name "*.md" | xargs -n1 awk '/Keywords/&&!x{print gensub("main.md", "", "g", gensub("./", "", "g", FILENAME)), $0;x++}'
大好き awk
。FILENAME
という変数は初めて知りました。これは今後のシェル芸に地味に役立ちそうな予感。
追記:
こっちの方がより建設的ですね(?)。もとより、自然かつサッパリとできています。
$ find . -name "*.md" | xargs grep Keywords | awk '!a[$1]++' | sed -r 's/:/ /1;s_(\./posts/|/main\.md)__g'
Q2
$ cat url.html | perl -pe 's/((href|src)\=)"\.*(?!https*:\/\/)+/\1"\/files\//g' | sed 's/files\/\//files\//g'
初めて perl
を置換コマンドとして使いました。理由は否定先読みが出来るからです。便利ですねぇ。s/sed/perl/g
したいかも(((
ここで自分が試行錯誤した内容を軽く述べます。
最初に $ cat url.html | sed -r 's/((href|src)\=)"[^(http)/]//g' | sed 's/files\/\//files\//g'
としていたのですが、テキスト中の href="huge.html"
にマッチしないんですね。おかしいなぁとおもったのですが、このワンライナー中にある [^(http)/]
は、「(もhもtもpも)も/も含まない文字1つ」になっていなのですね。
この場合の huge
の h
がバッチリこの条件にあってしまっているので置換が行われないのでした。はてと困って調べた所、
(?!hage)
と言うものがあるらしい?!(ボケたつもりです) これはおそらく「hage という文字列を含まない位置で」ということだと思います。自分はそう理解しているのです。ですので例えば ABC から始まらない任意の3文字は
(?!ABC)...
でパターンが作れますね。厳密には色々とあるようですが僕にはわかりません。ということで 特定の文字列を含まないという正規表現 (Weblog on mebius.tokaichiba.jp) がいい感じに述べてくれているので参照してください。
今回の場合はダブルクウォートの始まりが http:// か https:// にマッチしなければいいので
s/((href|src)\=)"\.*(?!https*:\/\/)+/\1"\/files\//g
としたわけです。この否定先読みは sed
では扱えないらしいので、とうとう perl
に手を出してしまいました。午前中もなんか perl 芸やっていたらしいので深みに嵌りそう...
Q3
$ cat list | awk 'BEGIN{print"Content-Type: text/html\n\n<!DOCTYPE html>\n<html>\n<head>\n <meta charset="utf-8">\n</head>\n<body>\n<ul>"} {print "<li>"$2"</li>"} END{print"</ul>\n</body>\n</html>"}'
スミマセン!!サボりました!!!!!!
Q5
$ cat complex | sed 's/\*i/j/g' | awk '{a[NR]=$0} END{printf("print(%s*%s)\n", "("a[1]")", "("a[2]")")}' | python
色々とゴリ押ししてしまいました。そして複素数と言ったら python
ですよねぇ。最近は Haskell 芸やってみたいです。
なお、tukubai の mojihame
を使うともうちょっと短くなります。python使っていますが。
$ mojihame <(echo 'print((%1)*(%2))') <(sed 's/ //g;s/\*i/j/g' complex | xargs) | python
Q6
$ echo 0 1 | awk '{while(1){$0 = $0 " " $NF + $(NF-1);print $(NF-1) " "}}' | sed '/6765/q' | tail -5 | head -1
すみませんまた awk
です。
この awk
をより短くしてみました。しかもこっちの方がわかりやすい。
$ echo | awk '{m=1;while(1){l=m;m=r;print r=l+m}}' | sed '/6765/q' | tail -5 | head -1
Q7
$ comm -3 <(seq 0 99 | xargs -n1 printf "%02d\n") <(seq 0 99 | xargs -n1 printf "%02d\n" | xargs -I {} grep -o {} nums | uniq)
なんか2度手間な気がしますが、「差分を取る」という割りと基本的なところに落ち着きました。
追記:
より短くできました↓
$ echo comm -3 "<(seq -w 99 "{," | xargs -I @ grep -o @ nums | uniq"}\) | bash
技巧的ですが、ブレース展開を使っています。 "<(seq -w 99 "{," | xargs -I {} grep -o {} nums | uniq"}\) といった感じです。後半の bash
は文字列をコマンドとして実行するコマンドですので、これで実際に動かしています。
初めの解答ではなんか seq
した後に printf
していましたが、これは1桁の数字の十の位を0でパディングするためです。実際の所 seq
自身にこの機能がありまして、-w
オプションを使えば速攻で解決というわけです。このオプションですが、manページには書いてありません。seq --help
でご覧になれます。
更に追記:
そうでした。echo
からの bash
をやるときはたいてい eval
が効くんでした。と言うわけで以下のようにまた短くなりましたよ↓
$ eval comm -3 "<(seq -w 99 "{," | xargs -I @ grep -o @ nums | uniq"}\)
Q8
$ cat alphabet | sed 's/\(.\)-\(.\)/\1-\2 \2 \1/g' | perl -ale 'print abs((ord $F[1]) - (ord $F[2]))' | nl -nln | sort -k2nr | head -1 | cut -f1
awk
に逃げがちな僕ですが、ここでやっと生きた心地のするシェル芸が出来ました。ここでも思うことですが、perl
マジ便利。perl
と awk
の両刀使いになりたいなぁ。
追記:
ゴチャゴチャやらずに素直な気持ちになったら結構短くなりました↓
$ cat alphabet | sed -r 's/(.)-(.)/echo {\1..\2}/e' | awk '{if(m<NF){m=NF;l=NR}} END{print l}'
また、出力を行番号とせずに、その行の中身としたい場合は以下のようになります。
$ cat alphabet | sed -r 's/(.)-(.)/echo \1-\2 {\1..\2}/e' | awk '{if(m<NF){m=NF;c=$1}} END{print c}'