Home > Latest topics

Latest topics 近況報告

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

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

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

Page 12/248: « 8 9 10 11 12 13 14 15 16 »

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の組み合わせだけでこんな事もできるんですよ!という事をご紹介します。アドベントカレンダー的には最後の方の日なのに初心者向けの話で恐縮ですが、まあせっかくなのでおつきあい下さい。

続きを表示する ...

シェルスクリプトの中でjoin()とsplit()相当の事をやる - Dec 15, 2016

このエントリは以下のエントリのフォローアップです。

(また、Qiitaにもクロスポストしています。)

結論を先に書くと、シェルスクリプトの中で普通のプログラミング言語で文字列を区切り文字で分割して配列にする操作、いわゆるsplit()相当の事はtr '区切り文字' '\n'でできます。その逆の、配列を結合して1つの文字列にする操作、いわゆるjoin()相当の事はpaste -s -d '区切り文字' -と覚えておくのが筆者的にはオススメです

(ちなみに、GNU coreutilsのコマンドでjoinという物がありますが、これは配列のjoin()ではなく、SQLで言うところのINNER JOINとかOUTER JOINとかの方の文脈の「join」に対応する物です。この記事の話とは関係ないので、忘れて下さい。)

以下、どういう場合にそれが言えるのかという事と、その理由の解説です。

続きを表示する ...

絵文字✨とTwitter Bot🤖と私👤とEmoji Editor📝 - Dec 15, 2016

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

この記事では、自分が絵文字込みのテキストを楽に編集するために作ったEmoji Editorという簡単なツールを紹介します。

PCでパレットから絵文字を入力したいんです……😭

唐突ですが皆さん、どうやって絵文字を入力されてますか?

Android版のGoogle日本語のように絵文字のパレットが付いていたり、macOSの日本語入力のように標準辞書に入っていて普通に「すし」と入れて変換すれば「🍣」になったりする環境もあるようなのですが、自分が主に使っているWindows 7+ATOK2016の環境とUbuntu 16.04LTS+ATOK X3の環境では良い方法がなさげです。自分は絵文字はパレットから選択したい派、というか顔文字に対応する読みをいちいち覚えてられない派なので、やるなら一覧の中からぽちぽちクリックして選びたいです。しかしATOKに付属の文字パレットというユーティリティの分類には「顔」みたいなグループ分けが無いため、Unicode絵文字を使おうと思うとUnicodeの表のあっちこっちを行き来しながら探さないといけません。(最新のATOK2017ではこの辺どうなんでしょう?)

また、これはカラー絵文字のフォントが無いという古い環境だからのようですが、文字パレット上だけでなくテキストエディタ上でも絵文字は白黒表示です。自分が絵文字を使いたいのは主にTwitterでの投稿なので、見るならTwitter上でどう見えるかが分からないと安心して使えません。

元々絵文字を使う習慣が無かった自分が絵文字に触れる機会が増えたのは、「シス管系女子」の広報用Twitterアカウント(みんとちゃんbot)がきっかけです。いつもの自分のノリでやると堅すぎると思ったので、みんとちゃんbotの発言についてはあえて絵文字を入れて柔らかい印象を持ってもらえるようにしたい、という下心ドリブンです。

当初はAndroidのGoogle日本語入力から絵文字を入れていましたが、運用のためのbotスクリプトを作成して、発言データにするためのテキストを大量に用意する段階になってPC上で作業に入ろうとしたときに、前述のような状況に気が付きました。

そこでとりあえずTwitter絵文字が使えるパレットを探してみた所、テスト用にちょっと投稿するだけならTwitterの絵文字をデスクトップPCから使おう! Ver2というサービスで絵文字の入力自体はできる事が分かりました。が、このページは既に作成済みのデータの編集には使えません。やはりここは、Twitter絵文字を含んだプレーンテキストをWYSIWYGで編集できるツールが欲しくなるところです。

探し方が悪かったのかもしれませんが、自分はその時「まさにこれだ!」と思える物を見つけられなかったため、無いなら作るか……という事で作りました。その名もEmoji Editor(名前が安直すぎる)。HTMLファイル1つだけで完結する、フレームワークも何も使ってないSPAとも呼べないような代物です。 (Emoji Editorの画面)

このエントリの公開を機に、GitHubにリポジトリを作りましたので、forkしたりプルリクしたりして頂ければ幸いです。

続きを表示する ...

女性エンジニアが自分の好きなように生きられるように、あるいはただの惚気話 - Dec 08, 2016

妻・夫を愛してるITエンジニア Advent Calendar 2016その2をご覧の皆様、はじめまして。Piroといいます。

自分は現職のITエンジニアですが、会社の仕事とは別に、日経LinuxというLinux関係の技術情報の月刊誌でシス管系女子と題した漫画形式の記事を連載しています。
まんがでわかるLinux シス管系女子(1巻表紙)

基本的に話も技術面での検証も作画も全部一人でやっているのですが、そのせいで内容を冷静な目で見られなくなってしまうことがあります。そんな時、幸いなことに妻も現職のITエンジニアなので、「話の展開に無理が無いか?」とか「この解説で通じるか?」とか不安を覚えると度々、妻に下描きを見せて率直な感想を聞かせてもらっています。率直に言って大変助かっています。 だというのに、最近は連載の〆切に追われて平日も休日も一杯一杯になってしまっている事が多く、妻には迷惑をかけてばかりで申し訳ない限りです。

さて。「シス管系女子」はとある企業の新米システム管理者の女の子「みんとちゃん」が主人公なのですが、今日の話はみんとちゃんの衣装の話です。

自慢じゃないですが、自分はファッションセンスがありません。そんな自分が女性キャラの衣装を自分でゼロから考えても碌な事にならない、しかし巷のファッション誌を参考にしようにも色々ありすぎてどんな服を着せればいいか分からない、ということでみんとちゃんの衣装はもっぱら妻の私服を参考にさせてもらっています。

<script src="http://source.pixiv.net/source/embed.js" data-id="58957204_7f2f3332231d9e925534c71343fcecc9" data-size="medium" data-border="on" charset="utf-8"></script><noscript>

シス管系女子 利奈みんと 作画設定(2016年版) by Piro/結城洋志 on pixiv

</noscript>

(そんな訳なので、たまに読者の方に「みんとちゃんの服装かわいいですね!」と言っていただけると、「でしょ!?かわいいっすよね!!」と全力で同意して間接的にのろけているのです。)

実をいうとみんとちゃんの衣装には単純にデザインのみならず、ポリシー面についても妻から学んだ事が反映されています。 それは、「男にウケるためでもなければ、周囲に文句を言われないためでもない、自分(みんとちゃん自身)のために自分(みんとちゃん)が好きな服を着る」ということです。

聞いた所によると、妻の最初の就職先は色々とお堅い会社で、服装規定も結構厳しかったそうです。 女性社員は私服であっても地味目のおとなしい服でなければならず、そのため妻も本人の好みに合わない服を買って着ていたそうです。 そんな訳なので、転職して服装既定の厳しくない会社に移ってからはその頃の服はほとんど全くと言っていいほど着なくなってしまい、結局、クローゼットの中を整理した時にそれらはほとんどすべて処分してしまいました。

社会人、特に普通に会社に通勤するという働き方の人にとっては、人生の中でもそれなりの長い時間を会社で過ごすことになります。 そんな長時間を、いたくもない姿の自分でいることを強いられるというのは実に不毛なことです。

せっかくなら、周囲の目を気にして好みに合わない格好を我慢してするのではなく、自分が本心から「可愛い!」とか「格好いい!」とか「楽だ!」とか思えるような格好でいられる方がいいに決まっていますよね。 一度きりの人生なのですから、しなくてもいい無理をせずに自分の生きたいように生きるのに越したことはないです。

実際に作品中で描いてる服のチョイスは妻の好みと僕の好みの論理積なので、どうしても偏りが生じてしまっているのは否めない(実際には妻はジーンズをはいている時も多々あります)のですが、それでも描き手の思いとしては、(ネタのコスプレ以外では)少なくともみんとちゃん本人が着たいと思わないであろう服は着させないようにしたいし、本人が見せたくないであろう物は見せないようにしたいと思っています。

例えば、みんとちゃんは丈の短いワンピースやミニスカートを着ていることが多いですが、下に短パンをはいているので絶対にパンチラしないということにしています。これには「掲載誌がお堅いのでえっちなのはNGだから」という理由もありますが、それより何より、彼女はかわいい服を着たいだけであってパンツを見せたい訳ではないのです。周囲の男性社員に対するサービスでかわいい服を着ているのではなく、彼女自身がその服を好きだから着ているのです。

「シス管系女子」は技術の話の学習漫画ですが、それとは別のささやかな裏テーマとして、みんとちゃんというキャラクターのありようを通して、女性エンジニアが当たり前に自分の生きたいように生きられている様子を描きたいという思いが自分にはあります。

それが妻の仕事環境や生活環境に何か直接影響を及ぼすわけではありませんが、「そういうのが当たり前」という様子を描いて、そういうコンテンツが増えていくことで、妻や後に続く人達が少しでも生きやすい世の中になってくれれば。 そう思って自分は今月号も、誰に頼まれたわけでもないのにみんとちゃんに先月号とは違う衣装を着せている次第です。 (そして絵を使い回せなくなり、また自分で自分の首を絞めているのでした……)

以上、飛び入りで妻・夫を愛してるITエンジニア Advent Calendar 2016その2の8日目に参加してみました。 明日の記事もお楽しみに!

Page 12/248: « 8 9 10 11 12 13 14 15 16 »

Powered by blosxom 2.0 + starter kit
Home

カテゴリ一覧

過去の記事

1999.2~2005.8

最近のコメント

最近のつぶやき