HPCメモ

HPC(High Performance Computing)に関連したりしなかったりすることのメモ書き

git mergeで特定のファイルを取り込む

別のブランチで修正したファイルの一部だけを取り込む必要があってぐぐってたんですが、なかなか同じシチュエーションの人が居ないようなので作業の記録を残しときます。

git mergeに特定のファイルを指定するようなオプションは無いので、ちょっと面倒ですがmerge -> 不要なファイルをreset ->commitという流れになります。

実際に使うコマンドはこんな感じです。

> git merge --no-commit -Xtheirs {mergeしたいブランチ名}
> git reset HEAD {mergeしたくないファイル名}
> git checkout {mergeしたくないファイル名}
> git commit

ぐぐるとcherry-pickでできるよ!みたいなことが書かれてるページもあったけど、cherry-pickはあくまで個別のコミットを適用するだけなので、mergeするブランチ側で同じコミットに必要なファイルと不要なファイルが混ざっていたり、複数のコミットでmergeしたいファイルを編集してると難しいです。*1

mergeするファイルが少なければ、こちらでやられているようにmergeするブランチ側でパッチを作って適用というのも一つの手だとは思います。今回は、mergeするファイルが大量&バイナリファイルがかなり含まれていたので使いませんでしたが。*2

blog.bgbgbg.net

*1:1回mergeされる側のブランチでコミットを分割してやればできそうだけど、面倒そう・・・

*2:merge commitの履歴が残らないというのも人によっては気になるかもしれませんね

node.jsのfs.Statsからファイルのパーミッションを読み取る

node.jsのfs.stat, fs.lstat, fs.fstatが返すfs.Statsクラスには、その名もずばりのmodeというpropertyがあるのですが、この値からファイルのパーミッションを直接知る手段が標準では用意されていません。1 まーそんなに難しい話ではないので、頭の体操がてらちょちょいとコードを書いてみましょう。

まず、fs.Stats.modeの値の正体ですが、これはそのまんまstat(), lstat(), fstat()が返すstat構造体のst_modeフィールドの値と同じようなので、下位の(8進数で)3桁をマスクしてやれば通常のパーミッションが値として取得できます。 javascriptでのbit演算はCと同じで、&がbit-wise ANDなのでmode & maskとすればOKです。

developer.mozilla.org

ただし、modeの値は10進数だけど、我々が普段使っているumask値は8進数なので、この変換を行う必要があります。 といっても、parseInt関数を使えばOKでparseInt(777,8)で下位3桁のmaskが作れます。 もしスティッキービットとかも必要だったら7777にすればOKです。

developer.mozilla.org

これで、ファイルのパーミッションが10進数の整数値として取得できたので、最後に8進数の表現に直します。 こちらも、javascriptの標準の範囲内でできてしまって、toString()に引数として8を渡してあげればOKです。

developer.mozilla.org

この3つを組み合わせて、カレントディレクトリのファイルのパーミッションを8進数で表示するプログラムを作るとこんな感じになります。

const {promisify} = require('util');
const fs = require('fs');

async function getFileMode(filename){
  let stats = await promisify(fs.stat)(filename);
  return (stats.mode & parseInt(777, 8)).toString(8);
}

async function main(){
  let files = await promisify(fs.readdir)('./');
  files.forEach(async (file)=>{
    console.log(file, await getFileMode(file));
  });
}

main();

テスト結果

$ ls -l .
total 8
---xrwxrwx  1 so5  staff    0 12 13 23:06 file177
--w-rwxrwx  1 so5  staff    0 12 13 23:07 file277
--wxrwxrwx  1 so5  staff    0 12 13 23:07 file377
-r--rwxrwx  1 so5  staff    0 12 13 23:06 file477
-r-xrwxrwx  1 so5  staff    0 12 13 23:08 file577
-rw-rwxrwx  1 so5  staff    0 12 13 23:07 file677
-rwx--xrwx  1 so5  staff    0 12 13 23:11 file717
-rwx-w-rwx  1 so5  staff    0 12 13 23:11 file727
-rwx-wxrwx  1 so5  staff    0 12 13 23:11 file737
-rwxr--rwx  1 so5  staff    0 12 13 23:11 file747
-rwxr-xrwx  1 so5  staff    0 12 13 23:11 file757
-rwxrw-rwx  1 so5  staff    0 12 13 23:11 file767
-rwxrwx--x  1 so5  staff    0 12 13 23:11 file771
-rwxrwx-w-  1 so5  staff    0 12 13 23:11 file772
-rwxrwx-wx  1 so5  staff    0 12 13 23:11 file773
-rwxrwxr--  1 so5  staff    0 12 13 23:11 file774
-rwxrwxr-x  1 so5  staff    0 12 13 23:11 file775
-rwxrwxrw-  1 so5  staff    0 12 13 23:11 file776
-rwxrwxrwx  1 so5  staff    0 12 13 23:11 file777
-rw-r--r--  1 so5  staff  378 12 13 23:35 test.js
$ node test.js 
file177 177
file277 277
file377 377
file477 477
file577 577
file677 677
file717 717
file727 727
file737 737
file747 747
file757 757
file767 767
file771 771
file772 772
file773 773
file774 774
file775 775
file776 776
file777 777
test.js 644

10進数->8進数(もちろん2進数や16進数も)の変換にtoStringを使うっていうのは違和感がありますが、JSだと10進数以外の整数は文字列として表すしかないから当然といえば当然かもしれませんが。


  1. fs.accessの方は引数にパーミッションの条件を指定できるので、ファイルのパーミッションが見たけりゃstat系じゃなくてこっちを使ってねってことかと思いますが。

echo

みんな大好きなecho(1)ですが、最近Amazonから物理デバイスとして発売されたようですね。



しょうもないボケはさておき、Amazon Echoの発表と合わせてMusic Unlimitedという新サービスを開始するなど、スマートスピーカーの売り込みを頑張ってるようですね。
pc.watch.impress.co.jp

個人的には、ここ数年はAmazon Prime会員で特に最近はほとんど音楽はkindle FireからPrime musicの音楽を聴くばっかりという生活なので、echoはこのスピーカー機能だけでも買いたいんですが、ひとつ重大な問題があります。

amazon musicの検索機能がクラシック音楽に対して弱すぎる

発表資料のなかに「アレクサ、スピッツをかけて」なんてのが載ってましたが、クラシック音楽でこれをやるとたぶん使いものになりません。
というのも、曲名も人名も長すぎるのでメジャーな曲やら作曲者の場合こんな感じで俗称というか略語がついてるんですが、amazon musicで検索してもまったくひっかからないんです。
クラシック音楽の曲名の俗称の一覧 - Wikipedia

例えば、チャイコフスキーが作曲したヴァイオリン協奏曲は通常「チャイコン」と呼ばれますがこれで検索するとまったくひっかかりません。
f:id:n_so5:20171109095455p:plain

それどころか、チャイコフスキーで検索するのとTchaikovskyで検索するのでは結果が違うというていたらくです。
f:id:n_so5:20171109095549p:plain
f:id:n_so5:20171109095553p:plain

また、ストラヴィンスキーが作曲した「春の祭典」という曲は「ハルサイ」と呼ばれてますが、これで検索するとナクソスから出てるネタアルバム「ハルサイとか聴いてるヤバい奴はクラスで俺だけ。」しかひっかかりません。
f:id:n_so5:20171109100516p:plain


amazon echo(に限らずスマートスピーカー)と音楽再生サービスの組合せには大いに期待してるんですが、このままでは「年末に第九でも聴くかー」という状況で「アレクサ、ベートーベンの交響曲第9番をかけて」と言わなくちゃならんようなコレジャナイ未来が待ってそうなので、今朝からひたすらAlexa Skills Kitのドキュメントを読み漁ってます。

https://developer.amazon.com/ja/alexa-skills-kit

音楽再生の要求に対して、検索キーワードを俗称ー>正式名称に変換するパイプを作れれば十分目的は果たしそうなんだけど、そういう機能は無いのかなぁ

node.jsのpromisify

ここ数ヶ月nodejsでお仕事をしていて、こんな感じのコードをいくつか作ったんですが、昨日初めてutil.promisifyの存在を知りました orz

function hoge(filename){
  return new Promise(function(resolve, reject){
    fs.readFile(filename, function(err,data){
      if(err){
         reject(err);
      }else{
         resolve(data);
      }
    });
  });
}

このコードが次の1行で済んじゃうという・・・

util.promisify(fs.readFile)

実装はこんな感じです。
github.com
やってることは、

  1. 引数で渡された関数を実行
  2. callbackの第1引数がfalsyだったらreject
  3. そうでなければcallbackの第2引数以降を引数にしてresolve

引数で渡された関数が例外を投げた時にもrejectしたり、callbackの第3引数以降もまとめてresolveの引数に渡してたりと上でやってることよりさらに汎用的な作りになってます。


要はいままで劣化した車輪を再発明してたようで・・・

ケーブルオーガナイザ

結構前の話ですが、IKEAで売ってる高さ調節のできるワークデスクを導入しました。*1*2

www.ikea.com

ほぼ高さ調節用のハンドルがついてるだけのシンプルな作りなのは良いんですが、仕事道具を並べたらさっそく机の上までスパゲッティに侵されました。*3

f:id:n_so5:20170719130148j:plain

そんなこと百も承知のIKEA様は机売り場のそこかしこにこんな製品を売ってたわけですが、今さらIKEAまで買いに行くのも負けた気がするので100均グッズで自作してみました。

www.ikea.com



用意したのは、ダイソーC型クランプ&ワイヤーネットです。

f:id:n_so5:20170719130614j:plain

クランプでワイヤーネットを机の天板からブラ下げて

f:id:n_so5:20170719134651j:plain

タイラップでケーブル&ACアダプタを固定すれば完成!

f:id:n_so5:20170719142831j:plain

クランプの上部が机の上に飛び出しているので、端の方にモノを置くと干渉しますが、合わせて350円(税抜)かと思うと許せます :)

*1:約2万円のお値段ですが、配送を頼むと送料が1万円を越えました・・・orz

*2:それでも格安

*3:足元はもっと大変なことになってますw

ansibleで改行コードの変換

ansilbleのplaybookの中でpatchモジュールを使って転送したファイルにpatchをあててたのがあったのですが、なぜかパッチファイルと適用対象ファイルの改行コードがLFの時は動作するのに、CRLFだと駄目という現象が起きたので、急遽playbookの中で改行コードを変換する方法を調べてみました。

使うモジュールはこちら
replace - Replace all instances of a particular string in a file using a back-referenced regular expression. — Ansible Documentation

テスト用にこんな内容のファイルを用意します。中身は一緒で改行コードがLFのものとCRLFのものです。

$ cat org_crlf.txt 
hoge
hoge
$ cat org_lf.txt 
hoge
hoge
$ file hoge_*
hoge_crlf_org.txt: ASCII text, with CRLF line terminators
hoge_lf_org.txt:   ASCII text

でもって、次のようなplaybookを作って実行します。

- hosts: 127.0.0.1
  connection: local
  strategy: debug
  tasks:
    - copy: 
        src: "{{ item.src }}"
        dest: "{{ item.dest }}"
      with_items:
        - {src: "org_crlf.txt", dest: "./hoge_crlf_to_lf.txt"}
        - {src: "org_crlf.txt", dest: "./hoge_crlf_to_crlf.txt"}
        - {src: "org_lf.txt",   dest: "./hoge_lf_to_lf.txt"}
        - {src: "org_lf.txt",   dest: "./hoge_lf_to_crlf.txt"}

    - name: CRLF to LF by replace
      replace:
        dest: "{{ item }}"
        regexp: '(.*)\r\n'
        replace: '\1\n'
      with_items:
        - "hoge_crlf_to_lf.txt"
        - "hoge_lf_to_lf.txt"

    - name: LF to CRLF by replace
      replace:
        dest: "{{ item }}"
        regexp: '(.*)[^\r]\n'
        replace: '\1\r\n'
      with_items:
        - "hoge_crlf_to_crlf.txt"
        - "hoge_lf_to_crlf.txt"

やってることは、用意したファイルをコピーしてregexpでそれぞれCRLF -> LF およびその逆の変換を行なっているだけです。

結果は以下のとおり。

$ ansible-playbook test.yml 
 [WARNING]: Host file not found: /usr/local/etc/ansible/hosts

 [WARNING]: provided hosts list is empty, only localhost is available


PLAY [127.0.0.1] ***************************************************************

TASK [setup] *******************************************************************
ok: [127.0.0.1]

TASK [copy] ********************************************************************
changed: [127.0.0.1] => (item={u'dest': u'./hoge_crlf_to_lf.txt', u'src': u'org_crlf.txt'})
changed: [127.0.0.1] => (item={u'dest': u'./hoge_crlf_to_crlf.txt', u'src': u'org_crlf.txt'})
changed: [127.0.0.1] => (item={u'dest': u'./hoge_lf_to_lf.txt', u'src': u'org_lf.txt'})
changed: [127.0.0.1] => (item={u'dest': u'./hoge_lf_to_crlf.txt', u'src': u'org_lf.txt'})

TASK [CRLF to LF by replace] ***************************************************
changed: [127.0.0.1] => (item=hoge_crlf_to_lf.txt)
ok: [127.0.0.1] => (item=hoge_lf_to_lf.txt)

TASK [LF to CRLF by replace] ***************************************************
ok: [127.0.0.1] => (item=hoge_crlf_to_crlf.txt)
changed: [127.0.0.1] => (item=hoge_lf_to_crlf.txt)

PLAY RECAP *********************************************************************
127.0.0.1                  : ok=4    changed=3    unreachable=0    failed=0   

$ file hoge_*
hoge_crlf_to_crlf.txt: ASCII text, with CRLF line terminators
hoge_crlf_to_lf.txt:   ASCII text
hoge_lf_to_crlf.txt:   ASCII text, with CRLF line terminators
hoge_lf_to_lf.txt:     ASCII text

というわけでめでたくreplaceモジュールで改行コードの変換ができたようです。
ターゲットマシンがwindowsになった時にregexpに指定している'\n'がLFとして解釈されるのか、CRLFとして解釈されるのか気にはなりますが、ちょっと今時間が無いのでここで一旦終了します。
もし知ってる方いたら教えてもらえると助かります。

mac 上で vagrant + ansibleの環境構築

前回は、ホストマシンとしてWindows10を使った環境を作りましたが、そろそろLinux環境をゲストにしたものも必要になってきたのでサクっと作ってみました。

hpcmemo.hatenablog.com


既に入っているものもありますが、必要なソフトをbrewでインストールします。

> brew install Caskroom/cask/vagrant Caskroom/cask/vagrant-manager vagrant-completion \
                        Caskroom/cask/virtualbox Caskroom/cask/virtualbox-extension-pack ansible

今回はCentOS7で環境を作る必要があったので、こちらの記載を参考にvagrant initします。
Vagrant box centos/7 | Atlas by HashiCorp

> vagrant init centos/7

Vagrantfileにはとりあえずvirtualboxのメモリ設定だけ追加して、あとは仮に"site.yml"というplaybookの名前を指定しておきます。

$ cat Vagrantfile 
# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
  config.vm.box = "centos/7"
  config.ssh.insert_key = false
  config.vm.provider "virtualbox" do |vb|
    vb.gui = false
    vb.memory = "4096"
  end
  config.vm.provision "ansible" do |ansible|
    ansible.playbook = "./site.yml"
  end
end

最後にこのsite.ymlを作っていきます。といっても今回は単純な開発環境なので、こんな感じでyumを使っていくつかのパッケージを入れるだけの簡単なplaybookです。

$ cat site.yml 
- hosts: all
  tasks:
  - name: install basic tools for build C/C++/Fortran program
    become: yes
    yum:
      name: "{{item}}"
      state: latest
      update_cache: True
    with_items:
      - cmake
      - autoconf
      - automake
      - git
      - gcc.x86_64
      - gcc-c++.x86_64
      - gcc-gfortran.x86_64

ターゲット環境が毎回微妙に変わるので*1この辺のツールをインストールするところはroleにして再利用できるようにしておくと大変便利そうです(でもやってない)

*1:CentOS or RHELのバージョンが 6だったり5だったり、たまにUbuntuの時もあるけど