ゴールデンウイークシェル芸問題のまとめ

Mon May 4 18:16:41 JST 2015 (modified: Sun Oct 1 10:50:27 JST 2017)
views: 2117, keywords:コマンド,CLI,ファイル入出力,素数ネタ,シェル芸 この記事は最終更新日が7年以上前のものです。

問題はこんなものでした

以下のように1から100まで数字が書いてあるansというファイルを作り、ansの中から素数でない数をワンライナーだけで消し去ってください。(ansの中身を書き換えるということです。forもwhileもなしで、コマンドはパイプでつないで。)

ueda@ubuntu:~/tmp$ seq 1 100 > ans

問題の意図

意図は隠しつつ伝える必要があるので毎回苦労しますが、この問題は素数がポイントなのではなく、「入力ファイルを出力で上書きできますか?」ということがポイントです。

こういうことを言うと「シェルによって違う」という話になりがちですが、まずは理詰めで考えることの方が大事なんじゃないかなと、個人的には思います。コード読めという話も出てきますが、これも同様、理詰めで考えればわざわざ読む必要もありません。

攻略法

理屈で考えると、パイプラインの中身がすべて同時に動いている状況で、入力ファイルを出力ファイルに書き戻すには、

  • 入力と出力を時間的に完全に分離
  • iノード(= ファイルの実体)を分離
  • パイプに通さずにコマンドに上書きさせる

のいずれかの方法がとられている必要があります。もちろん、こうでなくてもOSがよしなに計ってくれればよいのですが、そんな冗長な処理をOSに搭載するのは問題です。

シェルの話をすると、問題のansファイルは、シェルの「あるプロセス」がansファイルに書き込みを始めるときにファイルの中身が空になります。空にしないとファイルの頭からデータを保存できないので、これもコードを読まずに理屈で考えると分かるかと。

で、私が想定していた解は、こちらです。

平たく書くとこんな感じ。

###spongeコマンドをインストールする###
   ueda@ubuntu:~/tmp$ sudo apt-get install moreutils
   ###使う###
   ueda@ubuntu:~/tmp$ cat ans | factor | awk 'NF==2{print $2}' | sponge ans

sponge(1)というコマンドを使います。名前の通り、入力された字を吸い取っていき、吸い取ってから引数に指定したファイルに書き込みます。ということで、上に挙げた「入力と出力を時間的に完全に分離する」が守られています。

「ファイルの実体を分離する」の解もありました。すごい。これは思いつきません。

ebanさんはどんな方法でも解いてくるので本当にすごいなあと・・・。

解説をしておくと、まずansをオープンした後にansを消してから処理してansに書き出すという処理になっています。「rm ans」の時点では、ansが開いているので実体(iノード)はまだ消去されません。ansという名前だけ消えます。んで、その後に「factor ... > ans」となっているので、ansという名前の別のiノードができます。確認しておきましょう。

ueda@web:~/tmp$ seq 1 10 > ans
   ueda@web:~/tmp$ ls -i ans
   2363953 ans
   ueda@web:~/tmp$ (rm ans; factor | awk 'NF==2{print $2}' > ans) < ans
   ueda@web:~/tmp$ ls -i ans
   2364013 ans

実はこの挙動、フルスクラッチから1日でCMSを作る シェルスクリプト高速開発手法入門で少し説明しています。ただ、「具体的な使い道が分からん」という言葉と共に書いたので、今、使い道が分かりました。しかし、こんなコードは普通書かないので「使い道」なのかどうかは定かではありません。

最後のコマンドに上書きさせる方法は、(私がspongeを教えてもらったのは斉藤さんからなので)解答を禁止したはずの斉藤さんから強烈なものをいただきました。

説明は斉藤さんにお任せします。っていうかなんだこれ???

ブログで取り上げていただきましたのでリンク

おれの無いぞという方はぜひご連絡くだしあ。

ブログでないけど有難うございます。

せんでん

最後に宣伝・・・。今回の問題は扱ってませんが、基本、ワンライナーの、シェル芸人による、ワンライナーの本です。

ノート   このエントリーをはてなブックマークに追加 
 

prev:日記(花輪直付けスタイル) next:_

やり散らかし一覧

記事いろいろ