タグ・アーカイブ: bash

どうもお久しぶりです。デスマ帰りの (っ´∀`)っ ゃー です。元気に生きてます。
デスマの最中でも #hbstudy や #techlion は行ってたのですが、ブログに書くだけの体力は残っていませんでした(;´Д`)
12月に入り、デスマも無事終了したので、ようやっとブログ書くことができましたw

さてさて、昨日はUSP友の会怪鳥ぷれぜんつ シェル芸人養成勉強会に行ってきました。
当日のまとめはこちら、当日のスライドはこちらになります。ぼくのブログでは、全10問のお題のうち、どう解答したかをいくつかピックアップして書くことにします。(全部書くとさすがに半日くらいかかるw)

なお、正解は1つではなく、正答例と同じ解答が得られれば、それだけ多くの解答が正解となります。ご参考までに、私の環境はCentOS 6.3で文字コードはja_JP.utf8です。

※ シェル芸とは


UNIXシェル(主にbash)のワンライナーを駆使して文字列加工を自由自在に操ることができる人をシェル芸人と呼びます。(by USP友の会会長 上田さん)

最初に感想を書きますと、tukubaiコマンドがあると確かに楽です。が、sedやawk、trを駆使して解答しても頭の体操になります。また、仕事で使うぶんには後者のほうが導入障壁は少ないはず。私のようにプログラミングスキルがない人間にとっては、新しいコマンドやツールは決して銀の弾丸ではなく、使いどころを理解できるまではおいそれと使えないかなぁ・・・と。一方、コマンド名がローマ字だったり(例えば「mojihame」とか)、何段もパイプを繋がずにコマンドライン一発で文字列整形や数値の集計ができたりするので、コマンドラインに不慣れな方にコマンドを叩いてもらうには、これもありかなとも思います。

参加者の皆さんのうち、今回は半数以上の方が猛者のようで、perlのワンライナーで解答されたり、一方でcommコマンドという知らないコマンドが出てきたりなど、新たな発見もあったりして、勉強になりました。

それではお題と私の解答を見ていきましょう。

#1 文字化けしたファイルの削除


文字化けしたファイルを生成するところから始めました。nkfコマンドが入っていなかったので、まずはyumでnkfをインストール。こちらはbaseリポジトリにあるので何も考えず「yum install nkf」でOKです。nkfがインストールされたら、以下のコマンドでワザと文字化けしたファイルを作成しました。

echo ほげ | nkf -s | xargs touch

nkfのオプション「-s」を与えると、入力した文字コードをShift-JISに変換します。これをxargsコマンド越しにtouchコマンドへ渡してあげて、文字化けした空ファイルを作ることができます。

さてさてファイルを消してみましょう。今回のお題は、カレントディレクトリに通常のASCII文字で名づけられたファイル「abc」「DEFG」もあります。これは消しちゃいけません。なので、文字化けしたファイル「のみ」を消すのが正解です。

ls [!a-zA-Z]* | xargs rm

これで、カレントディレクトリにある文字化けしたファイルのみが消えてくれました。sambaでwindowsファイル共有をしているサーバなどで役立ちそうですね。

#2 フィールド数がバラバラなファイルにある数字を足し算してみる


次のようなファイルがあったとして、これらの数字を全部足し算します。

$ cat num
1
2 3 4
5 6
7 8 9 10

恐らく参加者全員が「まずはこれらの数字を一列に並べる」から始めたでしょう。数字を一列に並べるため、私は改行をスペースに変換しました。

cat num | tr 'n' ' '

そして、exprコマンド(整数値の計算ができるコマンド)に食わせるため、スペースを「 + 」に変換してあげました。

cat num | tr 'n' ' ' | sed -e "s/ / + /g"

しかしこれでは、10の後にも加算記号がついてしまいます。なので、末尾のスペースを削除することにしました。

cat num | tr 'n' ' ' | sed -e "s/ $//g;s/ / + /g"

これで出力が「1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10」となったはずです。あとは以下のようにしてexprコマンドに食わせてあげます。

expr $(cat num | tr 'n' ' ' | sed -e "s/ $//g;s/ / + /g")

そうすると、答え「55」を求めることができました。awkを使った解答例やtukubaiコマンドを使った解答例は、上田さんのスライドを参考にしてみてください。

#3 以下のhogeファイルから、aとbについてそれぞれ一番大きな数字を求める

$ cat hoge
a 12
a 13
b 13
a 432
b 111
b 43

これは当日正解を出すことができませんでした。今もう一度復習がてら解いた答えがこれです。

for i in a b; do grep ^$i hoge | sort -k2 -n | tail -n 1; done

まず、ファイルhogeの先頭がaかbに該当するものをgrepし、sortコマンドを使って、2列目を数字順に並べ替え、それぞれ最後の1行を抜き出しています。ファイルの先頭文字列の指定はfor文のループで回しています。ここでファイルhogeの「b 43」を「b 4343」にしてみます。そして再度上記コマンドを実行すると、ちゃんとbの最大は4343であることが求められます。さらに、「b 111」を「b 111111」にしてみると、こちらも「b 111111」が最大であることがわかります。

#5 日付と曜日


1990年から2012年までの1月1日が「YYYYNNDD」形式で書かれたファイルがあったとして、何曜日が何日あるかを集計します。GNU dateでは「-f」引数が使えるので、以下のようにして直接dateコマンドに食わせて集計しました。

LANG=C date -f osyouga2 | awk '{print $1}' | sort | uniq -c

日本語だと曜日が4フィールド目になるのでこうですね。

date -f osyouga2 | awk '{print $4}' | sort | uniq -c

-fオプションが使えない場合どうしよう、と考えてみた結果、forループを使うことにしました。

for i in $(cat osyouga2); do date +%A --date=$i; done | sort | uniq -c

または

for i in $(cat osyouga2); do LANG=C date +%A --date=$i; done | sort | uniq -c

ただしこれだと曜日の並びがバラバラになってしまいます。なので、forループを入れ子にするしかなさそうです。。。

for A in 日曜日 月曜日 火曜日 水曜日 木曜日 金曜日 土曜日; do for i in $(cat osyouga2); do date +%A --date=$i; done | sort | uniq -c | grep $A; done

力技ですが、もっとエレガントな答えがあればいいなあw

#8 ファイルの比較


file2からfile1にない数字を抽出します。いずれも数字は縦に並んでいるものとします。

file1: 1 3 4 6 9
file2: 2 3 4 5 9

最初に思いついたのはdiffコマンドの結果を食わせる方法でした。

diff -y file1 file2 | egrep "|" | awk '{print $NF}'

しかし、「diff使っちゃだめ(笑)」って縛りが入ったのでこう解答しました。

cat file2 | grep -v -f file1

grepの「-v」オプションは、引数に含まれないものを標準出力に表示します。「-f」はパターンをファイルから1行ごとに読み込みます。ゆえに、これらオプションの組み合わせで同じ解答を求めることができました。

当日の会場にはさらに猛者がいて、commコマンドを使って解いていました。

comm -13 file1 file2

オプションが与えられないとcommコマンドは三列に出力を行うので、「じゃあ1列目と3列目を削っちゃえばよくね?」という至極単純な発想で、パイプを使わないぶん、巨大なファイル比較を行った場合、パイプでgrepやawkに渡すより速そうですね。

家での復習も兼ねて解いてみて気づいたのですが、正解が複数ある問題を解くというのは、本当に頭の体操になるなあ、と。そして、コマンドやオプションのボキャブラリーが多いと、イザというとき本当に役立ちます。シェル芸人としてまだまだ精進せねば。

さっきのエントリで、for文使ったりパイプに渡すような書き方しちゃったけど、

早速 @chonan さんから ツッコミ が・・・(;゚Д゚)

$ date --date="`date +%d` days ago" +%Y%m01
20110201
$ date --date="`date +%d` days ago" +%Y%m%d
20110228
$ date --date="`date +%d` days ago" +%Y/%m/01
2011/02/01
$ date --date="`date +%d` days ago" +%Y/%m/%d
2011/02/28
えらくシンプルにできました(;゚Д゚) タッタコレダケ
ゼロパディングの影響を極力排除したいなら
$ date --date="`date +%e` days ago" +%Y/%m/%d
2011/02/28
とか
$ date --date="`date +%-d` days ago" +%Y/%m/%d
2011/02/28
らしいけど、まだまだ精進しなきゃです。。。 (´・ω・`)
@chonanさん、有難うございます。

2017/12/31 03:30 追記

この投稿の方法で前月の今日、みたいな日付の求め方をしてしまうと、31日まである月や2月のように、正確でない日付が出力されてしまいます。GNU dateでもっと確実に前月を表示する方法は以下の投稿に書きました。


あるシェルスクリプトで先月分のログを抽出しているのですが、毎回毎回 YYYYMMDDと引数を入れるのがかったるい。できればこういう処理は自動化できれば作業品質も上がるだろうということで、1行でやってみました。

…続きを読む

みなさま

こんばんは。(っ´∀`)っ ゃーです。2010/08/28に行われました、第一回いたこ会、無事終了いたしました。

講師のゆーすけべー様、高野光弘様、そしてご参加くださった皆様、本当に有難うございます。そして、不慣れな運営でご心配をおかけいたしまして申し訳御座いません。いたこ会の準備にあたりアドバイスいただきました法林さん、りゅうちさん、あらためてお礼申し上げます。

ゆーすけべー様の「PerlでCLI(くり)アプリ!」は、いかに効率よくOPPAI画像を収集するか、そしてYahooAPIを使う上での注意点などを非常に実用的な例を使って解説していただきました。

高野光弘様の「シェルスクリプトで頭の体操」ではbrainf*ckをshに食わせて1文字ずつHello Worldを表示させる過程を解説していただきました。

その後、鹿野さん、りゅうちさん、加藤さんと(っ´∀`)っ ゃーによるLT大会が行われ、本編が終了です。

懇親会は新橋の「かがや」で不思議な雰囲気の中行われ、マスターにはぁゃιぃ宗教団体と思われていたようですがこちらも童心に帰って非常に盛り上がりました。

9月10日、11日はOSCです。こちらも張り切っていきますよー (`・ω・´)