動かざることバグの如し

近づきたいよ 君の理想に

NodejsでファイルからMD5ハッシュを取得したい

md5はこういうの

~/tmp $md5 tmp.wav
MD5 (tmp.wav) = a7b3e6a4865d0b5ae39db05371008c2a

をNodejsで求めたい

環境

  • nodejs 8
    • けど見てる感じ4以上なら動きそう 10でも動いたし

コード

const crypto = require("crypto");
const fs = require("fs");

function md5file(filePath) {
    const target = fs.readFileSync(filePath);
    const md5hash = crypto.createHash('md5');
    md5hash.update(target);
    return md5hash.digest("hex");
}

console.log(md5file('./tmp.wav'));
~/tmp $node md5.js
a7b3e6a4865d0b5ae39db05371008c2a

あってるっぽい(当然

参考リンク先でも言及されてるが、〜GB単位のファイルを扱う場合だとfs.readFileSync()が死ぬ(多分メモリに乗せるので

それも考慮した作りにしたい場合はfs.createReadStream()でストリームを扱う(以下リンク先に丸投げ

参考リンク

sedコマンドで正規表現を使う場合は注意が必要

結論

発端

例えば以下のようなテキストファイルがあったとして、

~ $cat /tmp/sample.txt
1a
2b
3c
4a
5b

「a」を含む行を一括削除したいとする。以下のコマンドで可能

~ $sed -e '/a/d' /tmp/sample.txt
2b
3c
5b
  • -e のあとに条件式を書く(省略した場合は第一引数が条件になる)
  • /dは置き換えではなく削除の意味

仕様が変わって、「a」に加えて「b」を含む行も削除したいとする。

するとこうなる。

~ $sed -e '/a/d' /tmp/sample.txt | sed -e '/b/d'
3c

sedコマンドの仕様上、-e を連続して書けば複数条件式を指定できるので、以下のほうが好ましい

sed -e '/a/d' -e '/b/d' /tmp/sample.txt
3c

例なので単純だが、複雑な条件式だった場合正規表現で記述したほうがシンプルな例もあると思う

sed -E '/[a|b]/d' /tmp/sample.txt

だがこのコマンドは動くsedとエラーになるsedがある

どういうこと?

そもそもsedコマンドで確実に使える正規表現は以下のみ(正確にはPOSIX 1003.2で規定されているsed)

で、GNU sedでは↑に加えて以下も利用可能 つまり拡張版ってわけ

拡張対応済み(GNU sed)のバージョンなら動くが、バージョンやビルドオプション等によって動いたり動かなかったりする(実際同じOS,sedコマンドのバージョンでも挙動が違った)

さっきの sed -E '/[a|b]/d' /tmp/sample.txt は拡張側に含まれる正規表現パターンだったのでNGだったわけ

対策

対策1

正規表現を使わないで愚直にいく

対策2

POSIX 1003.2で規定されているsed正規表現パターンのみ使う

のでさっきの例だと

~ $sed -E '/(a|b)/d' /tmp/sample.txt
3c

になる (カッコが[]から()へ変更)

なんかgrepコマンドでも同じ状況にハマった気がするけど、気をつけよう。。。

対策3

自分でsedコマンドをビルドしてmake install

えへへ

参考リンク

Chefでyarnをインストールするレシピ書いた

やりたいこと

yarnでnpmパッケージのインストールを行いたいのではなくyarnバイナリファイル自体をChefで自動インストールしたいって話

レシピ

include_recipe 'apt::default'
apt_repository 'yarn' do
  uri 'https://dl.yarnpkg.com/debian/'
  key 'https://dl.yarnpkg.com/debian/pubkey.gpg'
  components ['main']
  distribution "stable"
  action :add
end

package "yarn"

てかすでにcookbookあるんじゃね?

すまん、もうあったわ(

github.com

けどこのcookbook自体はyarn installを行うやつで、yarn::repositoryでレポジトリ登録はやってくれる。が、apt install yarn は手動っぽい

PHPで複数画像を1枚の画像に結合する

環境

多分7以降でも動くと思う(未検証

やりたいこと

例えば以下のような画像3枚があったとして

f:id:thr3a:20181108215923j:plain

f:id:thr3a:20181108215926j:plain

f:id:thr3a:20181108215922j:plain

こんな感じに縦並びで1枚の画像に結合したい

f:id:thr3a:20181108220121j:plain

ImageMagickを使う方法

PHPからImageMagickを叩くことができる(imagick.soのインストールが必要)

さすがPHPといったところか、専用のメソッドがあるのでわずか数行で完成する。

<?php

$images = ["gazo1.jpg", "gazo2.jpg", "gazo3.jpg"];

$result = new Imagick();
foreach ($images as $path) {
  $im = new Imagick($path);
  $result->addImage($im);
}
$result->resetIterator();
$combined = $result->appendImages(true);
$combined->setImageFormat("jpeg");
header("Content-Type: image/jpeg");
echo $combined;

appendImages()時にtrueを渡しているのは縦並びに結合したいから。他の言語使ってると???ってなるが、引数にtrueだと縦並び、falseだと横並びに結合してくれる。

GDを使う場合

が、いろいろあってサーバーにImageMagickがインストールできない場合、GDを使う手もある。

<?php

function mergeImages($images) {
  $total_w = 0;
  $total_h = 0;
  foreach ($images as $path) {
    list($w, $h) = getimagesize($path);
    $total_h += $h;
    if ($w > $total_w) {
      $total_w = $w;
    }
  }

  $result_im = imagecreatetruecolor($total_w, $total_h);
  $merged_img_h_sum = 0;
  foreach ($images as $path) {
    $img = imagecreatefromjpeg($path);
    list($width, $height) = getimagesize($path);
    // コピー先の画像,コピー元の画像,コピー先のx座標,コピー先のy座標,コピー元のx座標,コピー元のy座標,コピー元の幅,コピー元の高さ
    imagecopy($result_im, $img, 0, $merged_img_h_sum, 0, 0, $width, $height);
    imagedestroy($img);
    // どこのy座標まで画像を展開したかを記録
    $merged_img_h_sum += $height;
  }
  // ブラウザに表示
  header("Content-Type: image/jpeg");
  imagejpeg($result_im);
  imagedestroy($result_im);
  // ファイルに保存
  $save_path = 'result.jpg';
  imagejpeg($result_im, $save_path);
}

$images = ["gazo1.jpg", "gazo2.jpg", "gazo3.jpg"];
mergeImages($images);

こっちは流石に関数化するレベルの行だが、やってることはそんなに難しくはない。先に空の画像を用意してforEachでそれぞれ画像を上から下へ貼り付けているイメージ。

参考リンク