Home > Latest topics

Latest topics 近況報告

たまに18歳未満の人や心臓の弱い人にはお勧めできない情報が含まれることもあるかもしれない、甘くなくて酸っぱくてしょっぱいチラシの裏。RSSによる簡単な更新情報を利用したりすると、ハッピーになるかも知れませんしそうでないかも知れません。

萌えるふぉくす子さんだば子本制作プロジェクトの動向はもえじら組ブログで。

宣伝1。日経LinuxにてLinuxの基礎?を紹介する漫画「シス管系女子」を連載させていただいています。 以下の特設サイトにて、単行本まんがでわかるLinux シス管系女子の試し読みが可能! シス管系女子って何!? - 「シス管系女子」特設サイト

宣伝2。Firefox Hacks Rebooted発売中。本書の1/3を使って、再起動不要なアドオンの作り方のテクニックや非同期処理の効率のいい書き方などを解説しています。既刊のFirefox 3 Hacks拡張機能開発チュートリアルと併せてどうぞ。

Firefox Hacks Rebooted ―Mozillaテクノロジ徹底活用テクニック
浅井 智也 池田 譲治 小山田 昌史 五味渕 大賀 下田 洋志 寺田 真 松澤 太郎
オライリージャパン

Page 1/238: 1 2 3 4 5 6 7 8 9 »

書評:わかばちゃんと学ぶ Git使い方入門 - Apr 30, 2017

わかばちゃんと学ぶ Git使い方入門〈GitHub、Bitbucket、SourceTree〉
湊川 あい
シーアンドアール研究所 (2017-04-21)
売り上げランキング: 787

「わかばちゃんと学ぶWeb制作の基本」のレビューを参照しながら書こうと思ったらそもそも書いてなかったので、先にそっちから書いていました。例によって自分は恐らく対象読者ではないのですが、人気の秘密を解き明かしてやんよ!とばかりに自費購入です。

本書は端的に言うと、「チームでGitを使う事になったが、よく分からないという人」「Gitをまだ使ったことがなく、なんだか難しそう……と尻込みしている人」「とりあえずなんとなくで使えてはいるが、決まった操作以外はできずにいる人」「チーム内のITエンジニアがGitを激推ししてくるのだが、使い方を覚えるのが億劫な非ITエンジニアの人」のための本だと思います。「Gitというツールの使い方の入門」+「Gitを使ったワークフローの世界への入門」という、人がGitに初めて触れる場面に即した内容になっています。

わかばちゃんシリーズの2冊目ではありますが、解説の内容は独立しています。一部キャラクターは前作からの登場のため、説明がないキャラは「誰これ?」となってしまうかもしれませんが、解説の理解を妨げるほどではなく、前作から読んでいる人のためのファンサービスと言えるでしょう。

マンガで技術解説、ふたたび

前作同様、本書も「マンガは導入や簡単な説明で、その後のテキストが本編」という構成です。漫画要素は前作より多く、トピックによってはマンガの内容が解説として機能している部分も増えていますが、テキスト部分をすべて読み飛ばして成立する内容ではないので、基本的にはやはり漫画もテキストも全部読みましょうという事になります。

GitやGitを使ったワークフローはそれ自体がそれなりに複雑な物なので、何か基準を定めないと、解説する側も解説される側も混乱してしまうでしょう。本書は主人公の「わかばちゃん」がその基準となり、「これをしたい!」や「こういう事で困った!」という形で話題を展開していきます。まだGitを使ったことがないという段階の人が、「初心者がGitを使い始める時に起こる一通りの事」をわかばちゃんを通して疑似体験できる構成なので、予習としても苦痛を感じず通読できるのではないかと思います。

似た分野で解説漫画を描いている自分としては、「あっ、この1コマで済ませてるやつもうちょっと2~3コマ使って段階的に説明したい……」と感じる箇所もあったのですが、これをやり過ぎるとマンガのページがどんどん膨れあがってしまうので、テキストを本文として置く限りにおいては現状はバランスの良い落とし所と言えると思います。)

純粋にGitの解説としての出来は?

本書の(ページ数的な意味での)前半はGitの基本的な使い方、後半は前半を踏まえてのケーススタディ集です。

Gitには山ほど機能があり、また、それぞれが関連しあっていることがあるため、「そういえば、あれも」「そういえば、これも」と解説し始めるとキリがありません。本書前半は「とりあえずこれだけ覚えれば使える」というレベルに持っていく事を優先して脇の話はざっくりカットしているので、多すぎる情報に惑わされることなく、押さえておかないといけない最も重要なポイントを知る事ができます。

また、そうして基本を分かった上でいざ使い始めてみて起こる様々なトラブルや「これをやりたい」といったニーズに対し、本書後半ではケースごとにその解決方法を解説しています。話題のチョイスとして、Gitの初心者~中級者が遭遇しがちな物がまとめられているため、実用性は高いです。

重要な事として、「やりたくなるけど、やっては駄目」な事は極力解説していないのも好印象です。具体的にはgit push -f絡みの話がそれで、データ喪失などの致命的な事態に陥らないためには「それはできない」という事にしておいた方が安全なのは確かです。この点からも、本書が実際の運用の事を強く意識していることが伺えます。

自分も解説を書く立場の人間ですが、自分も含めて自分から本を書こうというような人は、自分の持つ知識を無意味にひけらかしたがるものです(偏見)。よほど自分を抑制しないと、「Gitの生まれた経緯はこれこれこうで……」というような脇の話や、使用頻度の低い使いどころの難しい機能のように判断力(判断材料となる知識)が不足している初心者には使いこなせない知識など、実際の運用上はどうでもいい情報を盛り込んでしまいがちです。

その点、本書はわかばちゃんという視点を設定することで、「ここは教えなくていい」「今は教えない方がいい」というノイジーな情報をバッサリ切り落としていて、この本を必要としている読者のための本というスタンスを貫いています。読者に寄り添う姿勢を崩さない真摯さと、嘘を書かない真摯さを保ち、その上で必要な情報をきちんと盛り込むとい本書のバランス感覚は、見事だと思います。

SourceTreeユーザ以外へのケアが課題か

これはターゲッティングによるものなので仕方がないのですが、基本的にSourceTreeを使っての運用の解説なので、それ以外のツールでのやり方は解説されていません。「SourceTreeを極限まで使いこなす」という観点ではなく「Gitを始めるにあたって最初の道具としてとりあえずSourceTreeを選択する」という観点での解説ですし、pushやpullといった用語はどのツールでも共通なので、本書での解説はほとんどそのまま他のクライアントに読み替えて応用できるのですが、コマンドラインツールのようにまったく操作体系の異なるクライアントの場合はそこそこ苦労しそうな気がします。

希望としては、例えば本書の説明の下にgitコマンドや別のツールでのやり方の説明があればなお良かったのでは?とも思うのですが、それをやり始めると内容が薄いわりに分量だけあるという本になってしまいますし、第一、多すぎる情報の提示は初心者を惑わすだけですので、これらをカットしたのは至極妥当だと自分も思います。電子書籍のように、補足的な内容を視界の中にオーバーレイ(スーパーインポーズ)表示できる媒体の発展に期待したい所です。

なお、gitコマンドを使った操作に関しては、著者の湊川さんご自身の手による以下のコンテンツが存在しています。

副読本として本書と併せて読むと、SourceTreeを通した見え方以外のGitが見えてきて、よりGitへの理解が深まると思います。

シス管系女子+マンガでわかるGit コラボ特別編の裏側(想像) by Piro/結城洋志 on pixiv

非エンジニアの人にこそ読んでもらいたい

上では「課題あり」という書き方をしてしまいましたが、コマンド操作に関する説明のような、見た目に抵抗感や苦手意識を持たれそうな内容をばっさりカットしていることで、第一印象でのとっつきやすさは高く保たれていると思います。そこで考えられるのが、非ITエンジニアの人にGitを使ってもらうための入門書としての活用です。

本書の例はWebサイト制作を想定して書かれていますが、Web制作であれば企画やデザイナーといった職種の人も実作業に関わってきます。また、自分が会社の業務として過去に手がけたWEB+DB PRESSの記事執筆でも、複数人で並行して執筆して最後にとりまとめるという事をGitで行っていました。バージョン管理システムや、それを前提としたGitHubのようなサービスの便利さは、ソフトウェア開発に限らずあらゆる場面で実感できるものです。こんな便利なものをITエンジニアだけで使うに留めるのは勿体ないです。

電子化可能なデータを取り扱うプロジェクトに関わるあらゆる人の間で、データを共有しその履歴をトラッキングするインフラとして、Gitを活用する。ファイルの履歴管理や連絡の手間から皆を解放する。そのような働き方の改革にすらなり得る手引き書として、本書は広くおすすめできます。

わかばちゃんと学ぶ Git使い方入門〈GitHub、Bitbucket、SourceTree〉
湊川 あい
シーアンドアール研究所 (2017-04-21)
売り上げランキング: 787

書評:わかばちゃんと学ぶ Webサイト制作の基本 - Apr 29, 2017

わかばちゃんと学ぶ Webサイト制作の基本
湊川 あい
シーアンドアール研究所 (2016-06-15)
売り上げランキング: 27,596

そういえばちゃんとした書評を文章としてまとめていなかったことに気が付いたので、改めて書き留めておこうと思います。自分は対象読者層から外れていますが、「マンガで技術解説」という非常に近い領域で活動をしている以上、気になるのは事実なので、それならいっそちゃんと読んで学びを得ようと思い自費で購入しました。

本書はひとことで言えば、「今これからWebサイト制作を初めてみようと思っている、スタート前の人のための本」「格好いいサービスに憧れてWeb制作を始めてはみたけど、知れば知るほど次から次に新しいキーワードが出てきて、勉強しないといけない範囲がどんどん広がっていってしまい、途方に暮れている人」だと思います。

昨今のWebというと、アプリ寄りの見え方をするSingle Page Applicationと呼ばれるつくりが流行りで、やれAngularだのReactだのという話になりがちだと思うのですが、それらも全て基礎があっての話。SPAを作るにせよ、そこから移り変わった次のトレンドに乗るにせよ、絶対に外せないであろう知識というのはあります。本書は、主人公の「わかばちゃん」をはじめとするキャラクター達を立て、わかばちゃんを皆がサポートして導くという流れに乗せて、Webサイト制作の基礎中の基礎となるトピックを一通り解説する入門書ということになります。

「マンガでWeb技術」?

本書の基本構成は「その節で解説する概念の大まかな絵解き説明、あるいは内容に絡んだネタの4コマ」と「それに続いてテキストや図での解説(本文)」という形で、マンガ部分の分量はそんなに多くはないです。「マンガで」という所に期待しすぎると、もしかしたら肩透かしを食らうかもしれません。マンガ部分だけを追った場合に得られる情報量は限られていますので、当たり前と言えば当たり前ですが、ちゃんとテキストも読むことが必要です。

自分は中学校でNEW CROWNで英語を初めて教わりましたが、いらすとや系の無色透明な・人格を意識させない絵ではない、漫画雑誌で見慣れた絵柄の・趣味嗜好などのバックグラウンドを持っていそうなキャラクター達(当時の物は「緋が走る」のあおきてつお氏がイラストを担当されており、この形式の先駆けだったそうです)がいることで、「堅苦しくてつまらない教科書、ではない。僕らの価値観、僕らの好みの事をちゃんと分かってくれている。頭ごなしに押し付けてきているのではない」と感じ、未知のものへの抵抗感がずいぶん薄れたような記憶があります。

10月の誕生盆栽で誕生日をお祝いするわかばちゃん&HTMLちゃん by Piro/結城洋志 on pixiv

本書に対して抱く率直な感想は、その感覚に非常に近いと思います。解説のためのマンガというより、読者の心理的抵抗を和らげる緩衝材としてのマンガ、という性質が主であるように感じました。そして、その狙いは見事に果たされていると思います。自分を未熟な初心者の若葉ちゃんと重ねて読み進めることで、Web制作にまつわる膨大なトピックの中から「まず最初に押さえておかないといけないのは、ここ!」という部分をストレスなく学べるのではないでしょうか。

肝心の「解説」の質はどうか?

マンガは導入に過ぎないとはいえ、本編のテキストも決して堅苦しくはなく、文字だけではイメージしにくいであろう抽象的な概念の説明に図を多用していて、全体としては平易な解説になっていてます。「分かりやすい解説書にする」ための工夫が凝らされていますので、引っかかりを覚えることなくするっと通読できると思います。

初心者向けの技術解説は、どこまで説明してどこからをカットするか、例え話をする時はどこにフォーカスしてどこを無視するか、話を単純にするために嘘をつくのかつかないのか、という匙加減が難しいものです。あれもこれもと入れていくと、必然的に個々の解説に割ける文章の分量が減り、説明はおざなりになってしまいます。

本書は、自分の役割はあくまで導入と割り切っていて、難しい概念の話は別の専門書に任せるスタンスを取ることにより、解説として無理をせず、極力嘘をつかない、誠実な立場を取っていると感じました。本書を読んだ後であれば、「扱う話題はやたら幅広いが、内容は薄い」初心者向けの本をすっ飛ばして、中級者向けやあるいはそれ以上の難易度の本や解説サイトに挑戦していけると思います。

本書に込められた魂

自分が本書の最大の特長だと思ったのは、HTMLやCSSといった「Webサイト制作に必要な道具」の使い方の説明に終始してはいないという点です。

分量のほとんどの部分がそういった技術の解説なのは事実ですが、本書はそれらの手前の導入部に「そもそも、その道具を使って何を作ろうとしているのか? 何のためにWebサイトを作ろうとしているのか?」という目標設定のフェーズを、後ろに「で、作ったはいいが本当に目的は達成されているのか?」というフィードバックのフェーズを設けています。これにより、本書全体に一本の筋が通っていて、「イラストが豊富で内容も平易だが、作者が何を言いたいというのは特に読み取れない、雑多な内容の本」ではない、「やりたい事の本質は人とのコミュニケーションであり、前提の立て方次第で最適な手段は変わる。手段としてWebを選ぶというのは、こういうこと。その手段はこういうもの」という考え方までもを伝える野心的な構成の本になっていると思いました。

だからこそのマンガ要素、なのかもしれません。そんな深いテーマを語る長いテキストは途中で飽きてしまうという人でも、わかばちゃんと同じペースで進み続ければゴールに辿り着ける、そういう本なのだと言えます。

まとめ

Webは新しい技術が絶えず生まれ廃れる、荒波のような世界であり続けています。Webサイト・Webページ制作に関わる技術の全体像を把握しきることは困難ですし、その場を乗り切るのに必要な部分だけをつまみ食いしていても、それぞれが文脈上結びつかない個別の情報を増やすだけになりがちなのではないかと思います。

本書「わかばちゃんと学ぶWebサイト制作の基本」は、そんな中を自分に自信を持って生きていくための基準点となる、一朝一夕に廃れることのない確かな知識を伝える1冊だと思います。趣味で始める人に、仕事で関わり始めたという新人に、あるいは、自分で制作はしないまでもWebサイト制作の専門家と組んで何かをしようという人に、おすすめです。

わかばちゃんと学ぶ Webサイト制作の基本
湊川 あい
シーアンドアール研究所 (2016-06-15)
売り上げランキング: 27,596

技術書典2への参加で得た物、思った事 - Apr 16, 2017

サークル「シス管系女子会」のここまでの活動の振り返りです。 (技術書典2でのスペースの様子) 技術書典と技術書典2コミティア119、あとデブサミのDevBooks 2017にサークル参加して得られたデータや思った事を書き留めておきます。

商業出版物を同人イベントで取り扱うという観点から

最初にお金の絡む話から書いていきます。

ペイしたのかどうか

技術書典とコミティアは商業誌の販売が可能とのことだったので、シス管系女子12を持っていった他、技術書典と技術書典2では無料頒布のフリーペーパーやリーフレットも持っていきました。先に大まかな数字を出しておくと、かかった費用と実績は以下のような感じでした。

  • 技術書典
    • 参加費:10000円
    • 無料配布物の実績:コピー本印刷費18000円、500部全て配布
    • 商業本の販売実績:各20冊前後だったか?
  • コミティア119
    • 参加費:6400円(5800円+オンライン申し込み手数料)
    • 商業本の販売実績:各10冊の計20冊(持ち込み分全て)、計34000円
    • 同人誌の頒布実績:新刊コピー本85部、計8500円
  • DevBooks 2017
    • 参加費:無料
    • 商業本の販売実績:0(そもそも販売無しだったので)
    • 同人誌の頒布実績:コミティア新刊の在庫11部と今回新刊のコピー本15部、計2600円
  • 技術書典2
    • 参加費:15000円
    • 無料配布物の実績:リーフレット印刷費約18000円、900部中640〜670部ほどを配布
    • 商業本の販売実績:計53冊、計90100円
    • 同人誌の頒布実績:コミティア新刊の再発行分18部とブックエンド4個、計9800円
    • その他費用:A0ポスター印刷費約1万円(半分に切ってA1ポスターとして使用)

イベント参加費と技術書典・DevBooksのコピー本、あと技術書典2のリーフレットの印刷費は日経BP持ちで、ムックの販売は中間マージン無しの委託販売の体裁として、ムック売上額はそっくりそのまま日経BP社に渡しています(oh, ボランティア……)。1日人を張り付ける人件費を考慮に入れなければ、多分黒字にはなっていそうな気がします。

無料配布物について

技術書典1ではコピー本を無料配布し、技術書典2ではリーフレットを無料配布しました。 (技術書典2で無料配布したリーフレット) ただのチラシを配ってもどうせ見てもらえないと思ったので、中身は漫画になっています(シス管系女子BEGINSの前後に「いかにも宣伝」という感じの漫画を足した)。通り過ぎる一瞬で目を留めてもらうのは無理でも、持って帰ってじっくり読んでもらえるといいな……という魂胆です。この辺は、イベント以外の場でも買える商業出版物ならではの割り切りですね。

元々、技術書典2でもコピー本の体裁の物を無料配布する事を考えていたのですが、思ったよりページ数が増えて表紙込みで24ページになってしまった結果、どう作っても1冊あたりの原価が100円近くになってしまう事が分かった(なのでDevBooksでは一応100円での頒布としました)ため、A4サイズ3つ折りのリーフレット両面を使った縮刷版という体裁にしました。苦肉の策でしたが、これだと印刷枚数次第ではフルカラーでも1枚20円を下回るくらいになるので、結構アリな気がしています。

最初の技術書典では当初は、スペースに置いておいて立ち寄って話を聞いてくれた方や希望者に手渡すつもりだったのですが、「どうせ無料で配布するのなら通りかかった人にどんどん渡しても同じなのでは?」とツッコまれて、それもそうかと思って「無料配布のサンプルです」と声をかけて渡していくスタイルに切り替えたのでした。

通行中の人への声かけは同人イベントではマナー違反とされている事が多いようで、COMITIAに至っては「呼び込み禁止」とはっきり明記されています。技術書典では「商業出版物を取り扱う場合は企業参加扱い」というレギュレーションで、特に明示的に禁止するようなルールの記載も無かったので「企業ならこのくらいはやってもバチは当たるめぇ」と開き直って声を出していましたが、迷惑行為と言われてしまうとグウの音も出ないので、チラシ撒きともども、やるならせいぜい前を通りかかる人に届く程度の声量に留めた上で、恨まれる&出禁になる覚悟でやりましょう。とだけ書いておきます。

プロモーションの場としてはどうなのか

元々、シス管系女子会としてのイベント参加は以下のような目論見で始めました。

  1. 既存読者層やTwitterのフォロワー層以外の人(会場で初めて知った、という人)へ認知を拡大したい
  2. イベント用頒布物のために自主的に特別編を制作し、Webその他で自由に使えるコンテンツを増やすきっかけにしたい
  3. イベントに参加された方のブログ等から2次的・3次的に情報が拡散されて欲しい

結論から言うと、このうち1と2は実現され、3は目立った結果には繋がっていないという印象です。

1は、前述の通り会場でのムック売り上げがそこそこあり、その際にシリーズ2冊をまとめ買いしてくださる方が多かったという事から、やはりまだまだリーチできていない潜在読者がいるという事の証明にもなっていると思います。

2は、元々出版社サイドに何度か「試し読みになるように本編の一部を公開したい」という事を打診しているのですが、「原稿料を支払って下請けに制作させたコンテンツを、何故下請けが勝手に外に出したがるのだ?」という意識があるのかないのか進展が無いので、だったら原稿料もらわなくていいから自分で好きに使えるように勝手に描くわという事でコンテンツを制作したという事です。実際にこれを動機として特別編を制作し公開に繋げられていることから、〆切駆動型の自分には確かに効果的でした。

3は、その後のイベントレポートやTwitterでの反応を見る限りでは、「このイベントで初めて知った」「この試し読みで初めて意識した」みたいな新規開拓の方の反応はあまり見られませんでした。また、全一般参加者の方のうち1/5~1/6にリーフレットを受け取って頂けた計算になりますが、イベントレポートの写真等で「会場で入手した物」の一覧に写っている例は少なかったように思いました。

  • 「会場で無料配布のリーフレットを受け取るタイプの人」と「自分からイベントレポートを書くなどして情報を発信する人」は属性があまり重なり合わないということ?
  • 「なにこれwwwwうけるwwwww拡散しよwwww」みたいなまでに人を突き動かす程のフックの強さが無い、ネタとしての爆発力に欠ける、という事?

理由は色々考えられますが、成果を上げるためにはまだまだ工夫が必要そうです。

「技術同人オンリー」イベントとしての技術書典

初回の技術書典の話はその時のエントリをご参照下さい。以下は技術書典2の感想です。

技術同人に関心のある人ばかりが集まる希有な機会として

技術書典2当日のTwitterの反応まとめを見ると、来場者が多すぎて入場が遅々として進まない事へのコメントが多く、参加を諦めた人も多かったというのは終わった後で知りました。方や会場内では、サークルによっては開場1時間や2時間で完売の拍手が上がっていました(6時間あるイベントの序盤での在庫切れというのは相当な読み違えがあったということになります)。「技術書オンリー」「同日に別の場所でも同人イベントが開催されており、その中にはけものフレンズのオンリーイベントも含まれていた」「天気は雨」など、来場者が少なくなる方向の材料ばかりがあったにも関わらずこの盛況さということで、当日に至ってすらも来場者数の読みが極めて難しい状況でした。

前回は会場が建物内の地下と2階に分断されていた上に、入場が整理券制になっていたことから、全体の様子というのは正直見えにくかったのですが、今回は他の同人誌即売会イベントに近いレイアウトだったので、全体の混雑具合などが俯瞰しやすかったです。そこで抱いた率直な感想は、まさに普通の同人誌即売会だなという事でした。

これは、コミケやコミティアなどに行った事のある自分からすると驚くべき事です。というのも、これらのイベントで評論・技術ジャンルのスペースを訪れると、他の混雑具合とは一転してびっくりするぐらい人がいないからです。そんなガラガラ具合のジャンルのサークルだけを集めるわけですから、本当にイベントとして成立するのか?という心配の声が運営サイドからすらも上がっていたのも頷けます。

別にアンケートや聞き取り調査をやったわけではないのでただの想像ですが、実はみんなこういう「知の共有」に焦点を当てたイベントを求めていたということなのではないでしょうか。

既存のイベントで技術同人が参加できるイベントといったら、基本的にはオールジャンル(取り扱う内容のジャンルを問わない、なんでもあり)のイベントですが、それらのイベントでは技術同人以外のサークルはほとんどが漫画やイラスト、エンターテインメント作品をメインにしていますし、比較的近いジャンルのオンリーイベントと思われる文学フリマであってもメインは小説や評論です。そういう状況では、技術同人に興味がある人でも、行く苦労や金銭的コストに対して得られる物が少ない、つまり「割に合わない」と判断されて、行ってみようという気が起こりにくいのではないかと思います。例えて言うと、アニメイトの一角で技術書籍のコーナーが設けられていたとして、技術者を自認する人がわざわざそこまで買いに行くのか? あるいは、アニメイトに来る客が技術書コーナーまで見て回るのか? っていう話です。

その点、技術書典は最初から技術書オンリーと銘打っていますから、「当たり」に出会えるだろうという期待値はグッと高まります。来場者が多いのは、他に類似のイベントが無いからそういうニーズがここに一気に集中してしまっているという事なのではないか。というのが、僕なりの予想です。

より質の高い情報が精錬され形になる機会として

IT技術者をやっていると、よく「技術カンファレンスや勉強会は、発表する人が一番勉強になる」「雑誌の記事や本は、著者が一番勉強になる」なんて話を聞きます。

知見やノウハウは、自分だけが見るメモ程度であればいくらでも書けますが、他の人にも共有できるレベルの内容に引き上げるのには手間がかかります。話を一般化したり、あやふやだった所を調べ直して根拠をはっきりさせたり、話題を整理したり……こういう事が面倒で、メモのまま放置されてしまう情報や、メモすら残されないまま忘れられてしまう情報というのは結構あります。

1コマの発表内容だったり1冊の本だったりといった「パッケージ」の形にまとめる時には、必然的にそういった作業が発生します。「イベントに参加するので」「そこで新刊として発表するので」という風に〆切を設定する事で、情報を精錬するための動機が生まれ、より価値の高い情報が出てくる、そういう動機になるというのは、技術書典というイベントの重要な意義の1つと言えるでしょう。

青田買いの場として

イベント終了後、技術書典2での刊行物をベースに商業出版する事になったという話を見かけました。同人誌で活躍していた人が商業誌で活躍するようになるというのはマンガ・ゲーム業界にはよくある話で、出版社の人が会場内を見て回って、有望そうな人に声をかけるという事が、技術書でも起こっているということのようです。

会場内で実際に売れ行きが良ければ商品化する好材料になるというのはもちろんあるでしょうが、そもそもこういうイベントに物を出している時点で「〆切を設定して」「それに間に合うように」「情報を整理してパッケージ化して」同人誌として世に出すという事ができている訳で、ネットでブログに断片的な情報を書き散らかしているだけの人に比べれば、商業出版物の原稿を書くという仕事にちゃんと取り組んでくれそうだと期待できるのではないかと思います。

著者と読者が対面で話せる機会として

最後に、これは同人誌即売会一般の話ですが、対面での頒布は「読者の方と直接触れ合える」という事が最大の特長でしょう。

先のまとめで「ネットでええやん」という感じのコメントをいくつか見かけましたが、VRでないただのインターネットモールを想定しているのであれば、それは「欲しい物を事前に決めて、買いに行って、買う」という事以上の意義をイベントに対して見出していないという事なのではないかと思います。同人イベントには、会場で作者に直接感想やお礼を言いに行く人もいれば、会場で読者が喜び興奮している様子を見たくて出展する人もいます。

自分の場合は、Webでない紙媒体で連載をやっていて、読者層がWebでアクティブな人達の層とは微妙に違うらしいという日経Linux誌での連載であることから、いつもはあまり「読者の方々に実際読まれている」という実感を持てずにいます。そのため、目の前にいる方に「持ってる」とか「読んでる」とか言って頂けるのは素直に嬉しかったです。普段なかなか認識できない「読者の実在」を意識することができて、励みになるのは間違いないです。

あと、対読者というのとはちょっとズレますが、ご同業の方と話せる機会としても自分にとっては有意義でした。技術書典2ではお隣のスペースがマンガで分かるWebデザイン/マンガでわかるGitの湊川さんのスペースだったため、イベント中やイベント終了後の合同打ち上げでこの仕事の事やそれ以外の事など色々話せたのがとても嬉しかったです。

DevBooks 2017について

そういえば個別にレポートを書いていなかったので、DevBooksの事についてもここに書き留めておこうと思います。

恐らく技術書典の成功を受けてだと思いますが、今年はDevelopers Summit 2017の会場内の1室に小規模な同人誌即売スペースが設けられていました。商業出版物の頒布は不可というレギュレーションだったので、直前のコミティア119での在庫放出と技術書典2向けの頒布物のプレお目見えだけできればいいかと思って参加してみました。

で、参加してみた感想なのですが、とにかく精神的にキツかったです。

というのも、通常の同人イベントだと全時間を通じて人の流れがありますが、DevBooksはデブサミの1コーナーという性質上、セッションとセッションの合間にどっと人が来るもののそれ以外の時間はガラッガラで、もう暇で暇でしょうがなかったです。一人での参加だったので、誰かに店番を任せてセッションを見に行くという事もできませんでしたし……

あと、「シス管系女子」というコンテンツとイベントの来場者層がマッチしていなかったようだという事も感じました。デブサミはどちらかというと流行りの技術に強い関心のある方が多いようで、DevOpsとかと真逆の方向を向いている「シス管系女子」は訴求力が無いのでしょう……多分。

ということで、もし2回目以降があるとしたらですが、「元々デブサミのセッションに興味があって」「技術同人もやっている」「当日は店番の手伝いをしてくれる人がいる」という条件を満たせる方が、会場内での荷物置き場確保も兼ねて参加するのが良いのではないかと思います。

まとめ

まあ何というかタラタラ書いてきましたが、オフラインのイベントはやっぱ良いですよ。オンラインでオンデマンドでいつでも欲しい情報が手に入る、どころか、欲しくない情報まで洪水のように押し寄せてくるのが当たり前の今だから、身体感覚を伴うライブな体験の価値がより際立つ。などという言い方も実に月並みですが、その月並みな体験をした上で「やっぱり月並みだね」と言うのと、体験しない状態で想像で物を言うのとでは違うと思いますので、未体験の人は体験してみて欲しいです。直近では4月29日に幕張メッセで「超技術書典」というのが開催されるので、まずはここから。

Tree structure and tab icons are always lost after restart / 再起動すると必ずツリー構造やタブのアイコンが失われる - Mar 12, 2017

Q

When I start Firefox, tree of tabs and favicons are always lost. Even if I reorganize tree of tabs again, they are flattened after every restart.

Firefoxを起動すると、必ずタブのツリーとタブのアイコンが失われます。ツリーを作り直しても、再起動する度にツリーが失われてすべてのタブが同階層に表示されてしまいます。

A

Please press Ctrl(Command)-Shift-J to open the "Browser Console" - one of developer tools of Firefox. Is any error message like this reported?: NS_ERROR_ILLEGAL_VALUE: Component returned failure code: 0x80070057 (NS_ERROR_ILLEGAL_VALUE)
[nsISerializationHelper.deserializeObject] Utils.jsm:94

Screenshot: (The console with the error message.)

Then, the problem may be caused by invalid information stored in your session data. Try following steps to cleanup your session data:

  1. Close the browser console.
  2. Input "about:config" into the location bar and hit the Enter key.
  3. Skip the confirmation message by clicking the button in the page.
  4. Input "devtools.chrome.enabled" into the "Search:" field.
  5. Then you'll see just one entry. Double click it to turn the value to "true", if it is "false".
  6. Open the browser console again by Ctrl(Command)-Shift-J. Then you'll see an input field at the bottom of the console.
  7. Copy this script and paste it into the bottom field of the console:
    var TabStateCache = Components.utils.import('resource:///modules/sessionstore/SessionStore.jsm',{}).TabStateCache;
    Array.forEach(
      gBrowser.browsers,
      (browser)=>
        TabStateCache.update(browser, {
          iconLoadingPrincipal : null
        })
    );
    "OK"
  8. And hit the Enter key in the field. Then you'll see a new message "OK" in the console.
  9. Restart Firefox.

Ctrl(Command)-Shit-Jを押して、Firefoxの開発ツールの1つである「ブラウザコンソール」を開いて下さい。以下のようなエラーメッセージが出力されていませんか?:NS_ERROR_ILLEGAL_VALUE: Component returned failure code: 0x80070057 (NS_ERROR_ILLEGAL_VALUE)
[nsISerializationHelper.deserializeObject] Utils.jsm:94

スクリーンショット:(コンソールに上記のエラーが出ている様子)

もしこのエラーが出ているなら、問題はセッション情報の中に混入してしまった不正なデータが原因で引き起こされている可能性があります。以下の手順でセッション情報をクリーンアップしてみて下さい。

  1. ブラウザコンソールを閉じる。
  2. ロケーションバーに「about:config」と入力し、Enterキーを押す。
  3. 確認のメッセージが表示されるので、ページ内に表示されるボタンをクリックして先に進む。
  4. 「検索:」欄に「devtools.chrome.enabled」と入力する。
  5. 項目が1つだけ見つかるはずなので、値が「false」になっている場合は項目をダブルクリックして値を「true」に変更する。
  6. Ctrl(Command)-Shit-Jを押してブラウザコンソールを開き直す。すると、コンソール株に入力欄が表示されるようになっている。
  7. 以下のスクリプトをコピーして、その入力欄に貼り付ける。
    var TabStateCache = Components.utils.import('resource:///modules/sessionstore/SessionStore.jsm',{}).TabStateCache;
    Array.forEach(
      gBrowser.browsers,
      (browser)=>
        TabStateCache.update(browser, {
          iconLoadingPrincipal : null
        })
    );
    "OK"
  8. そのまま入力欄でEnterキーを押す。コンソールに「OK」というメッセージが出力される。
  9. Firefoxを再起動する。

テキストファイルの中に書いたinclude文をsedで解決する - Mar 02, 2017

このエントリはQiitaとのクロスポストです。

複数のファイルに共通する部分があるとき、共通箇所をまとめて切り出しておいて、各ファイルからはそれらを参照するだけにする、というのはよくある話です。C言語なら#include <stdio.h>という書き方をしますし、Web制作をやる人なら、CSSの@import規則をご存じだと思います。

しかしたまに、これに似たことを静的なファイルで行って「include文の位置に参照先のファイルがそのまま埋め込まれたファイル」を作りたいという場面が出てきます。

この記事では、そんな「静的なファイルを生成するために、ソースとなるテキストファイルに書かれたinclude文をシェルスクリプトで処理して、参照先ファイルの内容をその位置に埋め込んだ結果のファイルを得たい」というニーズに対する、なるべく効率のよい実現方法を模索してみます。

やりたいこと

以前、さくらのレンタルサーバーの一番安いプランでWebサイトを公開するノウハウという記事で、「ssh接続できない月額129円の激安レンタルサーバーでも、手元にLinuxな環境があるならコマンドラインのFTPクライアントとシェルスクリプトでrsyncっぽいことができるよ! ついでに色々前処理もさせられるし、シス管系女子のサイトのようにチープな静的コンテンツだけのサイトなら全然余裕で運用できちゃう! やったね!」という事例をご紹介しました。

その際にやりたかった前処理のひとつに前述のinclude文があり、全ページで共通のヘッダやフッタを

html/_parts/metadata.html(共通パーツ):

  <meta charset="UTF-8">
  <meta name="twitter:card" content="summary_large_image">
  <meta name="twitter:site" content="@piro_or">
  <meta name="twitter:creator" content="@piro_or">
...

こんな風に断片ファイルとして切り出して用意しておいて、HTMLファイルの中に

html/index.html(ソースファイル):

<!DOCTYPE html>
<html lang="ja" xmlns:og="http://ogp.me/ns#" xmlns:fb="http://www.facebook.com/2008/fbml">
<head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# article: http://ogp.me/ns/article#">
<!--EMBED(metadata.html)-->
  <title>シス管系女子って何!? - 【シス管系女子】特設サイト</title>
...

のように書いておき、アップロード直前に

html_resolved/index.html(埋め込み後):

<!DOCTYPE html>
<html lang="ja" xmlns:og="http://ogp.me/ns#" xmlns:fb="http://www.facebook.com/2008/fbml">
<head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# article: http://ogp.me/ns/article#">
  <meta charset="UTF-8">
  <meta name="twitter:card" content="summary_large_image">
  <meta name="twitter:site" content="@piro_or">
  <meta name="twitter:creator" content="@piro_or">
  ...
  <title>シス管系女子って何!? - 【シス管系女子】特設サイト</title>
...

のように解決する、という事をしていました。

良くない実装例

最初に先の記事を公開した時の実装は、以下のようなものでした(実際にはちょっと違う書き方でしたが、要旨としてはこんな感じ、ということで)。

build.sh:共通パーツを埋め込む:

#!/bin/bash

# 環境によってsedで拡張正規表現を使うためのオプションが違うので、
# egrepコマンドのように使える「$esed」を定義しておく。
case $(uname) in
  Darwin|*BSD|CYGWIN*)
    esed="sed -E"
    ;;
  *)
    esed="sed -r"
    ;;
esac

rm -rf html_resolved
cp -r html html_resolved

# include文の検出用正規表現。
# ファイル名部分は、後方参照で取り出せるように`()`で囲っておく。
embed_matcher='<!-- *EMBED\( *([^) ]+) *\) *-->'

# 処理対象のファイル(include文があるファイル)を検索する。
egrep -r \
      --include='*.html' \
      "$embed_matcher" \
      html_resolved |
  cut -d ':' -f 1 | # ファイルパスだけを取り出す。
  uniq | # 1ファイルの中で何カ所も見つかる事があるので、重複を取り除く。
  while read path
do
  # 見つかった各ファイルに対して処理を行う。
  echo "updating $path"
  # ファイルを退避し、
  mv "$path"{,.tmp}
  # ファイルの内容を1行ずつスキャンする。
  # `IFS= read -r`とすることで、行頭・行末の連続する空白文字や
  # 行の中のエスケープ文字を保持する。
  cat "$path.tmp" | while IFS= read -r line
  do
    # include文がある行だったら、
    if echo "$line" | egrep "$embed_matcher" 2>&1 > /dev/null
    then
      # 埋め込み対象のファイルの内容をcatして、
      # リダイレクトで書き出す。
      parts_name="$(echo "$line" |
                      $esed -e "s/^.*$embed_matcher.*/\\1/")"
      cat "html/_parts/$parts_name" >> "$path"
    else
      # それ以外の行はそのまま書き出す。
      echo "$line" >> "$path"
    fi
  done
  rm "$path.tmp"
done

これでも一応目的は達成されていたのですが、以下のような問題が残ってしまっていました。

  1. ソースの1行ずつに対してBashのwhileループを回すので、とにかく遅い
  2. 行の中にinclude文があると、include文の位置ではなくその次の行にファイルが埋め込まれる

1は、処理対象のファイルの行数とファイル数が増えるごとに大きな負担となります。前の記事を書いた時には「変更が無かったファイルは無視する」という別方向からの対策を取ってみましたが、それでも全ファイルを対象に処理し直す時にはずいぶん待たされてしまいます。

2は、とりあえず今のところ問題にはなっていませんが、include文をHTMLのコメントの形式にしたので、もしかしたらこの制約の事を忘れて行中に書きたくなってしまうかもしれません。その時に「えっそんな制約あったなんて……」と戸惑う羽目になる前に、なんとかできるものならなんとかしておきたいところです。

より良い実装

その後長らくそれっきりになっていたのですが、仕事の中でまた似たようなことをやりたい場面(Firefoxの法人導入では管理者による設定を静的なJavaScriptファイルとして用意するのですが、「大部分は共通だけれども一部分だけが異なる」という設定ファイルを複数種類用意する必要が生じたのでした。共通部分を括り出すのでなく、ソースファイルに書かれたプレースホルダの位置に、環境ごとの別のソースファイルの内容を埋め込んで各環境ごとの静的なファイルをビルドしたい、という感じです)が出てたので、これを機にもっとマシなやり方を探してみました。すると、sedrコマンドというまさにこういう事をやるためにあるような機能の情報が見つかりました。

ということで、ここからがこの記事の本題です。

マッチした箇所に他のファイルの内容を埋め込む、を高速にやる

sedrコマンドは、「カーソル行の直後(次の行の直前)に別のファイルの内容を読み込んで挿入する」という物です。パターンマッチと組み合わせれば、「include文をパターンマッチで見つける→見つけたinclude文の箇所にinclude対象の外部ファイルの内容を埋め込む」という事もできるはずです。

例えば、最初に示した例をべた書きするとこうなります。

$ cat html/index.html |
    sed '/<!--EMBED(metadata.html)-->/r html/_parts/metadata.html' \
    > html_resolved/index.html

sedの機能なので、Bashのwhileループと比べると圧倒的に高速です。これで、問題の1つ目の「とにかく遅い」という点が解消されます。やりましたね!

マッチした箇所の次の行、ではなく、マッチしたまさにその箇所に他のファイルの内容を埋め込む

ただ、まだ問題はもう1つ残っています。sedrは「マッチしたまさにその箇所」ではなく「マッチした箇所が含まれる行とその次の行の間」にファイルの内容を出力するため、行の中程にinclude文があると「include文の前の文字列→include文の後の文字列→参照されたファイルの内容→次の行」という結果になってしまいます。

この問題を解消するには、include文の後で必ず改行するように事前処理してしまうのが手っ取り早いです。

$ cat html/index.html |
    $esed "s/($embed_matcher)( *[^$])/\1"\\$'\n''\3/g' |
    sed '/<!--EMBED(metadata.html)-->/r html/_parts/metadata.html' \
    > html_resolved/index.html

何だかゴチャゴチャ書いてあって分かりにくいですが、置換の指定としては以下のような内容になっています。

  • 置換前→(<include文>)(<改行と空白以外でinclude文より後の文字列>)
  • 置換後→<マッチしたinclude文><改行文字><include文より後の文字列>
  • 行内のマッチ箇所をすべて置換(gフラグ)

sedで置換後の文字に改行文字を含めれば行の途中で改行することができますが、それには色々と工夫が必要です。詳細はsedで改行を出力するをご覧下さい。

このように置換してからsedrでinclude文を処理すれば、ちゃんと「include文の前の文字列→参照されたファイルの内容→include文の後の文字列→次の行」という順で出力されるようになるわけです。

ファイル内のすべてのinclude文を処理する

ここまでのコマンド列にはinclude文を解決するための指定をべた書きしていましたが、実際には任意のファイルでいろんなファイルに対するinclude文を処理する必要があります。後方参照でsed -r '/<!--EMBED\((metadata.html)\)-->/r html/_parts/\1'みたいなことができると楽なのですが、残念ながらsedrコマンドの読み込み対象ファイルの指定には後方参照は使えません。

解決策としては、sedを実行するコマンド列をsedで組み立てるという方法があります。

$ embed_matcher='<!-- *EMBED\( *([^) ]+) *\) *-->'
$ embed_mark_to_resolver="s|($embed_matcher)| -e '/\\1/r html/_parts/\\2'|"
$ cat html/index.html |
    egrep -o "$embed_matcher" |
    sort |
    uniq
<!--EMBED(metadata.html)-->
<!--EMBED(footer.html)-->
<!--EMBED(header.html)-->
...

grepegrepgrep -Eと同等)に-oオプションを指定すると、「マッチした文字列がある行」ではなく「マッチした文字列そのもの」、ここではinclude文の部分だけが出力されます。それをsed -r "s|($embed_matcher)| -e '/\\1/r html/_parts/\\2'|"で置換して-e 'sedスクリプト'というコマンドラインオプションに変換すると、こうなります。

$ cat html/index.html |
    egrep -o "$embed_matcher" |
    sort |
    uniq |
    sed -r -e "$embed_mark_to_resolver"
 -e '/<!--EMBED(metadata.html)-->/r html/_parts/metadata.html'
 -e '/<!--EMBED(footer.html)-->/r html/_parts/footer.html'
 -e '/<!--EMBED(header.html)-->/r html/_parts/header.html'
...

この出力に対してtr -d '\n'で改行を削除し(1行に繋げ)てsedのコマンドライン引数に指定すれば、ファイル内のすべてのinclude文を一気に処理することができます。

$ resolve_embedded_resources="sed $(cat "$path" |
                                egrep -o "$embed_matcher" |
                                sort |
                                uniq |
                                $esed -e "$embed_mark_to_resolver" |
                                tr -d '\n')"
$ cat html/index.html |
    $esed "s/($embed_matcher)( *[^$])/\1"\\$'\n''\3/g' |
    eval "$resolve_embedded_resources" \
    > html_resolved/index.html

ちなみに、-eオプションの指定の中には丸括弧など拡張正規表現では特別な意味を持つ文字があるので、これらは本来スケープする必要があります。が、$esedではなくsedを使うようにすればエスケープは不要です。

$resolve_embedded_resourcesと直接書くのではなく、わざわざevalコマンドを使ってeval "$resolve_embedded_resources"と書いているのは、組み立てたコマンドラインオプションの'が値の一部にならないようにするためです。というのも、そのままパイプラインの中に cat ... | $resolve_embedded_resources | ...と書くと、シェル変数が展開されてsed -e "'/<!--EMBED(metadata.html)-->/r html/_parts/metadata.html'" ...のように書かれた扱いとなってしまい、sedに「'なんてコマンドは無い」と言われてしまからです。evalを使えば、指定文字列を改めてシェルのコマンド列としてパースするため、sed -e '/<!--EMBED(metadata.html)-->/r html/_parts/metadata.html' ...と書いたのと同じに扱われるようになります。

また、これだけだとinclude文自体がソースの中に残ってしまうので、ついでにそれらを消す置換操作の指定も加えるとこうなります。

$ embed_mark_to_resolver="s|($embed_matcher)| -e '/\\1/r html/_parts/\\2' -e '/^ *\\1 *$/d' -e 's/ *\\1 *//'|"

1つのinclude文から3つの-eオプションができる形ですね。

さらに、これだとマッチしたinclude文の中にファイルパスのデリミタの/が入った時に破綻してしまうので、マッチングパターンの正規表現を囲う文字を/から;に変えておきます。

$ embed_mark_to_resolver="s|($embed_matcher)| -e '\\\\;\\1;r html/_parts/\\2' -e '\\\\;^ *\\1 *$;d' -e 's; *\\1 *;;'|"

sコマンドでは単に;で囲うだけでいいですが、rコマンドとdコマンドについては\;マッチングパターン;という風に最初に\を付ける必要があります。それをまた全体として1つの文字列の中に入れているので、エスケープがたくさん並ぶ読みにくいスクリプトになってしまいました……まぁこれはしょうがないです。

まとめ

以上を踏まえて前述のスクリプトの例を書き直すと、こうなります。

build.sh:共通パーツを埋め込む(改良版):

#!/bin/bash

case $(uname) in
  Darwin|*BSD|CYGWIN*)
    esed="sed -E"
    ;;
  *)
    esed="sed -r"
    ;;
esac

rm -rf html_resolved
cp -r html html_resolved

embed_matcher='<!-- *EMBED\( *([^) ]+) *\) *-->'
embed_mark_to_resolver="s|($embed_matcher)| -e '\\\\;\\1;r html/_parts/\\2' -e '\\\\;^ *\\1 *$;d' -e 's; *\\1 *;;'|"

egrep -r \
      --include='*.html' \
      "$embed_matcher" \
      html_resolved |
  cut -d ':' -f 1 |
  uniq |
  while read path
do
  echo "updating $path"
  resolve_embedded_resources="sed $(cat "$path" |
                                egrep -o "$embed_matcher" |
                                sort |
                                uniq |
                                $esed -e "$embed_mark_to_resolver" |
                                tr -d '\n')"
  mv "$path"{,.tmp}
  cat "$path.tmp" |
    $esed "s/($embed_matcher)( *[^$])/\1"\\$'\n''\3/g' |
    eval "$resolve_embedded_resources" \
    > "$path"
  rm "$path.tmp"
done

筆者が普段使用している環境で新旧それぞれのスクリプトをtime ./build.sh -fという感じで実行して計測してみたところ、

環境 改修前のrealtime 改修後のrealtime
Ubuntu on Let's note CF-SX3 3.217秒 0.644秒
Raspbian on Raspberry Pi2 Model B 20.072秒 2.475秒

という感じで実時間で5~8倍の高速化となりました。ラズパイでも、カジュアルに全ファイルを処理させても気にならない程度まで高速になっています。万歳!

sedawkだけでも頑張ればこういう事ができるのかもしれません。でも、ごく基本的な機能だけしか知らなくても「コマンドの実行結果でコマンド列を作る」という一工夫によってできる事の幅はかなり広がると思います。実際、この記事に「grepの結果をcut | uniqで加工しなくてもgrep -lでいける」というフィードバックを頂きましたが、これもまさに「grep-lオプションを知らなくても、基本的な文字列加工コマンドの組み合わせで目的は達成できる」という事の一例と言えるでしょう。

この記事をご覧になった皆さんも、「自分はどうせ基本的な使い方くらいしか知らないから……ガッツリ覚えるつもりもないし……」と卑屈にならず、ぜひ柔軟な発想で問題を解決してみてはいかがでしょうか?

別解:Cプリプロセッサ(cppコマンド)を使う

ここまで「include文のようなことをsedでやる」という事を頑張ってみましたが、C言語のプリプロセッサ向けのinclude文そのものと同じ仕様でよければ、それこそCプリプロセッサをそのまま使うという方法もあります。

C言語のプリプロセッサはcppというコマンドとして単体で使う事ができ、UbuntuやDebianであればその名もcppというパッケージでインストールできます。Gemのパッケージ等でバイナリをビルドする必要がある物をインストールする際の依存関係で既にインストールされているという人も多いのではないでしょうか。これがインストールされている環境であれば、

html/index.html(ソースファイル):

<!DOCTYPE html>
<html lang="ja" xmlns:og="http://ogp.me/ns#" xmlns:fb="http://www.facebook.com/2008/fbml">
<head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# article: http://ogp.me/ns/article#">
#include "html/_parts/metadata.html"
  <title>シス管系女子って何!? - 【シス管系女子】特設サイト</title>
...

このようにソースに書いておいて

$ cat html/index.html |
    cpp -P \
    > html_resolved/index.html

と実行すれば、まさにC言語のソースと同じ要領でinclude文を処理した結果を得る事ができます(cppコマンドの-Pオプションは、プリプロセッサの行番号情報を出力しないようにする指定です。これを指定しないと、# 1 "<command-line>"やら# 1 "html_resolved/index.html" 1やらといった処理中のデバッグ情報的なメッセージまでが出力に含まれてしまいます)。

もちろん、include以外の構文も使えます。ただ、たまたまプリプロセッサ向けの書き方と同じ文字列があると意図せず処理されてしまうというリスクはあります。自分はC言語には詳しくなくて地雷を踏むのが怖いので、しこしこsedで頑張ろうと思います……

ラズパイに繋いでたディスクが死んだようです - Feb 28, 2017

Raspberry Pi 2にUbuntu(Lubuntu)を入れて、その後USB接続の外付けHDDを使うようにしたRaspberry Piの自宅サーバですが、ディスクがお亡くなりになったようです……

発覚

当該サーバに相乗りしていたみんとちゃんbotが2月24日16時半頃の自動ツイートを最後に停止していた。帰宅後普段使いのユーザ=みんとちゃんbotを動作させているユーザでsshでログインしようとするが、鍵がないと言われて蹴られる(そんな馬鹿な!)。管理作業用に用意してあった別のユーザではログインできた。

普段使いのユーザのhomeは外付けHDDにあり、管理作業用のユーザのhomeはラズパイ本体のMicroSDにあったので、この時点でもう外付けHDD自体がアクセス不能になっていたようだ。

調査

その後再起動してみるもUbuntu自体が起動せず(起動プロセスの途中で止まってしまう)。

Ext4を扱える作業用PCを用意してMicroSDからとりあえず/logと/etcを取り出した後、メインとバックアップの外付けHDDをそれぞれUSBケーブルで作業PCに接続するも、以下のようなメッセージが/var/log/kern.log(dmesgでも可)に出て先に進まない。

Feb 27 23:54:22 hostname kernel: [  482.750854] usb 2-2: new high-speed USB device number 4 using xhci_hcd
Feb 27 23:54:23 hostname kernel: [  482.881646] usb 2-2: New USB device found, idVendor=0411, idProduct=024f
Feb 27 23:54:23 hostname kernel: [  482.881649] usb 2-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
Feb 27 23:54:23 hostname kernel: [  482.881651] usb 2-2: Product: HD-LBVU3
Feb 27 23:54:23 hostname kernel: [  482.881652] usb 2-2: Manufacturer: BUFFALO
Feb 27 23:54:23 hostname kernel: [  482.881653] usb 2-2: SerialNumber: 000000270000F09E
Feb 27 23:54:23 hostname kernel: [  483.068504] usb-storage 2-2:1.0: USB Mass Storage device detected
Feb 27 23:54:23 hostname kernel: [  483.068580] scsi host4: usb-storage 2-2:1.0
Feb 27 23:54:23 hostname kernel: [  483.068679] usbcore: registered new interface driver usb-storage
Feb 27 23:54:23 hostname kernel: [  483.146849] usbcore: registered new interface driver uas
Feb 27 23:54:24 hostname kernel: [  484.067523] scsi 4:0:0:0: Direct-Access     BUFFALO  External HDD     0000 PQ: 0 ANSI: 3
Feb 27 23:54:24 hostname kernel: [  484.067952] sd 4:0:0:0: Attached scsi generic sg2 type 0
Feb 27 23:54:55 hostname kernel: [  514.996705] usb 2-2: reset high-speed USB device number 4 using xhci_hcd
Feb 27 23:55:26 hostname kernel: [  546.102442] usb 2-2: reset high-speed USB device number 4 using xhci_hcd
Feb 27 23:55:57 hostname kernel: [  577.080222] usb 2-2: reset high-speed USB device number 4 using xhci_hcd
Feb 27 23:56:28 hostname kernel: [  608.058034] usb 2-2: reset high-speed USB device number 4 using xhci_hcd
Feb 27 23:56:28 hostname kernel: [  608.187252] sd 4:0:0:0: [sdb] Read Capacity(10) failed: Result: hostbyte=DID_TIME_OUT driverbyte=DRIVER_OK
Feb 27 23:56:28 hostname kernel: [  608.187256] sd 4:0:0:0: [sdb] Sense not available.
Feb 27 23:56:59 hostname kernel: [  639.100029] usb 2-2: reset high-speed USB device number 4 using xhci_hcd
Feb 27 23:56:59 hostname kernel: [  639.229451] sd 4:0:0:0: [sdb] Write Protect is off
Feb 27 23:56:59 hostname kernel: [  639.229457] sd 4:0:0:0: [sdb] Mode Sense: 00 00 00 00
Feb 27 23:57:30 hostname kernel: [  670.081799] usb 2-2: reset high-speed USB device number 4 using xhci_hcd
Feb 27 23:57:30 hostname kernel: [  670.211139] sd 4:0:0:0: [sdb] Asking for cache data failed
Feb 27 23:57:30 hostname kernel: [  670.211156] sd 4:0:0:0: [sdb] Assuming drive cache: write through
Feb 27 23:58:01 hostname kernel: [  701.119601] usb 2-2: reset high-speed USB device number 4 using xhci_hcd
Feb 27 23:58:32 hostname kernel: [  732.097607] usb 2-2: reset high-speed USB device number 4 using xhci_hcd
Feb 27 23:59:03 hostname kernel: [  763.075505] usb 2-2: reset high-speed USB device number 4 using xhci_hcd
Feb 27 23:59:34 hostname kernel: [  794.053339] usb 2-2: reset high-speed USB device number 4 using xhci_hcd
Feb 28 00:00:05 hostname kernel: [  825.031343] usb 2-2: reset high-speed USB device number 4 using xhci_hcd
Feb 28 00:00:36 hostname kernel: [  856.137236] usb 2-2: reset high-speed USB device number 4 using xhci_hcd
Feb 28 00:00:36 hostname kernel: [  856.266439] sd 4:0:0:0: [sdb] Read Capacity(10) failed: Result: hostbyte=DID_TIME_OUT driverbyte=DRIVER_OK
Feb 28 00:00:36 hostname kernel: [  856.266447] sd 4:0:0:0: [sdb] Sense not available.
Feb 28 00:01:07 hostname kernel: [  887.115166] usb 2-2: reset high-speed USB device number 4 using xhci_hcd
Feb 28 00:01:38 hostname kernel: [  918.093122] usb 2-2: reset high-speed USB device number 4 using xhci_hcd
Feb 28 00:02:09 hostname kernel: [  949.071135] usb 2-2: reset high-speed USB device number 4 using xhci_hcd
Feb 28 00:02:40 hostname kernel: [  980.113167] usb 2-2: reset high-speed USB device number 4 using xhci_hcd
Feb 28 00:02:40 hostname kernel: [  980.242580] sd 4:0:0:0: [sdb] Attached SCSI disk
Feb 28 00:03:11 hostname kernel: [ 1011.155150] usb 2-2: reset high-speed USB device number 4 using xhci_hcd
Feb 28 00:03:42 hostname kernel: [ 1042.133159] usb 2-2: reset high-speed USB device number 4 using xhci_hcd
Feb 28 00:04:13 hostname kernel: [ 1073.111069] usb 2-2: reset high-speed USB device number 4 using xhci_hcd
Feb 28 00:04:44 hostname kernel: [ 1104.089034] usb 2-2: reset high-speed USB device number 4 using xhci_hcd
Feb 28 00:05:15 hostname kernel: [ 1135.071017] usb 2-2: reset high-speed USB device number 4 using xhci_hcd
Feb 28 00:05:46 hostname kernel: [ 1166.108960] usb 2-2: reset high-speed USB device number 4 using xhci_hcd
Feb 28 00:06:17 hostname kernel: [ 1197.151034] usb 2-2: reset high-speed USB device number 4 using xhci_hcd

取り出してあったラズパイのログを見てみると、/var/log/kern.log(kern.log.4.gz)に1月下旬からエラーが出ていた。

Jan 23 07:42:11 hostname kernel: [6895164.358213] sd 1:0:0:0: rejecting I/O to offline device
Jan 23 07:42:11 hostname kernel: [6895164.358268] sd 1:0:0:0: rejecting I/O to offline device
Jan 23 07:42:11 hostname kernel: [6895164.376947] sd 1:0:0:0: rejecting I/O to offline device
Jan 23 08:48:38 hostname kernel: [6899151.391096] EXT4-fs (sdb1): error count since last fsck: 1815
Jan 23 08:48:38 hostname kernel: [6899151.391146] EXT4-fs (sdb1): initial error at time 1482879680: __ext4_get_inode_loc:3798: inode 129499236: block 517996582
Jan 23 08:48:38 hostname kernel: [6899151.391172] EXT4-fs (sdb1): last error at time 1485039158: ext4_find_entry:1289: inode 128602714
Jan 24 05:00:01 hostname kernel: [6971834.191579] sd 1:0:0:0: rejecting I/O to offline device
Jan 24 07:55:57 hostname kernel: [6982390.836437] sd 1:0:0:0: rejecting I/O to offline device
Jan 24 07:55:57 hostname kernel: [6982390.836522] EXT4-fs warning: 43 callbacks suppressed
Jan 24 07:55:57 hostname kernel: [6982390.836538] EXT4-fs warning (device sdb1): __ext4_read_dirblock:674: error -5 reading directory block (ino 128199902, block 0)
Jan 24 07:55:57 hostname kernel: [6982390.836599] sd 1:0:0:0: rejecting I/O to offline device
Jan 24 07:55:57 hostname kernel: [6982390.836632] EXT4-fs warning (device sdb1): __ext4_read_dirblock:674: error -5 reading directory block (ino 128199902, block 0)

ログローテートされていない最新の/var/log/kern.logは295MBあって(ローテートされた方は300KBもない)、そちらはこんな感じ。

Feb 24 09:45:59 hostname kernel: [9667395.780613] EXT4-fs (sdb1): error count since last fsck: 2139
Feb 24 09:45:59 hostname kernel: [9667395.780665] EXT4-fs (sdb1): initial error at time 1482879680: __ext4_get_inode_loc:3798: inode 129499236: block 517996582
Feb 24 09:45:59 hostname kernel: [9667395.780692] EXT4-fs (sdb1): last error at time 1485815831: ext4_find_entry:1289: inode 128602714
Feb 24 12:07:43 hostname kernel: [9675899.677825] usb 1-1.2: reset high-speed USB device number 4 using dwc_otg
Feb 24 12:09:01 hostname kernel: [9675977.758202] usb 1-1.2: reset high-speed USB device number 4 using dwc_otg
Feb 24 12:10:33 hostname kernel: [9676070.622174] usb 1-1.2: reset high-speed USB device number 4 using dwc_otg
Feb 24 12:11:05 hostname kernel: [9676101.654074] usb 1-1.2: reset high-speed USB device number 4 using dwc_otg
Feb 24 12:11:15 hostname kernel: [9676111.814096] usb 1-1.2: reset high-speed USB device number 4 using dwc_otg
Feb 24 12:11:31 hostname kernel: [9676127.974069] usb 1-1.2: reset high-speed USB device number 4 using dwc_otg
Feb 24 12:11:31 hostname kernel: [9676128.150102] usb 1-1.2: reset high-speed USB device number 4 using dwc_otg
Feb 24 12:11:41 hostname kernel: [9676138.310126] usb 1-1.2: reset high-speed USB device number 4 using dwc_otg
Feb 24 12:11:41 hostname kernel: [9676138.399352] sd 0:0:0:0: Device offlined - not ready after error recovery
Feb 24 12:11:41 hostname kernel: [9676138.399399] sd 0:0:0:0: [sda]  
Feb 24 12:11:41 hostname kernel: [9676138.399412] Result: hostbyte=DID_ABORT driverbyte=DRIVER_OK
Feb 24 12:11:41 hostname kernel: [9676138.399427] sd 0:0:0:0: [sda] CDB: 
Feb 24 12:11:41 hostname kernel: [9676138.399437] Write(16): 8a 00 00 00 00 00 0e 9d f4 e2 00 00 00 08 00 00
Feb 24 12:11:41 hostname kernel: [9676138.399510] blk_update_request: I/O error, dev sda, sector 245232866
Feb 24 12:11:41 hostname kernel: [9676138.399539] EXT4-fs warning (device sda1): ext4_end_bio:317: I/O error -5 writing to inode 7077983 (offset 0 size 4096 starting block 30654109)
Feb 24 12:11:41 hostname kernel: [9676138.399561] Buffer I/O error on device sda1, logical block 30654104
Feb 24 12:11:41 hostname kernel: [9676138.399635] sd 0:0:0:0: rejecting I/O to offline device
Feb 24 12:11:41 hostname kernel: [9676138.399658] sd 0:0:0:0: [sda] killing request
Feb 24 12:11:41 hostname kernel: [9676138.399702] sd 0:0:0:0: [sda]  
Feb 24 12:11:41 hostname kernel: [9676138.399739] Result: hostbyte=DID_NO_CONNECT driverbyte=DRIVER_OK
Feb 24 12:11:41 hostname kernel: [9676138.399755] sd 0:0:0:0: [sda] CDB: 
Feb 24 12:11:41 hostname kernel: [9676138.399763] Write(16): 8a 00 00 00 00 00 ae 84 88 9a 00 00 00 30 00 00
Feb 24 12:11:41 hostname kernel: [9676138.399836] blk_update_request: I/O error, dev sda, sector 2927921306
Feb 24 12:11:41 hostname kernel: [9676138.401773] sd 0:0:0:0: rejecting I/O to offline device
Feb 24 12:11:41 hostname kernel: [9676138.401825] EXT4-fs warning (device sda1): ext4_end_bio:317: I/O error -5 writing to inode 7078075 (offset 0 size 4096 starting block 30654110)
Feb 24 12:11:41 hostname kernel: [9676138.401848] Buffer I/O error on device sda1, logical block 30654105
Feb 24 12:11:41 hostname kernel: [9676138.402111] Aborting journal on device sda1-8.
Feb 24 12:11:41 hostname kernel: [9676138.402166] EXT4-fs error (device sda1) in ext4_reserve_inode_write:4764: Journal has aborted
Feb 24 12:11:41 hostname kernel: [9676138.402233] sd 0:0:0:0: rejecting I/O to offline device
Feb 24 12:11:41 hostname kernel: [9676138.402343] JBD2: Error -5 detected when updating journal superblock for sda1-8.
Feb 24 12:11:41 hostname kernel: [9676138.402376] sd 0:0:0:0: rejecting I/O to offline device
Feb 24 12:11:41 hostname kernel: [9676138.402485] EXT4-fs error (device sda1): mpage_map_and_submit_extent:2129: comm bash: Failed to mark inode 7077983 dirty
Feb 24 12:11:41 hostname kernel: [9676138.402503] EXT4-fs (sda1): previous I/O error to superblock detected
Feb 24 12:11:41 hostname kernel: [9676138.402572] sd 0:0:0:0: rejecting I/O to offline device
Feb 24 12:11:41 hostname kernel: [9676138.402663] EXT4-fs error (device sda1) in ext4_writepages:2420: Journal has aborted
Feb 24 12:11:41 hostname kernel: [9676138.402681] EXT4-fs (sda1): previous I/O error to superblock detected
Feb 24 12:11:41 hostname kernel: [9676138.402736] sd 0:0:0:0: rejecting I/O to offline device
Feb 24 12:11:41 hostname kernel: [9676138.410089] sd 0:0:0:0: rejecting I/O to offline device
Feb 24 12:11:41 hostname kernel: [9676138.410149] EXT4-fs warning (device sda1): ext4_end_bio:317: I/O error -5 writing to inode 7077983 (offset 0 size 4096 starting block 30653956)
Feb 24 12:11:41 hostname kernel: [9676138.410174] Buffer I/O error on device sda1, logical block 30653951
Feb 24 12:12:42 hostname kernel: [9676199.410548] EXT4-fs (sda1): previous I/O error to superblock detected
Feb 24 12:12:42 hostname kernel: [9676199.410719] sd 0:0:0:0: rejecting I/O to offline device
Feb 24 12:12:42 hostname kernel: [9676199.410826] EXT4-fs error (device sda1): ext4_journal_check_start:56: Detected aborted journal
Feb 24 12:12:42 hostname kernel: [9676199.410846] EXT4-fs (sda1): Remounting filesystem read-only
Feb 24 12:12:42 hostname kernel: [9676199.410856] EXT4-fs (sda1): previous I/O error to superblock detected
Feb 24 12:12:42 hostname kernel: [9676199.410890] sd 0:0:0:0: rejecting I/O to offline device
Feb 24 16:28:21 hostname kernel: [9691537.720835] sd 0:0:0:0: rejecting I/O to offline device
Feb 24 16:28:21 hostname kernel: [9691537.720999] EXT4-fs warning (device sda1): __ext4_read_dirblock:884: error -5 reading directory block (ino 7078003, block 114)
Feb 24 16:28:21 hostname kernel: [9691537.721346] sd 0:0:0:0: rejecting I/O to offline device
Feb 24 16:28:21 hostname kernel: [9691537.721443] EXT4-fs warning (device sda1): __ext4_read_dirblock:884: error -5 reading directory block (ino 7078003, block 114)
Feb 24 16:28:21 hostname kernel: [9691537.722797] sd 0:0:0:0: rejecting I/O to offline device
Feb 24 16:28:21 hostname kernel: [9691537.722917] EXT4-fs warning (device sda1): __ext4_read_dirblock:884: error -5 reading directory block (ino 7078003, block 114)
Feb 24 16:28:21 hostname kernel: [9691537.723035] sd 0:0:0:0: rejecting I/O to offline device
Feb 24 16:28:21 hostname kernel: [9691537.723109] EXT4-fs warning (device sda1): __ext4_read_dirblock:884: error -5 reading directory block (ino 7078003, block 114)
Feb 24 16:28:21 hostname kernel: [9691537.723303] sd 0:0:0:0: rejecting I/O to offline device
Feb 24 16:28:21 hostname kernel: [9691537.723381] EXT4-fs warning (device sda1): __ext4_read_dirblock:884: error -5 reading directory block (ino 7078003, block 114)
Feb 24 16:28:21 hostname kernel: [9691537.723476] sd 0:0:0:0: rejecting I/O to offline device
Feb 24 16:28:21 hostname kernel: [9691537.723542] EXT4-fs warning (device sda1): __ext4_read_dirblock:884: error -5 reading directory block (ino 7078003, block 114)
Feb 24 16:28:21 hostname kernel: [9691537.723634] sd 0:0:0:0: rejecting I/O to offline device
Feb 24 16:28:21 hostname kernel: [9691537.723697] EXT4-fs warning (device sda1): __ext4_read_dirblock:884: error -5 reading directory block (ino 7078003, block 114)
Feb 24 16:28:21 hostname kernel: [9691537.723786] sd 0:0:0:0: rejecting I/O to offline device
Feb 24 16:28:21 hostname kernel: [9691537.723850] EXT4-fs warning (device sda1): __ext4_read_dirblock:884: error -5 reading directory block (ino 7078003, block 114)
Feb 24 16:28:21 hostname kernel: [9691537.723941] sd 0:0:0:0: rejecting I/O to offline device
Feb 24 16:28:21 hostname kernel: [9691537.724003] EXT4-fs warning (device sda1): __ext4_read_dirblock:884: error -5 reading directory block (ino 7078003, block 114)
Feb 24 16:28:21 hostname kernel: [9691537.724113] sd 0:0:0:0: rejecting I/O to offline device
Feb 24 16:28:21 hostname kernel: [9691537.724177] EXT4-fs warning (device sda1): __ext4_read_dirblock:884: error -5 reading directory block (ino 7078003, block 114)
Feb 24 16:28:21 hostname kernel: [9691537.724267] sd 0:0:0:0: rejecting I/O to offline device
Feb 24 16:28:21 hostname kernel: [9691537.724399] sd 0:0:0:0: rejecting I/O to offline device
Feb 24 16:28:21 hostname kernel: [9691537.724533] sd 0:0:0:0: rejecting I/O to offline device
...
(以下同じログが延々300万行ほど)
...

認識されてる順番的に、バックアップ側(/dev/sdb1)の方がまず死んで、その後メイン側(/dev/sda1)が死んだという感じでしょうか。

次のステップ

fstabでこれらのディスクを自動的にマウントするように書いてあったので、Raspberry Piが起動途中で止まるのはそのせいかもしれない。後でfstabを書き換えて試してみようと思う。

あと筐体を分解して中のHDDを取り出して直接接続したらデータを読めたという話もあるみたいなので、これも試してみないといけない。

仮にデータを取り出せたとして、今後どうするか。普通にメインとバックアップの2台を用意してrsyncするようにしてただけだと、今回のようにバックアップ側が先に死んだことに気がつけない。死活監視とかちゃんとやらないとなんだろうけど、結局の所「ちゃんと動いてるかどうか」って機械的にスッキリ判定できる物なのかどうかよく分からない。理想を言うと、RAID1みたいなクラスタになってるか1日おきにメインとバックアップが入れ替わるかみたいになってて、普通に使ってて「何かおかしいぞ」とすぐ気付けるようになってるといいのかなと思うんだけど、それはそれで「壊れた方をコピー元にして壊れた物を無事な物に上書きしてしまう」問題がやっぱりあるわけで。

エラー検知までしてくれるようなちゃんとしたNASの製品を買うのが一番なのかな……もういい歳なんだし金で解決できる物は金で解決するのが手っ取り早いよね。

再挑戦

類似モデルの分解事例を見ながら分解してみたところ、中身はSeagateのSATAのディスクでした。

これを手持ちのSATA→USB変換器に繋いでみたところ、1台はウンともスンとも言わなかったのですが、もう1台の方はシーク音らしき音がし始めてkern.logにもディスクとして認識できたっぽいメッセージが出始めたので「やった!」と思った……のもつかの間、

Error mounting /dev/sdb1 at /media/piro/buffalo-external: Command-line `mount -t "ext4" -o "uhelper=udisks2,nodev,nosuid" "/dev/sdb1" "/media/piro/buffalo-external"' exited with non-zero exit status 32: mount: wrong fs type, bad option, bad superblock on /dev/sdb1,
missing codepage or helper program, or other error

In some cases useful info is found in syslog - try
dmesg | tail or so.

てなダイアログが出て、マウントされずにそのまま無反応になってしまいました。認識されかけた時もkern.logにはEXT4-fsのエラーがぽろぽろ出ていたので、ハードウェア障害が根本にあってさっきのが最後の一息みたいなもんだったっぽいです。

もうこりゃ自分の手に負えないわと諦めて、でも思い出の写真とかこの中にしかないデータもあるので、金額次第ですが専門の業者に依頼してみようかと思ってます。「HDD データ復旧 Ext4」とかで検索したら業者がいくつか出てきたので、とりあえず見積もりから……(経費扱いで計上したら控除対象にならんかなこれ)

あ、そんな感じで肝心のHDDはさっぱりだったのですがラズパイの方はあっさり復旧できました。問題の外付けHDDを認識させるための設定を/etc/fstabに直書きしてたのをコメントアウトして、他にも外付けHDDの中のディレクトリへのシンボリックリンクになってた部分を全部普通のディレクトリに改めてみた所、無事Lubuntuのログイン画面まで辿り着けました。でもこれだけ復活しても、記憶を全部なくした抜け殻みたいなもんだからあんまり意味無い……

自分の作品のグッズを作りたいだけの人生だった - Feb 13, 2017

昨年、このツイートを見たんですよ。

それで「うらやましすぎる!!!」とテンション爆上がりになってしまった勢いで妄想グッズの絵を描いてしまったりなんかしまして。

いやね、元々シス管系女子も何かグッズ作りたいなあとは思ってたんですよ。でも、ステッカーとかマグカップとかの比較的すぐ作れそうな物で読者の方に喜んで頂けるイメージがわかなくて。

思えば、以前Mozilla JapanのFirefoxマーケティング活動のお手伝いをしてた頃にストラップや紙袋やフォクすけぬいぐるみのデザインをやらせてもらった時も、やれ「印刷のペラいのじゃなくてちゃんとしたラバーストラップの方が絶対満足感ありますって!!」だの「紐の色と紙袋本体のコントラストが大事なんですよ!!」だの「もっと鼻の所がツンと出てた方がカワイイですって!!」だのと好き勝手駄目出しして、自分が素直に欲しいと思える物を他人のお金で作ってもらうというヤクザなことをしていたのでした(ぬいぐるみは根来さんの駄目出しもすごかったけど)。

でも、Firefoxならロゴマークがかっこいいからそれだけで満足感あるけど、自作の美(少)女イラストとなると、まず美(少)女という時点で照れの方が勝ってしまうし、そもそも自分の絵自体がそんなに上手なわけじゃないし……と色々考えてしまって、何作っても素直に自分で使える気がしなかったのです。

そんな折に見かけたのが冒頭の写真。見た瞬間に「これだ!!!っていうか自分が欲しいわ!!!!」と思いましたね。

だってシス管系女子って一応技術の本で学習(解説)マンガですやん。「これ使って本棚が技術書で埋まるくらいにいっぱい勉強しましょう!」っていうの、シャレが効いててよくないすか? 作品コンセプトにめっちゃマッチしてません? いや「今どき紙の本かよ」って呆れられそうですけども……

あと、仕様上どうしたって単色にならざるを得なくて、でもそれが却ってポップでかっこよくね?とも思いましたし。自分の絵ってそんな上手な方じゃないから丁寧に描いたり塗ったりすればするほどアラが目立って死にたい気持ちが増してくるけど、デザイン的に処理すれば見る側の脳内で勝手に補って見てもらえて実物以上に良く見えそうだし。

そう思ったらもう止まらなくて、妄想絵だけじゃ満足できず、実現できないものかと水面下でなんやかや動いていたのです。日経BPからは予算が出ない自主制作で、完全に趣味の世界です。

そしてついに実現された試作品がこちら!!! (試作1号) まさにイラストの通りの仕上がり!!!! 素晴らしい!!!!

ですがひとつ難点が。

(青色の表紙の本を重ねた様子) 後ろの本の表紙の色で顔色が変わっちゃうんです……

単色の場合はまだマシで、絵が入ってきちゃうと (シス管系女子の本を重ねた様子) もうワケが分からないことに…… これは正直盲点でした。デザイン画の時点で表紙画像と合わせたりすれば一発で分かる事だったのに、それを怠ったばかりに、試作品で実物を見るまでこの問題に全く気付いてなかったという。

なので泣く泣く一からやり直しました。 (試作2号) 明暗反転版です。 といっても、単純に図案の明暗を反転するだけだとパーツが宙に浮いてしまう箇所が結構ありました。そこで、妄想イラストの単純化された図案から「元絵」にあたる線画を起こして、そこから改めて各所の線を拾い上げる形で図案化するという事をした結果がこれです。

これなら、多少うるさい内容の表紙と合わせても顔がちゃんと判別できます(真っ白の紙でも、本の表紙に対してブックエンド自体の影が落ちるので)。 (試作2号をシス管系女子の本と重ねた様子)

しかも、タイツの部分を抜いたので、本の表紙の色や柄がそのままみんとちゃんのタイツの色や柄になります。つまりタイツの履き替え遊びができます。試しに手元の本をいくつか合わせてみました。 (Webの創世) 濃い色はもちろん合いますし…… (わかばちゃん本) 文字が入っててもへっちゃら。 (シェルプログラミング実用テクニック) 帯部分だけ色が違うのも良いですね。 (Firefox Hacks Rebooted) オライリー柄とか。 (ヒューメイン・インタフェース) 英字柄とか。 (徳丸本) 淡い色もいいですね。

試作2号で勝利が見えたので、これをベースに微修正した物を最小ロットで量産して、シス管系女子Advent Calendar 2016にご参加頂いた皆様にお礼として贈答した残りを4月9日の技術書典2に持っていこうと思ってます。利益をほとんど載せない状態でも数千円にはなってしまいます(少数生産だとどうしても割高になってしまう……かといって個人で何百個もこんなかさばる物を発注しても手に負えませんし)が、もし良かったら手に取ってみて頂ければと思います。

以下、デザインするときに分かったこととかコツとかをメモしておきます。

基本的には切り絵の要領なんですが、切り絵の中でも全パーツが繋がってるタイプの形になってないといけないというのがポイントです。

試作1号の図案の時は顔の肌部分を抜いて輪郭を残すデザインにしようとしたのですが、そうすると普通に絵を描くと鼻と口がどうしても輪郭に繋がらないパーツになってしまいます。なので、顔の角度やポーズを工夫してそれらのパーツに髪や目や膝が接するようにすることで、どうにか浮かない形でパーツを残すことができました。

明暗反転版では鼻や口のように短い線は逆に穴を開けるだけなので簡単だったのですが、顔全体を残して輪郭を穴にする場合、輪郭を全部繋げないでちょっとだけ橋渡しする部分を残してやらないといけません。あまり橋をかけすぎると見た目が悪いと思って、目立たない所(普通に線画を描く時に輪郭を途切れさせるような所)に絞って橋をかけてみました。ただ、試作2号では数を絞りすぎて頭の曲線部分が完全に枠から切り離されてしまい、枠が歪むと頭の方が飛び出てしまうようだったので、強度を増すために量産版では頭と枠の間に2箇所橋を増やそうと思ってます。

ということで、ご報告という体裁でのただの見せびらかし記事でした。

雑に描くイラスト100枚チャレンジの振り返り - Dec 25, 2016

2016年4月2日でに始めて、途中からペースダウンしつつも同年12月25日に100枚を達成したので、これを一区切りとして振り返ってみます。

なぜ始めたの?

背景事情の要点を整理すると、

  • シス管系女子はWebにおいてのオフィシャルな広報が出版社サイドからほとんどなされておらず、日経Linux既存読者層以外にほとんどリーチできていない。このままでは本誌ともどもジリ貧。
  • コンテンツ先見せがデフォのWebでは、勿体ぶり型モデルでは埋没してしまう。既にブランド価値を築けているプレイヤーならそれでも成り立つが、ブランド価値が無い・これから築いていくしかないプレイヤーが同じ事をしても話にならない。
  • かといって既存の絵や原稿を大々的に公開すると日経BPに怒られる。出せるコンテンツの絶対数が足りない。無い物はフォロワーの方々にも拡散してもらいようがない。
  • 他のプレイヤーは既にコンテンツを出していて、拡散を担う受け手もそれを材料に共有する事でもう手が塞がっている。自分でコンテンツを作ってくれるほどの熱量の高いファンにだけ期待して、自分で何も出さないのでは、土俵にすら上がれない。
  • 出せるコンテンツが無いなら作るしかない。自分で手を動かさない無名の人に、人はついて来ない。

そんな感じで、現状分析も甘ければ効果測定の方法も定義できないまま、とにかく数を増やさねばという焦燥感にのみ駆られて始めたのでした。

失敗だった点

広報的な成果は、期待したほどには無かったように思います。

タイムラインによく艦これ等のラフなイラストが流れてきていたので勘違いしてしまっていましたが、あれは作品のファン同士のコミュニケーション、あるいは神絵師とそのフォロワーの方々のコミュニケーションという文脈のものであって、すでにある巨大なコミュニティからこぼれ落ちる雫のようなものなのですよね。焦りと「これくらいならやれるかも」という妥協とで完全に見誤ってしまっていました。

それに、シス管系女子というコンテンツの最も大事な価値は解説する事にこそあるわけで、日常の一コマを切り取ってもプレビューになりません。宣伝としてやるなら1枚絵ではなく、本編同様の解説絵にするべきだったのだと今となっては思います。1枚絵で喜んで頂けるのは既存のファンの方だけですし、落描きレベルの雑なイラストではそれすらも……

得られた物

自分の中で絵を描く事の心理的ハードルが下がったのは、収穫と言えると思います。学生時代にはノートの隅に落描きをしていましたが、社会人になってからはその機会がなくなり、なにか特別な理由がないと絵を描かないようになってしまっていました。1日に1時間だけでも無理やりにでも絵を描く時間を設けたことで、季節のイラストもそれほど気負わず描くことができました。

副次的な効果として、絵描きの方との交流のきっかけになったのも良かったです。絵を描く事のハードルが下がったことで、他の人へのお祝いイラストや、オリジナルキャラの絵等を描きやすくなりました。億劫がっていたら、気に入った絵の描き手の方にご挨拶すらできないままだったでしょう。

また、なるべく手をかけずに見栄えのする絵を描くノウハウが多少は身に付いたとも思っています。例えば、自分の場合はペン入れ工程が最も時間を食うため、それをスキップするやり方を取るようになりました。また、背景や小物などについて、今まではいつも律儀に線画を仕上げてから塗っていたのを、いきなり塗りから始めるというやり方を取れるようになりました。夏に描いたヒマワリや七夕の笹、仙台七夕の飾り等は、このやり方でなければもっと時間がかかっていたでしょう。

まとめ

総評としては、よく頑張ったけどピントがずれてたね、というあたりでしょうか。

来年以降は、無理の無い範囲までペースを落としつつも、当初の目的である広報に繋がるように、解説を主にしたイラストの割合を増やしていきたいです。

2016年のアドベントカレンダーのふり返り - Dec 25, 2016

今までアドベントカレンダーには熱心に参加した事はなかったと思うのですが、今年はシス管系女子の草の根広報活動の一環として、自分でもびっくりするくらい力を注いでおりました。

シス管系女子アドベントカレンダー

まず、自分で初めてシス管系女子 Advent Calendar 2016というアドベントカレンダーを立てました。IT系のクリスマスといえばアドベントカレンダーが定番という印象があったので、思いつきでのチャレンジです。

とはいえ、現状のWebでの認知度を鑑みるに読者の方のご協力だけで全日程はまず埋まらないだろうと見込んでいたため、全25コマの構成で事前にマンガを用意しておき、最悪の場合でも1日1コマ公開していけば日程は埋められるという準備を整えた上でスタートしました。Webでの試し読み代わりに自由に使える話を増やしたいという動機が元々あったので、「描くつもりだった話を描ければそれでまず成功。アドベントカレンダー関係のブームに乗っかって露出が増えれば一石二鳥。読者の方のご協力を得られれば一石三鳥」というつもりでした。

蓋を開けてみると、およそ3割の日程を読者の方に寄稿して頂けており、予想以上の結果に大変嬉しい思いをしております。自分なんかの思いつきに乗っかってくれた方がこんなにいた事、エールを頂けた事がとても励みになりました。本当にありがとうございます。

一方で、課題も色々あったと思っています。

  • コラボ対象としてのシス管系女子のコンテンツ力不足。例えば湊川さんに寄稿頂いた記事は、大変な労作なのにも関わらず、ブクマ数は1桁台に留まってしまいました。ご恩に見合うメリットを提供できていないというのは心苦しい所です。
  • アドベントカレンダーは大人数が参加するからこそ盛り上がるという事。世間には最初から最後まで一人だけで完遂しているITアドベントカレンダーもありますが、テーマ自体が魅力的であるとか話題性があるとかでも無い限り、ネットの片隅でひっそり始まってひっそり終わるだけになってしまいます。
  • ファン向けコンテンツとして見た時の魅力の見えにくさ。ConoHaアドベントカレンダーでは参加者にカレンダーをプレゼントという事がアナウンスされていましたが、シス管系女子アドベントカレンダーでは具体的にどんなプレゼントがあるという事をアナウンスできていませんでした(現時点でも。これはグッズがまだ準備できていないせい)。また、こちらで投稿していく内容が本編でスキップした初歩的な部分についての物であったことから、多くの既存読者の方にとっては「知っている事」に過ぎず、連載を追う楽しみを感じられにくかったのではないかと思います。

以上を総合すると、今回誰が一番得をしたかというと自分自身だった(自由に使える特別編を1つ増やす契機になった、読者の方々からのエールを頂けて元気が出た)という気がします。つまり俺得。自己満足に付き合わせるだけになってしまってすみませんでした……こんな風に人の厚意を食んで自分のやる気に変えるような事ばかりしてたら信頼を損なうばかりですよね。ほんとに。

他のアドベントカレンダーへの参加

元々、自分から見つけてきてくれる人だけを対象にしていても認知は広がらないので、どこか「外」に出ていく必要があるという事は認識していました。

そんな折、Geek Women Japan 2016の懇親会で、「シス管系女子」を読まれた方から本編で扱っていなかった話についての質問を頂きました。それに対する回答を記事化して公開するにあたり、ちょうどそのタイミングで各アドベントカレンダーの参加募集が始まっていたため、「外」のアドベントカレンダーに参加すればその読者層に認知を広げる機会になるのでは?と思い至りました。ただ、無差別に参加して宣伝をばらまくだけではただのspam行為なので、

  • 記事の内容はそれぞれきちんとそのカレンダーの趣旨に則ったものになるように務め、その記事単体でちゃんと情報として価値がある物になるようにする。これはspam行為にならないようにという消極的理由よりも、積極的に良いコンテンツにする事で信頼の種を蒔きたいという理由が大きい。
  • その上で、「その記事でやっているような事を自分でやれるようになりたい人向けのコンテンツ」としてシス管系女子を紹介する。
  • そのために、参加するアドベントカレンダーは「シス管系女子」の内容や周辺事情と親和性が高そうな物を選ぶ。

といったあたりの事を考えながらエントリーを増やしていった結果、以下の8記事ができました。

結論としては、この試みは一定の成功を見たと思っています。特にShellscript Advent Calendarに投稿した記事がどういうわけか若干バズってくれて、ここからの流入が突出して多かったです。この記事は日を開けて何度か紹介されていて、その度にアクセスが発生するという状況になっていました。

ただ、このアクセス増は狙って起こせたものではなく(記事のタイトルを付ける際に若干挑発的なタイトルを意識したのは事実ですが……)、それ以外の記事のPVはそれほど伸びなかった事、また増加したアクセスも継続的なものではなくあくまでスパイク状の一過性の増加に留まった事から、やるならもっと継続的にやった方が良さそうという事は思っています。そうする事で、「シス管系女子」の内容や技術レベルに対する懐疑的な見方を払拭する材料を増やせれば、という思いもあります。

自分のサイト内でコンテンツを公開するよりも、技術情報であればQiitaのように、多くの人が見ていて且つ情報をシェアしやすい場所で公開するようにした方が良いという事も実感しました。

Groonga

会社の業務の一環で、Groonga Advent Calendar 2016にもGroonga名義でいくつか記事を投稿しました。

これらはGroongaの知名度向上や盛り上がり感の演出、既存ユーザ・新規ユーザ向けの情報の整備を目的に行いましたが、シス管系女子の場合と同様、これ自体が知名度向上に役立ったという事は言えなさそうです。

まとめ

以上をまとめると、認知度向上のための手段としてアドベントカレンダーを使う時は、既に人が多く集まっている場所(サイトもそうだし、アドベントカレンダーもそう)に飛び込んでいくのが有効なようです。 という、当たり前といえば実に当たり前の話なのでした。

GitHubが落ちてても慌てずに済む、SSHとGitだけでのPush/Pull - Dec 24, 2016

このエントリはGit Advent Calendarとのクロスポストです。(→Qiitaの方の投稿

今日の記事では、「SSHって何?」や「SSHは知ってるし時々使うけど、普段そんなに使う機会は無い」くらいのレベルの方を対象に、SSHとGitの組み合わせだけでこんな事もできるんですよ!という事をご紹介します。アドベントカレンダー的には最後の方の日なのに初心者向けの話で恐縮ですが、まあせっかくなのでおつきあい下さい。

続きを表示する ...

Page 1/238: 1 2 3 4 5 6 7 8 9 »

Powered by blosxom 2.0 + starter kit
Home

カテゴリ一覧

過去の記事

1999.2~2005.8

最近のつぶやき

オススメ

Mozilla Firefox ブラウザ無料ダウンロード