文系プログラマはそんなところで挫折しない

原文に言及

Paizaのプログラミングの勉強を始めたときに、文系が挫折しやすい7つのポイントという記事に猛烈な違和感があり、ついでなので巷に溢れる理系だ文系だ文系プログラマだとかいう風説を否定していこうと思う。

既にTwitterで言及されたりしているのだが、明らかに「プログラミングの勉強を始めたときに、文系が挫折しやすい7つのポイント」ではなく、「プログラミングの勉強を始めたときに、文系の私が挫折した7つのポイント」だ。

予め言っておこう。私は文系である。脳はバリバリの理系思考だが、興味分野は文系であり、教科としての得意分野も文系科目なので、世の中的には文系ということになる。だが、私は2歳の時にはプログラミングをやっていたし、幼少期は際立って天才的だったのだが、それでも言語発達よりもプログラミング習得のほうが早かった。ひらがなをマスターしたのは4歳になってからなので、ひらがなよりもアルファベットが先にあった。

Hello, World!

別にそのようなことは微塵も思わないし、それは文系か理系かということとは全く別次元のところにある。

簡単にいえば、「テレビが大好きな人はプログラミングに向かない」のだ。

テレビを視聴している時、脳は停止している、ということが既に研究により判明している。だが、おそらくこれは真実ではない。私はテレビを観ていようが、非常に活発に様々なことを考えている。そして、その速度に追いつかないことや内容が稚拙なこと、私の疑問に応えないことにいつも苛立っている。だから、私はTVが大嫌いだ。

対して、ゲームが好きな人は向いている。ドライブなら助手席が好きな人より運転席が好きな人が向いているし、絵を見るのが好きな人よりも絵を描く人が向いているし、ラジコンが好きな人はかなり向いている。

要は自分が支配し、操縦することが好きな人は向いていて、受け身な人は向いていない。

さらにいえば、好奇心の多寡が重大な違いにもなる。

この節において、この人は単に受動的すぎて、しかも好奇心によって駆動してもいない、テレビが好きなタイプであるために受け入れられなかった、というだけだろう。

だいたい、そもそもこれを「暗記する」と考える時点で向いていない。「どうなっているんだろう」と思ったあなたは向いている。

#include <stdio.h>

int main(void)
  {
      printf("Hello, world!");
      return 0;
  }

おまじない

これは、よく使われるが、よく知られた悪しきものである。
単にきちんと説明する力がないのだろう。理解させる必要がないのかもしれないが。

C言語の#includeは、含める、そのまま「取り込み」に用いるものだ。stdioはSTanDard Input/Outputであり、読み込んだり、書き出したりする機能を持っている。
この機能を利用するために

#include <stdio.h>

と書いておく。

C言語の基本部分はただの骨組みだ。機能は別パーツになっている。これを取り付けるのがこの作業だ。

「何でも入る型ひとつあればいいのでは」は、疑問としては正しいし、意見としても構わない。汎用型を持っているものもあるし、Perlは静的型の言語だが、スカラー変数にはスカラー値であればなんでも入る。

これは思想的な問題である。C言語がそうなっているだけだ。
もちろん、メリットはある。気になるならそれを調べればいいし、そんな簡単に思いつくことは誰かが試している可能性が高いのだから、それを調べてもいい。

だが、躓きはしないだろう。学習を困難にさせる部分であり、なるべく型を意識させない言語というのは、私がプログラミングを教える上で重要な条件にはなっているが、別にそれは文系だからという問題でもない。

なお、なぜ型があるか?という質問に対する応えは明瞭だ。

トマトは冷蔵庫にいれなくてはいけないが、猫を冷蔵庫にいれるわけにはいかない。別な扱いが必要だと区別する必要があるだろう?

セミコロン

これも、行の切れ目が文の切れ目ということでいいじゃないか、はそのような思想の言語はたくさんある。

Perlは常にセミコロンで終端する。各種UNIXシェルは通常、行末を文ターミネータとして扱う。

RubyやJavaScriptは、「文脈的に次が続きであれば続き、そうでないければ終端とみなす」という扱いだ。

どれも一長一短だ。Perlの統一はわかりやすいが、同時に忘れやすい。

my $a = "Something";
print $a;

シェルの分割されるのは思わぬトラブルの元だ。

text="Hello,
This is Aki!"

これはエラーになる。でもZshではエラーにならない。

Rubyであればこういうこともできる。

array = [ "a", "b",
          "c", "d"]

JavaScriptも同じようにできるが、シェルで分割される文字列の改行は許されない。

よほど変なルールならともかく、C言語の場合はだいたい「セミコロンをつける」で統一されている。
それを「つまずく」と言っているのは、もはや言いがかりをつけたい気持ちであるだけなのではないか。

ループ

ループが面倒だというのはあって、そのあたりRubyが素晴らしい処理を行っている。JavaScriptライブラリもそれにならったものが多い。

「何を意味しているんだ」というのをつまずくと言ってしまうのならば、それは学習意欲の欠如を恥じるべきだと思うけれども。

3 part for loopは結構わかりにくいので、工夫が必要だ。

for (a = 0; a < 11; a++) {

11回ループなのだからそれほどわかりにくいこともないと思うが、別に1 originにしたいのであれば

for (a = 1; a <= 11; a++) {

とか至って構わないわけだ。もちろん、これはわかりづらいので、Rubyはこのループを排除している。

11.times do |a|

だが、この3 part forが欲しいケースは、全然違うところであったりする。まつもとさんが大嫌いなので、Rubyに入ることは絶対にないが。

for (<>; ! ~ /^END$/; <>) {

Perlになれている人以外はまるでわからないと思うが、これは、「各行を読み、それが「END」だけの行でないならばループを続ける」ということだ。

この応用として

for ( my @lines; ! ~ /^END$/; <>) { push(@lines, $_) }

というスマートなループが書けたりする。

まぁ、こんなものはお遊びだが、ループする必要性は簡単だ。日常的に、「食べ終わるまでごはんを食べるのとおかずを食べるのを繰り返す」といったループを、送っているはずなのだから。

配列

1クラス40人いたとして、出席簿を40冊用意してそれにタイトルを書きたいか?「出席簿」というタイトルで40人並べるほうが絶対楽だろう?

それだけのことだ。というか、配列なんていくら文系でも数学で習うだろう。

配列でつまずくとかわけがわからないよ。

ポインタ

確かに難易度は高いし、もっとよしなにやってくれと思うところではあるけれども、結局それが参照なのか実体なのかというのは常に問題になるのだ。

だが、これは文系がどうかには全く関係がない。文系だって「Aさんが佐藤だと思っている人物と、Bさんが鈴木だと思っている人物は、同一人物である」みたいな文章が出てくれば理解できるはずなのだから。

まとめに対して

本人も意欲のせいだということを言ってはいるが、そのあとがおかしい。

「なぜなのか?どうなっているのか?なんのために?どういう意味?」それを気にするのは理系の傾向であって文系の傾向ではないし(理系は証明する学問だ)、非常に重要な資質なのだ。

「そういうものだからで済ませる」というのは、明らかに向いていない人だ

それは、理系だの文系だのという話ではなく、理知に対する姿勢だと思う。どうでもいいことばかりを考えて難癖つけたがるというのは、もっと単純に「意欲がないし、取り組む姿勢もない」というだけの話で、文系に対する風評被害を生じさせるのはやめていただきたい。

実際文系プログラマってどうなの

プログラミングなんて9割方文系領域なので、別に何も問題ない。

クリエイティブであるはずの料理を単純作業にできるように、世の中ではプログラマの仕事が単純作業だったりもするわけで、そこはスキルなんてほぼ不要だったりもする。やる気なんかなくても覚えたことを繰り返すだけでいいという世界もある。

優れたスキルが欲しいのだとしても、別に文系だから不利ということは特にない。理知的であれば良いのだ。

むしろ、私が持っている「記憶力」というハンデのほうがはるかに大きい。

様々なブラウザと様々なプロファイルを切り替えて使うスクリプト

基本解説

GitHub

これまでFirefox Latestスクリプトを使ってきたが、これではFirefoxしか利用できなかったり、異常終了やフリーズすると問題が生じる可能性があった。

Firefox Latestの仕組みは次のようなものだ。

Gist

要はシンボリックリンクで.mozillaを切り替えて起動→終了したらシンボリックリンク修正、をしているだけだ。

ちなみに、もともとはMageiaがFirefoxをLTS版を採用していて、それが気に入らなかったのでこのようにしている。
だが、Manjaroはその必要はないので、Firefoxだけでなく、任意のブラウザを任意のプロファイルで起動できるようなスクリプトを書いた。

多くの関数が定義されているが、まずはプロファイル切り替えの方法をまとめる必要があった。

Chromium系は--user-data-dirオプションを使用する。

これに該当するのは

  • Google Chrome
  • Chromium
  • Opera (Blink系)
  • Vivaldi
  • SRWare Iron
  • Maxthon

Maxthon for LinuxはあまりにもOUT OF DATEが過ぎるが。

Firefox系は-P profile-profile pathのふたつ。設定全体を切り替えることはできず、プロファイルのみの保存切り替えだが、問題はあまりなさそうだった。これに該当するのは

  • Firefox
  • SeaMonkey
  • Palemoon

それ以外

  • Midoriは-c directory
  • qupzillaは-p directory
  • Rekonqは--config directory

この5タイプに対応した上で、それぞれのブラウザに対応した関数を用意し、簡単に起動できるようにしている。

例えば

mybrowsers[shopping]="chr $HOME/.browser-settings/chromium/shopping"

とした上で、

$ mybrowser.zsh shopping

とすれば、$HOME/.browser-settings/chromium/shoppingを設定ディレクトリとしてChromiumが起動することになる。

Zsh Assoc

今回のスクリプトではZshの連想配列を2つ使用している。

ひとつは、ブラウザの起動設定を行うためのものだ。これは

eval "$mybrowsers[$1]"

という部分で作用している。

もうひとつは、ブラウザのコマンドを修正するものだ。Chromeはgoogle-chromeだったりするかと思うが、Archではgoogle-chrome-stableだったりもする。

そこでこれを修正するため、ブラウザの各コマンドは"${${modify_browsers[$browser]}:-$browser}"とすることで修正可能にした。

Windows 10について

悪名高いWindows 10をある程度使ってみている。
とりあえず、それで思ったことをまとめてみよう。

OSとしての進化

一般の人の中にはOSの進化を頑なに認めない人がいるが、元々Windowsが(それ以前にMS-DOSが)ひどい代物である以上、修正すべき点は多い。低い生産性は人類の進歩を妨げさえする。

だから、Windowsはどんどん改善してもらわないと困る。

純粋にOSとしての点をみれば、それが正しいかどうかはともかく、かなり良くなっている。
「ここはこれがおかしくて、こうあるべきだからこうしてほしい」という点は山ほどあるのがWindowsだが、その望まれているあるべき解決方法ではなく、なぜか全然違う方法で提供して、「なんでそうなった!」と突っ込まれるのは恒例なのだが、今回もそのような変更点は非常に多い。

いつまで経ってもfork(2)をサポートしないこととか、まともなシェルを用意せずに、明らかにシェル、端末として使いようのないPowershellを導入したまま放置していることとか。

私はWindowsアプリは書かないし(書くとしてもクロスプラットフォームレイヤ上で)、コアな開発者でもないため感じることは少ないが、「クロスプラットフォームレイヤのWindows環境下での制約」という形で困る。

そういう内部的なことは置いておくとしても、Windows7以降で改善された点として

  • ISOファイルのマウントが可能になった(Win8)
  • 通知機能がまともになり、通知履歴が見られるようになった
  • すべてのUI部品が(一部は仮想的に)スケーラブルになった
  • Win + cursorによるquick tile機能が8方向タイルに対応した(現在位置に基づくタイル。Cinnamonと同じ)
  • IMEスイッチャが搭載された(Windows+Space)

使い勝手

Windows 10の使い勝手そのものは改善しているといっていい。

UIについて考えてみると、基本的な部分はWindows XPの時点で既に完成されていた。Windows 95の時点で基本的な部分はできあがっていたが、見た目がチープであることと、たどりにくい階層構造が難点だった。
Windows XPではテーマ制を導入(Visual Styleの変更はパッチ当てが必要)、簡易なものではあるが、デスクトップ検索も追加している。
また、ウィンドウのタイル表示も可能になった。

Windows Vistaでさらなる改良が行われたが、本質的な進化というよりも、変化であるという面も大きかった。
Windows 7でアイコン式のタスクバーになったが、これも元よりそうであるべきだったというものではない。選択できるなら機能追加だが、選択できないので単なる変更だった。

明らかに改良された点は検索機能が強化され、ポータブルアプリの起動にランチャが不要になったという点だが、スタートメニューの構造やタイルの仕様変更などは、変更でしかないように感じられた。

ちなみに、Windows 7からはWin + Cursorによるquick tileが可能になった。ただし、Windows 7では左右のみだ。

Unix系デスクトップはこの時期、何を取り込んだのか先行したのかわからない機能を数多く導入している。quick tile機能はKDE4, Cinnamon 1.0の導入なので恐らくWindows 7のほうが早い。入れ替え式階層メニューはWindows Vistaが早いが、KDE Plasma 4のものはまた違うし、Cinnamonもさらに違う。また、MacのExpose相当の機能はCompizが入れたため、Windowsよりも早い。

だが、Unixデスクトップで考えても、機能的にはGNOME 2あるいはKDE3で既に完成していた。これらはWindows 95のUIを参考に発展したものであり、その後は細かな機能追加に過ぎないと感じている。

変化を求めている部分もあるし、変化に応じている部分もある。だが、どちらかというと、Microsoftは変な先読みをしている、というよりも自分がスタンダードを作るのだという妙な気負いがある。

それが、Windows 8でのスタートメニューを廃止してModarn UIを導入したことにあらわれているのだろう。奇抜だが、真新しさの演出と、Windowsがやればそれが普通になる、という考え方に基づくのだと思う。

結果として失敗した。実際に問題点が多く、使いにくかったということもあるが。

Windows 10のスタートメニューはその修正だと考えていい。スタートメニューとModarn UIを一体化させたものは、確かにある意味では使いやすいが、目新しいものであるというよりは、Androidスマートフォンで見慣れたものになった。

Windows 8からのフラットUIもより推し進めてはいるが、やはりデザイン的にリッチさに劣るように(個人的には)感じられるし、特に見やすくなったわけでもないため、単なる変更だろう。

機能が一体的に提供されるようになったために、それに従うかどうかだが、「Microsoft製のOS(Windows)で、Microsoftが推奨する音声エージェント(Cortana)を使用し、Microsoftのサーチエンジン(Bing)を介して、Microsoftのブラウザ(Edge)で開く」のであれば使いやすいのだ。

だが、これはAndroid同様のリスキーな面を持っている。

ケータイ化・個人情報商売という悪夢

Windows 10において問題なのは使い勝手ではなく、この2点だ。

Windowsは統合的な環境になった。基本部分ではなく、ユーザーのすべてを「Windowsが」提供しようというのだ。

そこには、メールやスケジュール、あるいはSkypeやTwitter, LINEさえも含まれる。
WindowsストアアプリはWindowsから独立ではなくなった。言い換えると、Windowsストアアプリに提供されている情報はMicrosoftにも与えられる、という形になった。

Windowsに、言い換えればMicrosoftに依存して生活するのであればこれは便利な機能だ。もちろん、Windows Phoneも一緒に。検索エンジンはBingだ。

だが、これはありとあらゆる情報的ライフライン及び情報そのものをMicrosoftに掌握させることを意味する。事実、Windwos 8.1ではデフォルトでオフだった、利用状況や入力内容の送信など、プライバシー上重大な懸念のある機能が、全てオンの状態になるようになった。

しかも、コアな利用状況のレポートは、送信をオフにするとWindowsが動作しないという理由をつけて、必ず送信させる。

ありとあらゆる情報を収集し、個人を監視するような行動は、これまでGoogle及びAppleがとってきたものだ。それと比べるとMicrosoftは穏やかなやり方をしてきた。

だが、今回Microsoftは積極的にプライバシーを手に入れようとしている。Web, Twitter, Facebook, Skype, LINEはもちろん、電話からメールまで全てだ。

それに合わせて規約も、メールの内容を読むというものになった。
また、利用には基本的な部分でMicrosoftアカウントと紐付ける必要がある。完全に個人を特定し、追跡できるようにするものだ。

これらのプライバシーに対する重大な懸念を、私は今回最も問題視する。

また、ケータイの場合はそうした統合的な機能を求める傾向があるのだが、Windows 8で学んだはずの「スマートデバイスとの融合は不自由を生む」ということを無視して、より融合を進める方向になった。

電話だのSNSだのといった機能は、Skypeと連動させることでパソコンでも利用可能なのかもしれないが、明らかにコンピュータの使い方としてそれは重要な部分ではない。それを中心に据えられるのは非常に迷惑だ。

また、パーソなりゼーションを進め、SNSの情報を常に表示し、ニュースを表示し…といった機能は、ビジネスシーンで使われるWindowsということを一切無視しているとしか思えない。

その意味で、ものすごく使いにくくなった。

KDE Plasmaも、KDEアプリケーションを使ってこそという部分はある。KDE PIMとAkonadiだ。
だが、使わないという選択肢はある。その場合、Plasmaを使う魅力は大いに損なわれてしまうのだが、普通のデスクトップ環境としては使うことができる。

今回のWindowsは、その選択権がない。ちなみに、しょっちゅうデフォルトのアプリをMicrosoft製のものに変えられるようになった。ブラウザがEdgeにされるのは日常茶飯事だし、IMEはデフォルトを完全に無視してMS-IMEを選択する。

アカウント

Windows 10ではアカウントが、コンピュータ上のローカルアカウントとMicrosoftアカウントの二種類がある。

ローカルアカウントをMicrosoftアカウントに接続することで、ローカルアカウントとして使いながらMicrosoftアカウントを要求するストアアプリを利用するといったことも可能だ。

だが、Microsoftアカウントでは、ログイン(今回からローカルアカウントでも「サインイン」という表現になった)時にオンライン認証をする。
そのため、インターネットに接続されていなければコンピュータを利用すること自体できなくなった。

デスクトップアプリとストアアプリ

ほぼ全てのプログラムに「デスクトップアプリ」と「ストアアプリ」という区別ができた。

Windows 8にもあった区別ではあるが、ストアアプリを使う機会が設定しかないということもザラにあったため、目立たなかった。

今回、多くの機能がストアアプリに移行したため、この区別が重要な意味をもつようになった。また、ストアアプリがウィンドウ表示できるようになったというのも大きい。

これにおいて重要なのは以下の点だ。

  • ストアアプリは事実上Microsoftが全権を持っている
  • ストアアプリではIMEがストアアプリに対応しているMS-IMEしか使えない
  • ストアアプリはオンラインアカウントとひも付けて実行される

表面から消失した機能

コントロールパネルが隠されてしまっている。
これは検索から起動できる。

これは、ストアアプリの設定を使わせるということなのだろうが、全項目があるわけではなく、重複があったり、片方にしかないものがあったり、表現に整合性がとれていなかったりと非常にわかりにくくなった。

設定に関してはgodmodeも追加された。これは、コントロールパネル的なデスクトップアプリのフラット版だ。

まだ、ログオフがメニューから消失した。
Windows 10はオンラインアカウントでのログインを原則としているため、サインアウトに変更されている。

logoffコマンドが存在するため、logoffで検索すれば抜けられる。あるいは、CAD(Ctrl+Alt+Delete)からでも良い。

総括

OSとしては妥当に進化した。あまりにもスマートフォンを意識しすぎて、デスクトップコンピュータとしての使い勝手が著しく損なわれた程度で、別に悪くなったとは言わない。

全てはログインを「ログオン」という表現から「サインイン」という表現にかえたことにあらわれていると思う。

今やWindowsを使うということは、Microsoftのサービスを利用するということとイコールなのだ。

これまではWindows上で他のサービスを利用することも当たり前だった。だが、これからはそうではない。WindowsはMicrosoftのサービスを使うことを強要する。Appleがそうしたように、ロックインして競合する他のサービスの利用を不能とする可能性もないわけではない。

既に、Windowsを起動すれば当然にGoogle Chromeが起動し、Google日本語入力で文章を打ち…ということはできなくなった。Edgeに変更された設定を戻し、Google日本語入力に切り替えなくてはならない。

また、Windows上で行う作業は当然に秘密が保たれていると信じていただろう。これからはそうではない。Windows上で行う作業は、須くMicrosoftが知りうるものなのだ。

こうしたことをどのように考えるかによって、Windows 10が良いものか悪いものかの判断ができるだろう。

PureBuilder2 TopicPath機能の追加

変更点

最新の変更@Gist

概要

これまでTopicPath機能はPureBuilderで提供されて来なかった。
そのため、個別のドキュメントとテンプレートにおいて実装可能な機能として紹介されてきたが、一般的な要望であるため今回の変更で取り込んだ。

このTopicPath機能は整形されたHTMLを返すわけではなく、シンボルと文字列からなる配列を返す。
PureBuilderの本来の設計に従い、サイトの階層構造に等しいディレクトリ構成を採用し、かつドキュメントごとにTopicPathが固定される状態であれば、かなり楽にTopicPathが生成できる。

これまではTopicPathはドキュメントあたりで設定することが勧められていた。これは、ACCS indexが組み込みで機能するためだ。

この機能を使えば設定ファイルによって、そのディレクトリの親パスを定義し、そして文書タイトルがStringとして追加される。

詳細

つまり、ディレクトリで設定されているのが

[ :Foo, :Bar ]journal/

で、文書タイトルがBazであるならば、

[ :Foo, :Bar, "Baz" ]

となる。

別にシンボルである必要はない。だが、シンボルを推奨している。マップを使用することが推奨されているためだ。
だが、特にシンボルであることを期待しているコードは組み込まれていない。

だから、例えば

[ { Address: "http://example.com/foo/", Title: "Foo" }, "Baz ]

のような構造にしても構わない。

実際にそれを使うコードとしては

% tp = DOC.mktopicpath
% tp.each do |i|
%   if i.kind_of?(String)
        <li><%= i %></li>
%   else
        <li><a href="<%= 	DOC.pbenv[:TopicPathMap][i][1] %>"><%= DOC.pbenv[:TopicPathMap][i][0] %></a></li>
%   end
% end if tp

のようになる。

ただし、Indexの場合はディレクトリで定義されている階層は今いるページなのであるから、その場合はtitleとpathの最終エレメントは重複しているはずだ。

そこで、:Indexが定義されている場合は、最終エレメントを取り除くことにした。

過渡期のコード

タイトルは"title"なのか"Title"なのか:Titleなのか、といったところに揺れがある。

正式には"title"を使用することになっている。だが、おそらくはreservedなキーは大文字で始めることにしたほうがいいだろう。

この互換性を維持するコードにしてある。

また、すでにページあたりでTopicPathを設定している場合に備え、現状はdevelブランチのみの対応だ。


途中で放置したため、書くべきことを忘れました。
ゴメンナサイ。

JP106キーボードでwindowsキーを使う

UnicompキーボードはダイヤテックがJPキーボードを取り扱っているのでJPキーボードはあるのだが、日本語106キーボードであり、Windowsキー、メニューキーがない。

Linux上でこの問題を改善したい。

まず、Cinnamonの場合、キーボード→レイアウト→オプションで左Altと左Super(Windows)を入れ替えるというものがある。これを使えば、右Altがあるため、Superの利用が可能になる。

だが、慣れた位置にAltがないことに戸惑う場合もある。109キーボードの場合、AltキーはSuperキーの右側にあるため、この位置にあり、私はあまり使わない無変換をAltキーにする。

Plasmaの場合はこの法歩うが使えない。AltキーをSuperに当てても、ウィンドウマネージャにおいてはAltキーとして動作するためだ。

そのため、無変換、Pause、ScrollLockのいずれかをSuperキーとして使うのが良いのではないか。

これらの設定をまとめたエイリアスがこちら。

# Xmodmap
alias xmapmhn2alt='xmodmap -e "keycode 102 = Alt_L"'       # Use Muhenkan as Alt key
alias xmapmhn2sup='xmodmap -e "keycode 102 = Super_L"'     # Use Muhenkan as Super
alias xmappus2sup='xmodmap -e "keycode 127 = Super_L"'     # Use Pause as Super
alias xmapscl2sup='xmodmap -e "keycode 78 = Super_L"'      # Use ScrollLock as Super
alias xmappus2hyp='xmodmap -e "keycode 127 = Hyper_L"'     # Use Pause as Hyper
alias xmapscl2hyp='xmodmap -e "keycode 78 = Hyper_L"'      # Use ScrollLock as Hyper

キーコードが不明な場合はxevで取得しよう。

cdrdaoでディスク吸い出し→DAEMON Tools Liteでマウント

Gist

ゲームソフトをいつも手元に置いておくのは邪魔なのだが、ゲームで起動するためにディスクが必要なものがあったりする。

シングルトラックならddなりcatなりで良いのだが、オーディオトラックを含むマルチトラックのものはそうはいかない。

吸い出しはLinuxでして、Windowsで仮想ディスクとしてマウントできるようにしたい。

基本的な方法としては、「DAEMON Toolsでcueファイルをマウント」であるらしい。cueファイルはトラック情報を記載したテキストファイルで、binファイルとセットになっている。

Linuxでのディスククローニングにはcdrdaoを使用する。これは、従来オーディオディスクでも使用している。

だが、cdrdaoはbinファイルとtocファイルを生成する。tocファイルをcueファイルに変換するtoc2cueを使用する必要がある。

だが、それだけではエンディアンに違いにより、DAEMON Toolsでマウントするとオーディオディスクはホワイトノイズのみとなってしまう。この解決のためddのconvオプションを使用する。

さらに、異なるパスに対して吐くと、binファイルのパスが問題になるため、パス記述を書き換えるsedを加えた。

LinuxのDropbox起動問題

問題

  • Linux 4.6rc4 + Cinnamon 3環境下でDelay0で起動されたDropboxがsystrayに入らない
  • Dropboxは自分でStartupを書き換えてしまうため、Delayを変更しておけない

解決方法

Startup Applicationsにcustom commandを選択、コマンドとしてdropboxを指定する。

Linux的にWindowsをバックアップしてみる

ClonezillaやMondo Rescueを使えば一発だし簡単なのだけれど、ここはより手軽で確実な方法を模索する。

Clonezillaでいいのであれば(最大の欠点は、より小さいディスクへの復元ができないことだが)、Clonezillaがわかりやすく容易だし、Mondo Rescueも工夫次第でかなり柔軟だ。

しかし、今回はよりテクニカルかつ原始的に解決してみる。

バックアップを考える

ものすごく単純なバックアップ方法としてddがある。この場合、ディスクに記録されている全ビット情報をクローンする。完全なクローンが出来上がるが、20GBを使用している2TBディスクのクローンのために2TBの領域が必要となる。

これを緩和する方法として圧縮があるが、この場合使っていないが書き込まれている「ノイズ」が多い場合は有効に小さくならない。

NTFSであればntfscloneというプログラムによって、必要最低限の領域で正確にクローンができる。特殊イメージ形式は非常にコンパクトだ。

ここで、Windowsディスクの特性と、Windowsシステムの特性を見てみよう。

$ socat tcp-listen:20083 STDOUT
GPT fdisk (gdisk) version 1.0.1

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.
Disk /dev/sdb: 625142448 sectors, 298.1 GiB
Logical sector size: 512 bytes
Disk identifier (GUID): 9AED5CE5-3A6D-469A-BCB0-51F33E062B6F
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 625142414
Partitions will be aligned on 2048-sector boundaries
Total free space is 2669 sectors (1.3 MiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048          206847   100.0 MiB   EF00  EFI system partition
   2          206848          468991   128.0 MiB   0C01  Microsoft reserved ...
   3          468992       625141759   297.9 GiB   0700  Basic data partition

これはUEFIシステムのWindows 7だ。

GPTが使用されており、sdb1がUEFI(ESP)で、FAT32またはFAT16である。

sdb2がMSR。sdb3がシステムパーティションだ。

ESPが2048ブロックから開始している。これより手前部分、GPTとUEFIであればなくてもそれほど苦労せずに復元できると思うのだが、一応残しておいたほうがいいだろう。

0, 1, 2はサイズが小さいため、単純にddでもそれほど問題ないだろう。

システムパーティションはWindows XP以降はNTFSであり、ntfscloneが利用可能だ。

Windowsのシステム起動については、ディスクレイアウトがバックアップ時と同じである必要がある。そのため、手前ブロックを含めて復元して同じレイアウトを再現する必要がある。

ただし、ディスクサイズに対してはある程度柔軟だ。大きいシステムパーティションが最後方にあるため、パーティション3を拡張したり切り詰めたりした上でntfscloneで復元することができる。MBRからGPTへの変換などは、同時にはできないと考えたほうが良い。

転送を考える

バックアップ先は色々考えられるだろう。光学ディスクへのバックアップ、外付けハードディスク、あるいはNAS。

今回の場合は、高性能でストレージ容量の大きいメインデスクトップへと転送する、という方法をとる。デスクトップとラップトップを併用している人などにとっては一般的な要求だろう。

ここでLAN接続の強みが出る。ネットワーク経由で転送することができ、USB2で転送するよりもはるかに速い。
ちなみに、USB1.1を採用するような古いマシンの場合でも通用する方法であり、100BASE-TXあたりのネットワークに接続できる(外部カードを使う方法を含めて)のであれば、USBドライブよりもはるかに高速だ。汎用性がある。

簡単さで考えればSSH経由が最も楽だ。例えば

$ ssh desktop.local -- sh -c 'cat > backup-sdb1' < /dev/sdb1

ただし、SSHによる負荷を嫌うのであれば、rshのほうがいいだろう。しかし、あまり最近はrshを許容していないのではないだろうか。

ほかにもZshを使うとか、FTPを使うとか、FTPFSを使うとか、方法は様々なのだが、ものすごくシンプルに考えると標準入出力渡しが良い。

nc(netcat)だ。

しかし、単純にサーバーで

$ nc -l -p 10000 > sdb1.img

クライアントで

$ nc desktop.local 10000 < /dev/sdb2

としても、転送終了でコネクションを切ってくれない。切るだけであれば、クライアントを

$ nc -c desktop.local 10000 < /dev/sdb2

(GNU netcatの場合)とすれば良いのだが、この場合ちゃんと転送が終わってから切ってくれないため、ちゃんとファイルが転送されない(!)

これを解決するには、クライアント側はsocatを使うようにすれば良い。

$ socat STDIN tcp:desktop.local:10000 < /dev/sdb2

別にサーバー側もsocatを使って良い。OPENよりもSTDOUTのほうが安定している。

$ socat tcp-listen:10000 STDOUT > sdb2.img

実際にやってみる

まずはオフセット分をコピーする。サーバー側:

$ nc -l -p 10000 > sdb0.img.xz

クライアント側:

$ dd if=/dev/sdb bs=512 count=2048 | xz -zev -T 4 | socat STDIN tcp:desktop.local:10000

このケースにおいてはクライアントに余力があるため、サーバーに余計な負担をかけないよう、xzはクライアントで行っているが、古いマシンならばサーバー側でしたほうがいいだろう。:

$ nc -l -p 10000 | xz -zev -T 8 > sdb0.img.xz

クライアント側:

$ dd if=/dev/sdb bs=512 count=2048 | socat STDIN tcp:desktop.local:10000

sdb1以降は少し楽。

$ xz -zev -T 4 < /dev/sdb1 | socat STDIN tcp:desktop.local:10000

sdb2も同様。sdb3はntfsclone。

$ ntfsclone --save-image --output - /dev/sdb3 | socat STDIN tcp:desktop.local:10000

NTFSクローンでもサーバー側ですることは変わらない。

クライアント側socatの代替

Pythonもやろうとしたが、辛いので諦めた。
とはいえ、この4つに対応していれば、だいたいの環境でいけるだろう。

もちろん、LuaやJavaScriptやPythonが好きな人は、即席でプログラムを書いても構わない。

Zsh

TCP Function Systemを使う。

$ autoload -U tcp_open
$ tcp_open desktop.local 10000
$ tcp_send -c < /dev/sdb1
$ tcp_close

TCP shootのほうが簡単

$ zmodload zsh/net/tcp &&  autoload -U tcp_point
$ tcp_shoot desktop.local 10000 < /dev/sdb1

あるいはzsh/net/tcpを使っても良い

$ zmodload zsh/net/tcp
$ ztcp -d3 desktop.local 10000 && ( cat /dev/sdb1 >&3 ) && ztcp -c 3

Perl

$ perl -MIO::Socket::INET -e 'BEGIN { $sock = IO::Socket::INET->new(PeerAddr => "desktop.local", PeerPort => 10000, Proto => "tcp")} while (read(STDIN, $buf, 524288)) { print $sock $buf }' < /dev/sdb1

Ruby

$ ruby -rsocket -e 'sock = TCPSocket.open("desktop.local", 10000); buf = "x" * 524288' -e 'sock.write buf while STDIN.read(524288, buf)' < /dev/sdb1

Bash

意外すぎる必殺技。Zshよりも簡単だったりするので恐ろしい。

$ cat /dev/sdb1 > /dev/tcp/desktop.local/10000

ただし、Bashはクライアントのみ。

書き戻しのnetcat代替

バックアップを使って書き戻す場合、bashが使えない。

仮にリモート側で

$ xz -dv sda0.img.xz | socat STDIN tcp:laptop:10000

とした状況としよう。

netcat

まずは基本

$ nc -l -p 10000 > /dev/sdb

socat

こちらも簡単

$ socat tcp-listen:10000 STDOUT > /dev/sdb

Zsh

$ zmodload zsh/net/tcp &&  autoload -U tcp_point
$ tcp_point 10000 > /dev/sda

もしくは

$ zmodload zsh/net/tcp
$ ztcp -ld3 10000 && ztcp -ad4 3 && cat <&4 > /dev/sdb && ztcp -c 4

Perl

$ perl -MIO::Socket::INET -e 'BEGIN{$l=IO::Socket::INET->new(LocalPort=>10000,Proto=>"tcp",Listen=>5,ReuseAddr=>1); $l=$l->accept} while (read($l, $buf, 524288)) { print $buf }' > /dev/sdb

Ruby

$ ruby -rsocket -e 's = TCPServer.open(10000).accept' -e 'buf = "x" * 524288 ' -e 'write buf while s.read(524288, buf)' > /dev/sdb

Bash

Bashはリスナーになれないので、リモート側を反転させる必要がある。

$ xz -dv sda0.img.xz | socat STDIN tcp-listen:10000

これを受け取る。

$ cat /dev/tcp/desktop.local/10000 > /dev/sdb

もちろん、この「クライアントがソケットを読む」デザインを他に適用しても良いのだが、多分あまりメリットがない。

この方法の意味と価値

まず、簡単の意味を考えなくてはならない。

この記事の内容は、基本的な知識を網羅していることを前提としている。当然ながら、システムバックアップをするためにLive Linuxを使おう、と発想するくらいには。

だが、理解するのは非常に容易だ。パイプとリダイレクションという、Unixerとしては初歩知識さえあれば十分理解できるレベルだ。

一般の人には難しいだろうと思うが、エンジニアを名乗るのであればこの程度の話は通じて然るべきだと思う。少なくとも、私はそう期待している。

「理解する気はないが、模倣して実行したいだけ」という場合は、わざわざこんな複雑な手順を取る必要はないと考えるだろう。実際、他にもバックアップソリューションはあるのだし、たとえコンピュータの後ろに手を伸ばしてでも、あるいは2度コピーする手間が生じたとしても、そのほうが良いと考えるだろう。

実際、即時参照できるネットワークドライブがあるにも関わらず、USBメモリーで渡せと言う人を、私は身近に知っている。

だが、思ったようにいかなかった時はどうすればいいだろう?例えば、Clonezillaが小さなディスクに復元できると思ったのにいざとなったらできなかった時だ。

特定のソリューションに依存するのは非常にリスクが高い。できるだけ汎用性のある方法で、かつ自分が理解できるもののほうがいい。

実際問題として、Mondo Rescueを使うという選択肢を取った場合は、バックアップ先をどうやってマウントするか、という問題が生じてしまう。

また、圧縮したいが、クライアントはリソースが非常に少ないのでできない、という場合に、一旦ディスクに保存してからの圧縮という方法をとらずに(恐らく復元する時は伸長してからの復元ということになる)行いたいといった要求に対応するのも、標準入出力を使うのであれば非常に容易だ。

技術を使う、知識を使うとはこういうことではないか。

特定の目的のために知識が技術を身に着けたところで、できるのはそれだけだ。それは単に慣れた、覚えたというだけの話であり、「知」ではないと思うのだ。

Windowsをアップグレードする前に元に戻せるようにスナップショットを取っておきたい、というのはごく当たり前にある状況だ。そのための機材が揃わないということも。

原理、物事の仕組みを理解し、方法を考えられることは非常に重要だ。すべてお膳立てされ、整った環境でなければできないのか?それがスキルなのか?

最低限ひとつの方法を知っていて、それで目的が達成できるということは有意なことだ。しかしそれは、楽をしようとして知らず犠牲を払っているということでもある。

覚えたひとつの方法でうまくいかない時、あるいは適切でないとき、適切な別の方法が考えられるか?そこが知なのだろう。

では、すべての人がそうした知識を持たなくてはならないのだろうか?私は、否であると考える。人には無限の時間が与えられているわけではない。知の獲得を志すことを是としても、そこにコンピュータに対するものが含まれているとは限らない。

だから、私の仕事があるのだ。ここに知がある。そして、それは独占するものではなく、分け与えられるべきものだ。私も生きなければならないので、無償で、というわけにはいかないが。

だからこそ、用意した方法が通用しない、しかし知を蓄える労力を払うべきという判断ではない、というのであれば、その時にこそ私を頼ってほしいのだ。

それだけの時間もお金もかけているのだ。それだけ真剣に努力を重ねてのものだ。

もちろん、自ら解決するための力をつけるために、私の手を借りてくれたって構わない。先人の肩を借りてその先までいくのは当然のことだ。このニュートンだって巨人の肩に乗るのだから。

Windows Updateに果てしなく時間がかかる

Forza Motorsport 6 Apexが登場するというので、

  • OEM版 Windows XPをP2VでWindows 10で動作しなくなるアプリの動作環境を確保
  • Windows 7のスナップショット取得
  • Windows 10にアップグレード

という手順を考えた。

Windows XPのP2Vのためのスナップショット取得などの作業とWindows7のバックアップ手順などは後で記述することとして、今回はWindows 7をWindows 10にしようとしたら、Windows Updateがチェックするにもアップデートするにも果てしなく時間がかかり、Windows 10にできそうにない。

Microsoftは何がしたいのか。

どうもこれはKB3138612のせいである模様。それが悪いのではないのかもしれないが、このアップデートからそうなっているようだ。

しばらくアップデートせずにおいておいた人からの報告が多い。私の場合、2月が最後だったため、2ヶ月半ほどアップデートしていなかった(そもそもWindowsを起動することが稀だから)。

で、KB3138612を入手して適用しようとしたのだが、これまた果てしなく時間がかかる。

で、もう少し調べていたら、オンラインではスタンドアロンインストーラも結局チェックに行くため時間がかかる模様

というわけでLANケーブルを抜いて適用した。

だが、これでも終わらないので、WindowsUpdateAgentの最新版(7.6), KB3050265も導入。

それでもダメで、修復してもダメなので、一旦Update前までロールバック。Windows Update AgentとKB3138612を先に導入し、更新チェックで放置。1時間40分ほどでチェックを完了した。

恐らくは最初に更新して時間がかかりすぎ、再起動したのが原因だが、ロールバックなんて普通はできない。更新途中での再起動も普通に考えられることだ(自動アップデートの場合もあるわけだし)。それで壊れるなんて、設計としておかしすぎるだろう。

アップデートやセットアップに何日も潰したことも数え切れない。今回も結局一日がかりだ。ほんと、Windowsは嫌いだ。Microsoftは何がしたいのだろう。