それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

bundlerを使い始めた時に詰まったので調べた。

bundle 最初に知ること

bundler で出来ることを最初に知ることが必要

bunder はプロジェクトで使ってるgem を一覧するためのもの。

bundler は 必要なgem をインストールしてくれる

bundler は、自作ライブラリのgem化が出来る

bundler は、プロジェクトに必要なGemを「プロジェクト毎」にインストール出来る

bundler は、共有gem とプロジェクトで足りないものをインストール出来る。

つまり、bundler を使うことで、「必要なGem」の一覧を管理することが出来る。

このことにより、デプロイ先に開発元と同じgem環境を再現することができる

bundler 始め方

ディレクトリにGemFileを作成する。

もしくは、コマンドラインで bundle init とする

基本的な流れやで

sudo apt install ruby-bundler
mkdir プロジェクト
cd プロジェクト
bundle init 
bundle config set --local path 'vendor/bundle'
echo gem "nokogiri" >> GemFile
bundle install 

なにか追加したいときは、マニュアルで編集するか

vim GemFile
bundle install

または、bundle add する

bundle add sqlite3

プロジェクトの中にインストールする場合(2023-08-10追記)

bundle コマンド導入後なら、次をコピペすれば、プロジェクトごとに分離したbundler 環境を作れる。

mkdir my-project
cd my-project

bundle init
bundle config set --local path 'vendor/bundle'
bundle config set --local disable_shared_gems true
bundle add sqlite3

プロジェクト内部でruby を実行するとき

bundle 経由で実行する

bundle exec ruby sample.rb

sample.rb ではrequire するだけでいい。

# in sample.rb 
require 'sqlite3'

つぎに GemFile の書きかた。その0。

はじめに、source を書く。

source "https://rubygems.org"

これにより、gemをどこから探すか指定することが出来る。

自作レポジトリも指定できる。

source "https://rubygems.org"
source "https://takuya.example.org"

作成方法はgem generate_indexなどでググれば出てくる。

 GemFile の書きかた1

GemFileには、必要なGemを書いていく。

gem "nokogiri"
gem "pry"
gem "sequel"

GemFile の書き方2

必要なgem ファイルは、グループ分けすることが出来る

gem "nokogiri"
group :development do 
     gem "pry"
group :test do 
 gem "shoulda"
end

グループ分けしておくと、便利なことがある。

  1. デプロイ先で無駄なインストールしなくて便利だ
  2. bundle.require でグループ毎にreuqire 出来て便利。

GemFile の書き方3

バージョンを指定することが出来る

gem "nokogiri", ">= 1.4.2"

バージョン指定の方法は ruby require や gem install で用いられるものと同じだと思えばイイ。

bundler が使うファイル。

  • GemFile  使う(依存する)gemの一覧
  • GemFile.lock (実際に使ってる)gemとそのバージョン一覧

おもに GemFile が使われる。

GemFile.lockがBundlerで実際にインストールされたバージョンの一覧。

lock ファイルの記述を優先してインストールすることになる

lockファイルの扱い

gem file lock 記述を優先してインストールを再現される。が、、困った現象が起きた経験がある。

Bundlerが windows バイナリをOSXに入れようとしてて驚愕。OSXCygwinのバイナリ入れたら動かんだろう?でもインストールしてた。それがlockファイル。

Windowsバイナリでトラブルったのでgemfile.lock は細かく管理したい時に用途を限ることにした。バージョンやバイナリを詳細設定するときに限る。

Windows(開発)→Linux(デプロイ)のときは、全く役に立たないし、lockは消して構わない。gemfile.lock があるせいで、lockファイルはゴミ

bundler を使ったrequie 

bundle install のやり方が分かったら、次に bundler を使ったrequire を覚えておく

require 'bundler'
Bundler.setup
require 'nokogiri'

はい、これでOK

Gem を何処に入れるのか

bundler が良く分からない理由はコレだと思う。

  1. bundler にgem 環境を プロジェクト毎に作らせる
  2. bundler にシステム環境へgemをインストールさせる。

この2パターンがあってですね。システム環境やユーザー環境のGEM_HOMEかプロジェクト環境を選ぶことが出来るわけ。

個人で使う分には、プロジェクト環境よりシステム環境やユーザー環境利用が幾分に楽。

disable shared gems = 1

共有のgem をdisabledにして、プロジェクト内にgem 環境をゼロから作る。

bundle install vendor/bundle --disable-shared-gems

この場合、システム環境のgemは全て無視される

または、.bundle/config にかく

echo  BUNDLE_DISABLE_SHARED_GEMS: '1' >> .bundle/config

これで、bundleはプロジェクト環境に全てのgemをインストールする。

  BUNDLE_DISABLE_SHARED_GEMSの功罪

bundle が良く分からない現代魔法になってるのは、shared_gems にあるとおもう。

 BUNDLE_DISABLE_SHARED_GEMS: '1'

システム環境のgemを使いたい時は、 BUNDLE_DISABLE_SHARED_GEMS: '1'を消す

一年くらい使っててbundle でプロジェクト毎にパッケージをインストールするのは無駄と思うようになった。

pry とか mysql2 とかいくつもインストールしてんだわ。

システムの libxml 環境変わったのに古いbundleがlib指してて反って面倒だったり。システム全体のgem 使って欲しい場面のほうが多かった。 とくに開発環境だと時間の無駄が多いので必要ない気がする。

デプロイ先なら  BUNDLE_DISABLE_SHARED_GEMS: '1' をした方がいいかも。

つまり、BUNDLE_DISABLE_SHARED_GEMSがあると同じgemをいくつもインストールするので、ちょっとイラッとする。

bundleの手軽さが死ぬきがする。bundleのよさは git clone ; bundle install ; ですぐ使えることだと思うんだ。

bundle の設定

bundle/config の上書き関係

たとえば、BUNDLE_DISABLE_SHARED_GEMSの設定は次の箇所で指定される。

~/.bundle/config
PROJECT/.bundle/config
bundle install --disable-shared-gems

の順番で、ユーザ → プロジェクト → コマンド実行時になる。

 bundle install は何処に入るのか

  1. bunele install は なにも設定をしなければ system のgem が使われる
  2. bundle install はなにも設定ををしなければ system の gem にインストールされる
  3. bundle install は gem listall の自動化になる。

ただし、BUNDLE_PATHが環境変数にあったり、disable-shared-gemsが指定されていればこの限りではない。

なので、油断してるとシステム環境にインストールされちゃったり、プロジェクト環境にインストールされちゃうので注意が必要

私の場合、rbenv 環境にgem がインストールされないと思って悩んでいたら

ユーザー環境にデフォルト設定が会った。 ~/.bundle/config に disable-shared-gemsが設定されていた。はぁ。。。

bundle list で bundleされた一覧を見る

rbenv やrubygems が管理してるgemに任せるのが楽だった。

takuya@~$ bundle config
Settings are listed in order of priority. The top value will be used.
bundle install

個人環境なら vendor/bundle 使うとかえって面倒かもね。 使わなくなったら

gem uninstall hogehoge
gem cleanup 

で消しちゃってもいいしrbenv環境ごとポイッ

個人のrbenv環境で使うなら、何も設定せずにbundle install 叩いてたほうが圧倒的に楽だと思った

同じファイルをいくつもシステムに持つのダルいしコンパイルオプション考えるの面倒だしさ 自分のプロジェクトってだいたい同じようなgem使ってるじゃん?大きく違うバージョンや環境でもない限り vendor  使う必要ないと思った。

gem のソースを追いかけるために、locate や mdfind して同じファイルが大量に来るのも気持ち悪い・・・

強制的にsystem に入れる方法

bundle install --system 

ただ、既存のlock やconfigがあるとうまく動かない

vendor/bundle を使う方法

bundle install vendor/bundle --disable-shared-gems

これは、shared-gem(さっき言及した、システム全体gem)を拒否して、vendor/bundle にゼロからgem 空間を構築する 必要なgems の全てがvendor/bundleに入ります。

path / disable-shared-gems はワンセットになってるので消しちゃうとちょっと面倒なことに

手元の環境で、vendor/bundle 使うと、ここが不便でいや何だけど。

bundle の設定

現在のbundler の設定は bundle config で確認できる

$ bundle config
Settings are listed in order of priority. The top value will be used.

bundle の設定は、プロジェクトとHOME_PATHの2つをマージして使う

~/.bundle/config
PROJECT_HOME/.bundle/config

の2つのymlをマージすることになる。

たとえば、bundle install nokogiriにビルドオプションを与えたい時

bundle config --global  build.nokogiri -- \
 --with-xml2-dir=`brew --prefix libxml2`\
 --with-xslt-dir=`brew --prefix libxslt`

とする。

全てのbundle install nokogiri に設定したいときは ~/.bundle/config に書く。

> cat  ~/.bundle/config
---
BUNDLE_BUILD__NOKOGIRI: "--with-xml2-dir=/usr/local/opt/libxml2 --with-xslt-dir=/usr/local/opt/libxslt"

*1

.bundle/config の設定項目

設定項目には次のようなものがある。

  • BUNDLE_PATH
  • BUNDLE_FROZEN
  • BUNDLE_WITHOUT
  • BUNDLE_BIN
  • BUNDLE_GEMFILE
  • BUNDLE_SSL_CA_CERT
  • BUNDLE_SSL_CLIENT_CERT
  • BUNDLE_CACHE_PATH
  • BUNDLE_DISABLE_MULTISOURCE
  • BUNDLE_IGNORE_MESSAGES

設定は、同名の環境変数でも上書きされる。 環境変数のほうが楽かも?

環境変数を使う場合

BUNDLE_BIN="vendor/bundle/bin" bundle install 

また、config を指定する時だけ、次のような読み替えが許されるっぽい

BUNDLE_PATH => path
BUNDLE_BIN => bin

設定ファイルとコマンドオプションは同じ設定項目を意味する。

BUNDLE_BIN: "./bin"
bundle config --global bin "./bin"

デプロイ

bundle が真価を発揮するのはここ。

bundle install --deployment

このコマンドで、GemFile.lock で指定されている必要なパッケージを取ってくる  GemFile はユーザー指定 Gemfile.lock は bundle が調べて必要だと判別した全て。

ちなみに、そういうのはネットから取ってこれない場合のために

gem package 

すると vendor/cache に貯めておいてくれるので git vendor/cache でもしとけばいいけど、、OS超えてまでまず動かないと思うしやらないと思う。

bundle install --deployment --path=vendor/bundle

で環境の再現ということで。

vendor/bundle に入れた場合

bundle exec nokogiri
bundle exec 

手元の環境で、vendor/bundle 使うと、ここが不便でいや何だけど。

その他の話題

bundle コマンドを補完したい

brew install ruby-completion
brew install bundler-completion

などと、補完もいっぱいある

2023-08-10

更新。

参考資料

http://bundler.io/v0.9/bundle_install.html

https://gist.github.com/kozy4324/5719555

http://stackoverflow.com/questions/8104370/what-does-it-mean-bundle-disable-shared-gems-1

http://xxxcaqui.hatenablog.com/entry/2013/02/11/013421

http://ruby.studio-kingdom.com/bundler/gemfile

*1:HOME_PATH を --global というのはセンスが無いと思った --user にして欲しい --global は etcを期待してしまうハマった

ruby で[] のブラケットが入った文字列を扱うととてもめんどくさい。

面倒くさいんですよ。

ファイル名に [] ブラケットが混ざるととても扱いが面倒くさい。

以下の様なファイル構造があるとする。

takuya@~/Desktop$ tree '[資料]授業資料'
[資料]授業資料
└── a
    └── b

たとえば、globする場合

>> Dir.glob '[資料]授業資料/*'
=> []

glob の マッチの文法に、[set] が含まれるので、どうしようもない。

たとえば、exec するばあい

takuya@~/Desktop$ irb
>>
>> cmd = "ls '[資料]授業資料' '[資料]授業資料-01' "
ls: cannot access '[資料]授業資料-01': No such file or directory>> cmd
=> "'mv [資料]授業資料' '[資料]授業資料-01' "
>> exec cmd
sh: [資料]授業資料: command not found

mv だけダメなんですよ。

ls なら出来る。

>> cmd = "ls -l '[資料]授業資料' "
=> "ls -l '[資料]授業資料' "
>> exec cmd
total 0
drwxr-xr-x 3 takuya staff 102  8  9 00:15 a

shellwords でエスケープするともうメチャクチャ

utf-8 の日本語全てがエスケープされちゃいましたよ。もうどうしろというのか

>> cmd = 'mv ' + Shellwords.escape('[資料]授業資料') + '  ' + Shellwords.escape('[資料]授業資料-01')
=> "mv \\[\\資\\料\\]\\授\\業\\資\\料  \\[\\資\\料\\]\\授\\業\\資\\料-01"
>> print cmd
mv \[\資\料\]\授\業\資\料  \[\資\料\]\授\業\資\料-01=> nil

結論:逐次置換しかかない

Stackoverflowとかも一応見てみたけれど、逐次置換するしか無いっぽいぞ。

Rubyのこのあたりの文字コードと、文字列の扱いは本当にひどいと思う。

このように、ファイルに含まれるglob文字列を展開する必要がありそうだ

file_name.gsub( %r'[\{\}\[\]\*\?\\]' ) {|c| '\\'+c }

あーめんどくさい

OSXのiCloud のアカウント設定を消去する

iCloud アカウントが連日ロックされています。

なぜか、私だって知りたい。私のicloud アカウントが毎朝ロックされています。

もう、面倒なので、メインのメールアドレスを変更してアカウントをしばらくちょっと違う状態にした。

しかし、メールアドレス変更後のOSXiCloud ログインで詰まる。メッチャ詰まる。

システム環境設定でicloud 設定をしても詰まる。メールアドレス変更後の iCloud アカウント設定オカシイ。

ずっとフリーズして、システム環境設定が応答なしになる。参った。

待ってられないので、設定まるまる削除する

icloud 関係の設定で関係がありそうな、ファイルを消して、エラーにしてしまえば、初期設定されるだろう。と予測。

それで、~/Library/ を探してたら mobileme 関連の plist があってソコにAppleIDが書いてあった。

~/Library/Preferences/MobileMeAccounts.plist

とりあえず、消せば、何かしらエラー初期化されるだろうと思って消したらビンゴ。

ちょっと適当な解決策だけど、目的を達成するには十分だった。

iCloud アカウント設定は、iPhone(Mac)を探すや、Keychainsが絡むので、アカウント変更は、思いつきでやっっちゃうと痛い目を見ることを学んだ。

SlideShareのスライド画像をまるっと保存してオフラインで見る。

slideshare 最近厳しい

slideshare がlinkedin になってから色々と厳しかったり、PDFをちょとお電波の届かないところで参照資料に出そうと思っても、不便すぎる

世の中100%オンラインと限らないんで。。。

表示しているJPGを手作業でキャッシュする

自作のブラウザで、JPG画像をキャッシュしてPDFに焼き直す

#!/usr/bin/env ruby 
#

require 'prawn'
require 'mechanize'
require 'open-uri'

m = Mechanize.new
m.get ARGV.shift

list =  m.page.search('//img[@class="slide_image"]/@data-full').map{|e| e.text() }


Prawn::Document.generate(( m.page.title.to_s+ ".pdf") ,:page_layout => :landscape, :page_size=>"A4") do 
  list.each{|image_url|
    image( open(image_url), :fit=>[800, 450] ) #フィットサイズはあとで考えたほうが。。。。
  }

end

prawn つかうと多少画像サイズが合わないので、サイズの微調整が面倒ですね。16:9スライドや4:3スライドを判別しないといけない。

面倒なときは、まるっとキャッシュしして、convert コマンドやpdftkにかけちゃったほうがいいかもね。

参考資料

http://takuya-1st.hatenablog.jp/entry/2015/01/18/223308

http://mechanize.rubyforge.org/Mechanize/Download.html

http://prawnpdf.org/docs/0.11.1/Prawn/Images.html

https://github.com/chou3ib1/slideshare-download-disabled-pdf

自作ブラウザとかキャッシュとか、そういう曖昧な表記は、規約云々が面倒だから、mechanize はブラウザ です!

ruby で mysql と sequel でぱぱっとアクセス

active record めんどくさいし

めんどうだし、遅いんですよ。SQLわかってれば、ActiveRecordってメッド生やすの面倒だし、オブジェクト作ったり手続きが面倒だったんで。

ruby で mysql2 を使ったアクセス

BestGem でダウンロード数を比較したらhttp://bestgems.org/search?q=mysql mysql2 がトリプルスコアで圧倒的だったので。これを使うことにした。

gem install mysql2

mysql2 の利用

使い方は、大変シンプル

  • client(connection)
  • Statement
  • Result

という王道の三本セット

require 'mysql2'

client = Mysql2::Client.new(host:"192.168.2.5", username:"takuya", password: "*********", database:"my_Db")
ret = client.query("select * from sample order by id desc limit 10 ");
ret.each{|row |
         p row
}
mysql2 で prepare ステートメント

mysql2公式に載ってた。

statement = @client.prepare("SELECT * FROM users WHERE login_count = ?")
result1 = statement.execute(1)
result2 = statement.execute(2)

名前付きbindをするのは、無理そう

トランザクション

自分で管理。

client.query("begin");
client.query("select * from sample order by id desc limit 10 ");
client.query("commit");

潔くていいし、低レベルレイヤなのでわかりやすいっすね。

sequel を使ってもう少しだけ抽象化

ruby のDBアクセスで、もう一つの定番はSequel

sequel はバックエンドにMysql2 を使える。いいね。

sequel 使うなら、ActiveRecord使えばいんだろうけどさ、rails 関連ってググラビリティの性能が悪いので、あまり好きじゃない。php 波にカオス。

gem install sequel

基本的な使いかた

require 'sequel'
require 'logger'


db = Sequel.connect('mysql2://takuya:**pass**@192.168.100.1/takuya_db',:loggers => [Logger.new($stdout)])
res =  db[:tablename]

res.each{|e|
 p e
}

ポイントは、 eachで回すまでクエリを発行しないところ。

sequel でトランザクション

トランザクションは、ブロックを渡す。これRubyっぽい。気に入った

db = Sequel.connect('mysql2://takuya:**pass**@192.168.100.1/takuya_db',:loggers => [Logger.new($stdout)])
db.transaction do 
  table =  db[:address]
  names.each{|e| 
    table.insert(e) 
  }
end

insert ・ update はハッシュ値で指定できて便利

データを突っ込むときは、ハッシュ値ベースで放り込めるので、オブジェクトあれば便利なんだろうけど、とりあえずファイルの値をつっこみたいとか、手作業でぱぱっと入れたいとかそういう時にすごく重宝するわこれ。

sequel で prepare

そのうち調べる。

ちょっと面倒なんだけどね。ハッシュのキーとバリューがあればできる。

参考資料

http://sequel.jeremyevans.net/

mac osx で無線LANをオンオフと指定SSIDに接続するコマンド

無線LANをオンオフできたら便利だよね。

便利だよね?絶対便利!

OSX標準の無線LANのON/OFFは手数が多い。

とくにWifiの構成とか考えて頻繁にオンオフ繰り返しているとすごく手間。

f:id:takuya_1st:20151227013852p:plain

ターミナルからWiFiをオフ

networksetup -setairportpower en0 off

ターミナルからWiFiをオン

networksetup -setairportpower en0 on

周囲のSSIDを探す

airport  -s 

airport の場所

airport コマンドは 結構深い場所にあるので、使いやすい位置に持ってきておく。

alias airport=/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport

ちなみに airport コマンドで wifi ssid から切断することもできる

sudo airport -z  # 切断

airport 使った切断は、sudo いるし再接続が面倒なのでnetworksetup 使ったほうがいいと思った

接続先のチャンネルを変える

11n 11ac など同一SSID複数チャンネル、複数周波数で展開されてる場合、また基地局が近くで同一SSIDで両方が拾える時、指定したチャンネル側につなぐ。

airport -c 11
チャンネルと、電波強度を確認する
airport -s 
           SSID BSSID             RSSI CHANNEL HT CC SECURITY (auth/unicast/group)
                  xxxxxxxxxxxxxx 00:12:00:fb:00:b3 -82  40      N  -- WPA2(PSK/AES/AES)
                  xxxxxxxxxxxxxx 00:12:00:fb:00:b2 -54  6       N  -- WPA2(PSK/AES/AES)
                  xxxxxxxxxxxxxx 00:6f:00:88:00:34 -87  1       Y  JP WPA(PSK/AES,TKIP/TKIP) WPA2(PSK/AES,TKIP/TKIP)
                  Buffalo-G-5E5F 00:e1:00:6b:00:60 -84  1       Y  JP WPA(PSK/TKIP,AES/TKIP) WPA2(PSK/TKIP,AES/TKIP)
                    xxxxxxxxxxxx 00:1d:00:b4:00:81 -54  11      Y  -- WPA2(PSK/AES/AES)

RSSI<90 のときは確実につながると思う。CHANNEL>100のときは5GHz だったかな。

接続中のSSIDを確認
airport -I | /usr/bin/grep -ie '^\s*ssid'  | cut -d ":" -f 2

指定のSSIDに接続したい

networksetup -setairportnetwork en0 0001softbank 
networksetup -setairportnetwork en0 graphic password(pre-sharekey )

などととすることで、結構簡単にSSIDを扱える。

参考資料

blog.mattcrampton.comxc d• Managing WIFI connections using the Mac OSX...

pdftk が動かなくなってたので入れなおし

pdftk が動かない・・・

pdfを扱うのに欠かせないpdftk が動かない 10.11 からの問題らしい

バイナリを取得して/opt/に入れてたのが動かなかった。なので brew から tap した

brew から tap はgcc/gcj とか一杯インストールしてしまうので、頭の良い解決方法でなさそう。 なによりコンパイルエラーで地獄見る

pdftk を入れる方法

ここに詳しく出ていた

http://stackoverflow.com/questions/32505951/pdftk-server-on-os-x-10-11

OSX 10.11 用にパッケージをリビルドされて提供してくれてる。

バイナリの取得先も書いてあったし、継続して情報を追いかけてくれてて感謝。

2017-01-13 追記

El Capitan でインストールすると、セキュリティエラーだった。。。

https://www.pdflabs.com/tools/pdftk-server/

cask / brew を使うと・・・

ビルド地獄になってとってもお勧めできなかった。

gccから作るんだもん・・・コンパイル済みパッケージがcask/brewで提供されるのはまだ先なんだろうな。

takuya@/private/tmp/docomo2015-12-0120151201-56837-stvrb1$ pdftk 1.pdf 2.pdf cat output  out.pdf
^C
takuya@/private/tmp/docomo2015-12-0120151201-56837-stvrb1$
takuya@/private/tmp/docomo2015-12-0120151201-56837-stvrb1$ which pdftk
/usr/local/bin/pdftk
takuya@/private/tmp/docomo2015-12-0120151201-56837-stvrb1$ brew info pdftk
Error: No available formula with the name "pdftk"
takuya@/private/tmp/docomo2015-12-0120151201-56837-stvrb1$ brew search pdftk
No formula found for "pdftk".
==> Searching pull requests...
Closed pull requests:
pdftk: 2.02 - a Handy Tool for Manipulating PDF Documents (https://github.com/Homebrew/homebrew/pull/25953)
takuya@/private/tmp/docomo2015-12-0120151201-56837-stvrb1$ ls -l $(which pdftk
> )
lrwxr-xr-x 1 takuya admin 28 10 16  2013 /usr/local/bin/pdftk -> /opt/pdflabs/pdftk/bin/pdftk
takuya@/private/tmp/docomo2015-12-0120151201-56837-stvrb1$ open /opt/pdflabs/pdftk
takuya@/private/tmp/docomo2015-12-0120151201-56837-stvrb1$ brew unlink pdftk
Error: No such keg: /usr/local/Cellar/pdftk
takuya@/private/tmp/docomo2015-12-0120151201-56837-stvrb1$ cask search pdftk
No Cask found for "pdftk".
takuya@/private/tmp/docomo2015-12-0120151201-56837-stvrb1$ rm /usr/local/bin/pdftk
takuya@/private/tmp/docomo2015-12-0120151201-56837-stvrb1$ which pdftk
takuya@/private/tmp/docomo2015-12-0120151201-56837-stvrb1$ brew search pdftk
No formula found for "pdftk".
==> Searching pull requests...
Closed pull requests:
pdftk: 2.02 - a Handy Tool for Manipulating PDF Documents (https://github.com/Homebrew/homebrew/pull/25953)
takuya@/private/tmp/docomo2015-12-0120151201-56837-stvrb1$ brew tap https://github.com/Homebrew/homebrew/pull/25953
Error: Invalid tap name
takuya@/private/tmp/docomo2015-12-0120151201-56837-stvrb1$ brew tap docmunch/pdftk
==> Tapping docmunch/pdftk
Cloning into '/usr/local/Library/Taps/docmunch/homebrew-pdftk'...
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (5/5), done.
remote: Total 5 (delta 0), reused 3 (delta 0), pack-reused 0
Unpacking objects: 100% (5/5), done.
Checking connectivity... done.
Tapped 1 formula (29 files, 120K)
takuya@/private/tmp/docomo2015-12-0120151201-56837-stvrb1$ brew install pdftk
==> Installing pdftk from docmunch/homebrew-pdftk
==> Installing dependencies for docmunch/pdftk/pdftk: ecj, gcc
==> Installing docmunch/pdftk/pdftk dependency: ecj
==> Downloading ftp://sourceware.org/pub/java/ecj-4.9.jar
######################################################################## 100.0%
🍺  /usr/local/Cellar/ecj/4.9: 2 files, 1.6M, built in 6 seconds
==> Installing docmunch/pdftk/pdftk dependency: gcc
==> Downloading http://ftpmirror.gnu.org/gcc/gcc-5.2.0/gcc-5.2.0.tar.bz2
Already downloaded: /Library/Caches/Homebrew/gcc-5.2.0.tar.bz2

EvernoteクリッパーでWebページを見るのが恐ろしく快適

evernote clipper 便利です。

あまりに便利だけど、人に言葉で説明するのが面倒なので、メモを残しておきます。

Evernote Clipper には、「簡易版の記事」があります。これがクッソ便利。

複数ページを自動で取得してくれる。

日経何某だとかのサイトでは、ページが無駄に分かれていて、この記事あとで参照しようとクリッピングしてもページ単位に分かれて不便なんですね。

それ、Evernoteクリッパーで!!

Evernoteクリッパーは「2ページ目」「3ページ目」を取得できます。

使い方は簡単:スクロールするだけ

複数のページに分割されたWEBページを「簡易版の記事」で表示して、スクロール&スクロール。

すると、残りのページが自動的に読み込まれて読みやすく「1枚のノート」にまとまってくれる。

f:id:takuya_1st:20151225021145p:plain

保存する前にSpace連打

保存する前にスペース連打してページダウンをする。

スクロールすると「次のページ」をロード

最終ページに到達するまでスペースキー連打しておけばよろしい。

ちょっと困るのは、自動で次ページロードをしないところ。

時々、忘れるので困ります・・

これで、気になった記事を帰りの通勤電車で読めますね。

勤務中に気になった記事を読んでると作業時間がなくなるんので助かります。

まぁもとはClearlyだろうし、当たり前と言えば当たり前ですけど。

はてなブックマークがこの機能提供すると最強だったよねぇ。(遠い目

参考

https://evernote.com/clearly/

https://evernote.com/intl/jp/webclipper/

IMAP経由やラベル一覧でGmailでラベルが見えない(SPAM/Draftとか)

API 経由でGmailにアクセスしたんですよ。

Gmailのアクセスを調べてて、試しに、SPAMをまとめて削除しようと思ってたらSPAMラベルが見えない

iphoneAndroid からGmailにアクセスしたら、フォルダやラベルが見えない。

Gmailはラベル一覧とIMAPへのエクスポートと、アクセス方法に応じて設定が異なる。

ラベル一覧は可視・不可視設定がある。

Gmailのラベルは可視・不可視が選べるので、むかしむかしに設定した不可視に邪魔されることがある

f:id:takuya_1st:20151225015704p:plain

というわけで。

Gmailでラベルが見えないときは設定を疑えということでした。

bash のシェルスクリプト(関数)でオプション引数を扱う getopts 使い方サンプル

シェルスクリプトで引数オプションを使いたい

my_shell_script -a /usr/bin

みたいに、bashシェルスクリプトでも引数を扱いたい。

getopts を使えば引数が取れる

bash では getopts を使えば、引数の処理を簡単に行うことができて便利。ruby などでは optparseに相当するやつです。

はじめに getopt / getopts の違いについて

全ての初めに。

getoptsについて読む前に知っておかなくちゃいけないことがある。

getopts コマンドとシェルのビルトインの二種類がある。

名前 種類
getopts シェル組み込み
getopt コマンド・ファイル

Google検索はgetopt/getopts を区別しないで結果を出すので注意が必要でした。

今回、調べたのは getopts です。

getopt は/usr/bin にあるコマンドです。

/usr/bin/getopt は、BSDOSX)とGNUlinux )で挙動が異なるため、OSXLinuxでどちらでも使えるようにするため、gnu bash の組み込み関数を使うことにしました。

getopts はシェル組み込み関数です。

今回扱うのは、getoptsシェル組み込み関数です。

getopts の基本的な使い方

getopts の基本的な使い方 は次の通りです。

getopts "引数名" 変数名
使用例

使い方は簡単です。

getopts "a" opts

引数に -a を指定したら opts 変数に中身(a)が入る

複数オプションを入れる

複数書きたいときは、連続してアルファベットを続ける

getopts "abc" opts

オプションで使いたいアルファベットを複数続けて書くだけ。楽チン。

実際に使って試してみる。

適当に関数を作ってgetopts を使ってみます。

function my_func(){
  echo 引数=$1
  getopts "adh" opts
  echo getopts結果=$opts
}

my_func -a
実行結果
引数=-a
getopts結果=a
未知の引数の場合
my_func -b
実行結果
引数=-a
/Users/takuya/Desktop/test.sh: illegal option -- b
getopts結果=?

何も書かなくても未指定の引数にはエラーを出してくれる。便利!

シェルの言語がja_JPであれば

takuya@~$ my_func -x
-bash: 不正なオプションです -- x

エラーメッセージをローカライズして表示してくれる。いいね

エラー処理を自分でやる?

組み込みのエラー処理はいらないと思う場合、

illegal option –を無視するには

先頭にセミコロンを置く
getopts ":adh" opts

こうすると、エラーを自分でハンドリングできる。

複数オプション の例

先ほどの関数に複数オプションをつけてみると。。。

my_func -a -d
引数=-a -d
getopts結果=a

複数オプションは考慮されない。 (後述するが、複数回getoptsを呼び出すことで処理できる。)

オプションを指定しない場合

getopts を設定した関数へ、なにもオプションを指定せずに起動すると・・・

my_func
引数=
getopts結果=?

結果は ? になる。

そのため、"?“ を判定してヘルプを表示するようスクリプトを記述する。

”?”(未知=ヘルプ)などと覚えておけばいいでしょう。

-a オプションではなく、ただの引数を入れる

ハイフン付きのオプションでななく、よくある引数を入れてみるとどうなるか。

my_func aaaaaaaaa
引数=aaaaaaaaa
getopts結果=?

引数がない時と同じになる。

オプション引数を扱う。

オプション付き引数を入れる。つまり、オプションで引数を入れてみる。

my_func -a my_value

このように、設定値付きのオプションを使う、オプション引数を使う。

“a:” 文字+”:”でオプション引数

オプションに引数をつけるには、getopts で「:」を追加する

引数が一つのとき
getopts "a:" OPT
引数が二つのとき
getopts "a:b:c:" OPT
引数が三つのとき
getopts "a:b:c:" OPT
オプション付き引数を処理しない場合

オプション付き引数を処理しない場合は連続して文字を続けるだけ

getopts "abcdefg" OPT

オプション引数のサンプル

シェルスクリプトは次のように記述した $OPTARG にオプション引数が入る点が違う。

function my_func(){

  echo 引数=$@
  getopts ":a:" opts
  echo getopts結果=$opts
  echo $OPTARG

}
my_func -a aaaaaaaaa

実行結果は次の通り

引数=-a aaaaaaaaa
getopts結果=a
aaaaaaaaa

ポイントは、$OPTARG にオプションの引数が入る

複数オプションを使う

ここまでで、基本的な使い方がわかったので、実際にありそうなオプションを取り扱ってみたいと思う。

そのままでは複数オプションを扱えないので、複数回 getopts を使う。

複数回の getopts 呼び出し

先ほど、複数個のオプションを処理できないと書きましたが、getoptsを複数回呼び出せば、複数個のオプションを処理できます。

function my_func(){

  echo args=$@
  getopts ":a:b:" OPT
  echo $OPT
  echo $OPTARG

  getopts ":a:b:" OPT
  echo $OPT
  echo $OPTARG

}
my_func -a aaaaaaaaa -b bbbbbbbbbbbbb

このようにgetoptsを何回も処理したら

実行結果が次のようになる。

args=-a aaaaaaaaa -b bbbbbbbbbbbbb
a
aaaaaaaaa
b
bbbbbbbbbbbbb

ここでのポイントは。複数回getoptsを適用すれば、複数個のオプションに対応できる。という点

複数オプションをループで処理する

ここまできて、漸くネットで見かけるサンプルに近くなる。

function my_func(){

  echo args=$@

  while getopts ":a:b:" OPT ; do

    echo $OPT
    echo $OPTARG

  done

}
my_func -a aaaaaaaaa -b bbbbbbbbbbbbb

複数オプションを取得するには、なくなるまでwhileループを回すのが基本

実行結果
args=-a aaaaaaaaa -b bbbbbbbbbbbbb
a
aaaaaaaaa
b
bbbbbbbbbbbbb

while で処理するときは caseとともに使う。

function my_func(){

  echo args=$@

  while getopts ":a:b:" OPT ; do
    case $OPT
      a)  echo $OPT
           echo $OPTARG
           ;;
      b)  echo $OPT
           echo $OPTARG
           ;;
     esac
  done

}
my_func -a aaaaaaaaa -b bbbbbbbbbbbbb

これで、getopts を使える。

getopts と while + case を組み合わせて戦うことで、シェルスクリプトでもオプションと引数つきオプションを扱えることがわかった。

ただし、whileループで処理した場合引数なしには対応できない

while ループを記述したコマンドを オプションなしで起動すると・・・

my_func aaa
args=aaa
(getopts のwhile ループでは処理されない

引数が指定されてない時に注意が必要

ナニも引数が指定されない場合は、getoptsはシゴトをするが、exit(1) するのでwhile と相性が悪い

while に入れた時には、結果は空っぽになる。

“?” はどこいった?

ただし、"?“ はきちんと処理されている。 while が処理終了になるので、while 中の case に処理がこないだけ。

function my_func(){

  echo args=$@

  getopts ":a:b:" OPT
  echo $?
  echo opt=$OPT
}
my_func -a hoge
my_func
結果
args=-a hoge
0
opt=a
args=
1
opt=?

OPTには ? が入るが、コマンドの実行結果ステータスが 1 になるので whileから抜けてしまう。

なので、while getopt でオプションが全く指定されない場合の ?をwhile中で検出することが出来ない。

ここまでのまとめ

ここまでの実験でわかったことをまとめた。

  • getoptsはオプション・引数付きオプションを処理することができる。
  • getoptsは複数回起動すると複数のオプションを処理できる
  • getoptsは while と case 句を組み合わせて処理をする。
function my_func(){

  echo args=$@

  while getopts ":a:b:" OPT ; do

    case $OPT in
      a)
        echo -a の引数 $OPTARG
        ;;
      b)
        echo -b の引数 $OPTARG
        ;;
      : )
        echo "ここ"
        ;;
      \? )
        echo nothing matched
        ;;
    esac

  done

}
my_func  -a aaa
my_func
my_func -c ccc

while とともに使った場合

検出ができるのは、「不正なオプション」を渡した時に限られる、「オプションが全く未指定」は検出ができない。別途考えないとダメなようだ

注意: getoptsをfunction内で利用す場合

getopts 複数回よびだせる、そのときにどこまで処理したかを保存する。

どこまで処理したかは、変数 $OPTIND(オプションインデックス)に保存する。

そのため、getopts は OPTINDを使うので、function で使うときは、OPTIND をグローバル汚染させない注意が必要

function path() {

 local OPTIND OPTARG OPT ## ここ重要

  while getopts 'ha:d:' OPT ; do
    case $OPT in
      d)
        echo "-D entered"
        ;;
      a)
        echo "-A entered"
        ;;
      \?)
        ;;
    esac
  done
}

local OPTIND を書かないと、グローバル変数に値が入っちゃうので、getopts が含まれる関数を何度も呼び出せない。

local 宣言があるとき

function 内に local 宣言をちゃんと書いた場合・・・

function path() {

 local OPTIND OPTARG OPT

  while getopts 'ha:d:' OPT ; do
    case $OPT in
      a)
        echo "-A entered"
        ;;
    esac
  done
  echo end
}
### ここで関数の実行
path -a aaa
path -a aaa
path -a aaa

実行と結果

-A entered
end
-A entered
end
-A entered
end

local 宣言がないとき。

function path() {
# local 宣言をコメントアウト
#local OPTIND OPTARG OPT 

  while getopts 'ha:d:' OPT ; do
    case $OPT in
      a)
        echo "-A entered"
        ;;
    esac
  done
  echo end
}
path -a aaa
path -a aaa
path -a aaa

実行結果

-A entered
end
end
end

このようにlocal宣言がないと変数$OPTIND が作用して、オプションを取れなくなる。

1つのファイルでシェルスクリプトを記述するのなら問題ないのだけれど、関数を作って連続して使うなら変数を共有しない工夫が必要

getoptsをファイルで使う場合

getoptsをファイルで使う場合は、環境変数をexport しない限りファイル内部で完結するので、管理は難しくない。

my_script.sh
#!/usr/bin/env bash
  while getopts 'ha:d:' OPT ; do
    case $OPT in
      a)
        echo "-A entered"
        ;;
    esac
  done
  echo end

ファイルに記述した場合

my_script.sh -a aaaa
my_script.sh -a aaaa
my_script.sh -a aaaa

としてもOPTINDはファイルごとに確保されるため全然影響がなく、問題ない。

getopts まとめ

  • getopts はシェル組み込み関数(builtinコマンド)
  • getopts “abc” とかけば -a -b -c とオプションを作れる
  • 複数個のオプションを一度に処理するには getoptsを複数回呼び出し
  • 複数個のオプションを処理するには while + case を使う
  • オプション引数を使うには getopts “a:b:c"とする
  • エラー処理を自分でやるには getopts “:a” と先頭に「 : 」をつける
  • 関数内で getopts を使うときは local OPT をつける

参考資料

http://stackoverflow.com/questions/16654607/using-getopts-inside-a-bash-function

au-Wifi の提供がスマホに限定される?

au Wi-Fi SPOTマルチデバイスサービス」がリニューアル してた

ご利用方法と対象機種の変更に伴い、これまで提供してきた専用アプリ(au Wi-Fi接続ツール)では2016年2月1日よりマルチデバイスサービスをご利用いただけなくなります。

え、ちょっとまてよ。いまで、PCから無料で使えたのが使えなくなるってことですかね。

PCから au-Wifi つながらない阿鼻叫喚が待ってる。

ちょっとまだ追いきれてないんだけど

www.au.kddi.com

いままでは無料でWiFi接続ツールで使えるようにしたけど、これからはWi2を使ってくれヨロシクってことですね。

影響のある料金プラン

これらのプランでは、引き続き無料(ただし新規加入・変更は出来ない) - LTEフラット - LTEフラット(V)

通常はこれからは月額300円のプランが標準になる。

  • データ定額2/3/5/8/10/13
  • データ定額2(V)/3(V)/5(V)/8(V)/10(V)/13(V)
  • データ定額2(VK)/3(VK)/5(VK)/8(VK)/10(VK)/13(VK)
  • LTEフラット cp(f・2GB)
  • LTEフラット cp(1GB)
  • ジュニアスマートフォンプラン
  • シニアプラン
  • シニアプラン(V)

www.au.kddi.com

ということです。

影響のあるSSID

これからのプランでは次のSSIDが有料になる。

  • Wi2
  • Wi2_club
  • Wi2_free
  • wifi_square
  • Wi2premium
  • Wi2premium_club
  • UQ_Wi-Fi

ご利用条件|公衆無線LANサービスWi2 300

今まで使えたSSID

au_Wi-Fi」(WPA2)、「au_Wi-Fi2」(WPA2エンタープライズ)、「Wi2premium_club」(WPA2)、「Wi2_club」(WPA2)、「UQ_Wi-Fi」(WEP)、「Wi2premium」(暗号化なし)、「Wi2」(暗号化なし)、「wifi_square」(暗号化なし)

つまり、iOS/Android以外をau-WiFiから追い出すってことですね。

au_Wif2 がEAP-SIMだから、au-Wifiは消えていくんでしょうか。

まぁ速く言えば

いままでPC向けもau-wifi で提供したけど、スマホ以外は、wi2に丸投げするからよろしく。ってことですね。

これからはPCなどでWifi使いたきゃ300円(税抜)払え、Wi2よりは50円ほど安いぞってことですね。

au は良心的?

猶予期間を1年位あけてるのと、残存旧プランへの救済措置を作ってるのは、やっぱりau は良心的なキャリアだなぁと思うわけです。

こうやって徐々に値上げされていくんですね。

WiMAX1まきとりで、使えないau_WiFiを巻き取りつつ、インフラ更新をwi2に丸投げして、自社はau_WiFi2でSIM認証でガードかけていくのかな

5G見据えたらwifiはイラないってことなんでしょうねぇ。

curl+xpath から始めるお手軽スクレイピング(2)

この記事は前回の続きです。

curl とxpath でお手軽スクレイピング

前回までで、xpath + curl + cookie を使いました。

xpath はとても便利なので、基本的な使い方を再掲しておきます。(以前にまとめたもののですが)

xpath 内容
//* 全てのノード
//a 全ての<a> ノード
(//a)[1] 全ての<a> ノードを取得して、最初の1個
(//a)[2] 全ての<a> ノードを取得して、2番め(配列アクセス)
(//a[1]) 親ノード中の最初の1個の<a>をすべて
//a/span span ノードで、親が<a>のものをすべて
//a/@href aノードのすべてのhref属性
//a/text() aノードのすべてのtext()表現
//a[@href="/index.html"] aノードのうち href属性が"/index.html"と合致するモノすべて
//a[contains(@href,"index.html")] aノードのうち href属性に index.html を含むものすべて
//title | //meta title と meta タグを両方
//img[ contains(@src,'jpg') or contains(@src,'png')] img ノードで src に jpg/pngを含むものすべて
//div[ contains(@class,'link') and contains(@class,'book')] div.link.bookに相当するもの
//form[ ./input[name="username"] ] 子ノードに //input[name="username"]を持つform ノード
//div[@id=main]//form form ノードで、親が div[@id=main] の物をすべて
//div/* div の子要素すべて
//div//* div の子孫要素をすべて
//table//td[2] table タグで2番めのtdのもの(2列目をすべて)
//*[@id] id属性があるものをすべて
id("tid_123") id属性がtid_123のもの(id="tid_123")

xpath の練習

このページに含まれる a 要素を列挙する

curl -s  http://takuya-1st.hatenablog.jp/ | xpath "//a/@href"

このページに含まれる title と meta を取り出す。

curl -s  http://takuya-1st.hatenablog.jp/ | xpath "//title | //meta"

このページに含まれるform で2番目のものを取り出す。

curl -s  http://takuya-1st.hatenablog.jp/ | xpath "(//form)[2]"

curl + xpath + md5sum で更新チェック

ページの更新チェックを、要素を内部HTMLの変化として考えて、要素の変化を追いかけて、更新チェックをする

url="http://localhost/"
xpath_exp="(//div[contains(@class, 'main')])[2]"
digest=`curl -s  $url | xpath $xpath_exp 2>/dev/null | md5sum `


while true ;  do
  current=`curl -s  $url | xpath $xpath_exp 2>/dev/null | md5sum `
  if [[ $digest != $current ]] ; then
    echo changed!!
    sendmail ほげほげ
    digest=$current
  fi
  sleep 1
done

このように、ページの更新をcurlxpath で確実に追いかけることができます。

curl で連続ページ取得

curl でも、連続したページの取得ができます。それが --next オプションです。

--next によるフォーム送信からのデータ取得

next を使うことで、通常のクローラーを描くような動作をcurl にも行わせることができます。

以下の例は、pitapa.com にログインして、トップページへ遷移している例です。

curl -v -k  -c  pitapa.cookie.yml -F id=takuyaXXX -F password=XXXXX \
  https://www2.pitapa.com/member/login.do\
 --next -k -c  pitapa.cookie.yml    https://www2.pitapa.com/member/top.do

next は続けていくつでもかけます。便利!

もっとまとめてデータを取得したい

URLの一覧を列挙して、バンバンアクセスして取得することができます。それが --config オプション

page.conf ファイル

アクセスしたいURLを列挙してページを取得に行きます。

url="http://www.yahoo.co.jp/"
output="yahoo.html"

url="https://qiita.com"
output="qiita.com.html"

url="http://b.hatena.ne.jp/"
output="hatebu.html"

ページの一覧はxpath で作っておけばいいと思います。

連続取得

curl に--config/-K オプションをつけると連続してデータ取得をバッチ処理してくれます。

curl -s -K page.conf 

これで、xpath で作成したアクセスURL一覧へどんどんアクセスすることが可能になります。便利

user-agent なども指定できる

config の名前の通り、 curl に渡すコマンドオプションも記述できます。

user-agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.48 Safari/537.36"

url="http://www.yahoo.co.jp/"
output="1.html"
url="http://www.yahoo.co.jp/"
output="2.html"
url="http://www.yahoo.co.jp/"
output="3.html"
url="http://www.yahoo.co.jp/"
output="4.html"
url="http://www.yahoo.co.jp/"
output="5.html"
url="http://www.yahoo.co.jp/"
output="6.html"
url="http://www.yahoo.co.jp/"
output="7.html"

libcurl オプション

curl コマンドからしか使えないってこともないです。C言語のソースも吐いてくれます。

curl --libcurl get_urls.c -s -k -K curl.conf

こうすると、get_urls.c が生成されて

/********* Sample code generated by the curl command line tool **********
 * All curl_easy_setopt() options are documented at:
 * http://curl.haxx.se/libcurl/c/curl_easy_setopt.html
 ************************************************************************/
#include <curl/curl.h>

int main(int argc, char *argv[])
{
  CURLcode ret;
  CURL *hnd;

  hnd = curl_easy_init();
  curl_easy_setopt(hnd, CURLOPT_URL, "http://www.yahoo.co.jp/");
  curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 1L);
  curl_easy_setopt(hnd, CURLOPT_USERAGENT, "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.48 Safari/537.36");
  curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 50L);
  curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L);
  curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 0L);
  curl_easy_setopt(hnd, CURLOPT_TCP_KEEPALIVE, 1L);
(以下略

よく使うアクセスパターンをC言語コンパイルでコマンド化することが可能になります。楽しい。

まとめ

curl って便利なので、スクレーパーを作る際に大変重宝します。

xpath 参考資料

http://yakinikunotare.boo.jp/orebase/index.php?XML%2FXPath%2FXPath%A4%CE%BD%F1%A4%AD%CA%FD

たのしいXML: XPath(基礎編)

https://sites.google.com/site/shin1ogawa/xsl/xpath

http://os0x.g.hatena.ne.jp/os0x/20080620/1213987223

find で指定のフォルダを除外するには

find コマンド結果多すぎて除外したいフォルダがありませんか?

find で除外したものといえば、.git や ruby だったら bundle/vendor など、find の結果から除外するには

find の -not オプションを使います

find は「検索条件」を列挙するタイプのコマンドなので、検索条件に -not をつけて否定形にすればいいわけです。

find . -not -iwholename '除外したい名前'
.git を無視する例。

一番使いそうなのがこれ。.git

find . -not -iwholename '*/.git/*'

-name と -iwholename とは?

find のオプションで、 -name と -iwholename は少し違う

  • -name : 自身のファイル名にマッチ
  • -wholename : 自身のファイルのパス名にマッチ
  • -iwholename : wholename のCaseInsensitive版

たとえば 、vendor/bundleについて -name / -iwholename でマッチすると

次のような結果になる。

名前 オプション 結果
vendor -name 'vendor' マッチする
vendor/bundle -name 'vendor' ×
vendor -iwholename 'vendor' マッチする
vendor/bundle -iwholename 'vendor' マッチする

のように検索結果のパス名(ディレクトリ名)にマッチさせることが出来る

-not -iwholename の意味

-iwholenameで .git をパス名に持つのをマッチさせて、それを -not で否定形にしている。

理屈がわかれば便利ですね

alias できない困った問題

.git を常に無視したいと思っても、find コマンドの性質上、検索パスは絞込条件の前に設置する必要がある。

find path -検索条件 -検索条件 -検索条件 #出来る
find  -検索条件 -検索条件 -検索条件  path # できない

そのために、alias をつけておくことが出来ない。

alias find="find $@ -not -name ss" # 出来ない

どうしてもやりたければfunctionを書くしか無いです。

function find { $( which find ) "$@"  -not -iwholename '*/.git/*'  ; }

関数書くしか解決策がないけども、function 使うのが手軽でベターな解決方法。

参考資料

http://stackoverflow.com/questions/2314643/how-can-i-get-find-to-ignore-svn-directories

Chrome(OSX)のパスワード・クッキーを取り出すコマンド作った

Mac OSX のChromeのパスワードを取り出すツールを書いた。

takuya/chrome-storage · GitHub

なぜ書いたのか

OSXChromeが、Keychains.app に同期しなくなったので、インポートするために仕方なく書きました。

いままではKeychainを見ればパスワードを回収できたのですが、OperaSafariFirefoxChromeがそれぞれ別管理になって、別々にアカウント同期でパスワードを持っていくのですが、どれが新しくてドレが古いか全くわからなくなりそうでした。

Chromeの管理からパスワード取り出せば良いのですが、chrome://settings/passwords のパスワードチェックがめんどくさい。ちょっと視認性の悪いUIですよね。

いままではローカルKeychainにパスワードつくってiCloudに移動すればよかったのに・・・Windowsの悪夢に逆戻りだ・・・

Opera/chrome のクッキーとパスワードのデコード

Chromium のパスワードは $PROFILE_PATH/Login Dataのファイルに保存されています。ファイルはSqlite3のデータベースになっています。そのファイルの中身でパスワードは暗号化されて保存されています。

暗号化されたパスワードは、Keychain.app にあるsafe storageに保存された文字列で復号化出来ます。

ダウンロード先

github.com

safe storage が幾つかある。

Opera(stable/beta) とChromeがそれぞれ使っています。

ここに保存されたパスフレーズでブラウザ中の暗号化データをデコード出来るようになっています。

f:id:takuya_1st:20151216034018p:plain:w600

暗号化・復号化処理も含めたブラウザのソースコードは公開されてるので、わたしたちはブラウザから平文でパスワードを得ることが可能になります。これからも安心して使える。ソース公開って素晴らしい

Firefoxはどうしてるんだろう・・・

パスワード・クッキーのデータベースの構造

パスワードとクッキーのデータベースは、次の通りのテーブル構造に格納されていました。

パスワードテーブル
CREATE TABLE   "logins"     (
    origin_url  VARCHAR    NOT   NULL    ,
        action_url  VARCHAR    ,
        username_element    VARCHAR    ,
        username_value  VARCHAR    ,
        password_element    VARCHAR    ,
        password_value  BLOB    ,
        submit_element  VARCHAR    ,
        signon_realm    VARCHAR    NOT   NULL    ,
    ssl_valid   INTEGER    NOT   NULL    ,
    preferred   INTEGER    NOT   NULL    ,
    date_created    INTEGER    NOT   NULL    ,
    blacklisted_by_user INTEGER    NOT   NULL    ,
    scheme  INTEGER    NOT   NULL    ,
    password_type   INTEGER    ,
    possible_usernames  BLOB    ,
    times_used  INTEGER    ,
    form_data   BLOB    ,
    date_synced INTEGER    ,
    display_name    VARCHAR    ,
    icon_url    VARCHAR    ,
    federation_url  VARCHAR    ,
    skip_zero_click INTEGER    ,
    generation_upload_status    INTEGER    ,
    UNIQUE      (
    origin_url  ,
        username_element    ,
        username_value  ,
        password_element    ,
        signon_realm    )
        )
        ;
クッキーテーブル
CREATE TABLE   cookies     (
    creation_utc    INTEGER    NOT   NULL    UNIQUE  PRIMARY KEY ,
    host_key    TEXT    NOT   NULL    ,
    name    TEXT    NOT   NULL    ,
    value   TEXT    NOT   NULL    ,
    path    TEXT    NOT   NULL    ,
    expires_utc INTEGER    NOT   NULL    ,
    secure  INTEGER    NOT   NULL    ,
    httponly    INTEGER    NOT   NULL    ,
    last_access_utc INTEGER    NOT   NULL    ,
        has_expires INTEGER    NOT   NULL    DEFAULT 1  ,
        persistent  INTEGER    NOT   NULL    DEFAULT 1  ,
    priority    INTEGER    NOT   NULL    DEFAULT 1  ,
    encrypted_value BLOB    DEFAULT '' ,
    firstpartyonly  INTEGER    NOT   NULL    DEFAULT 0  )
        ;

データベースからデータを取り出して、復号化

復号化はそんなに難しいものではなく、WEBを探したら色々出てきます。WindowsWin32を使っていて、linux/osxはキーチェンに保存したパスフレーズとhmacを使ったOpensslを使っているようだ

次の例は、ruby で復号化を試みた箇所です。 引数 passの部分は、Keychainから拾ってきます。

    def decrypt(encrypted_value, pass ) 
      iter = 1003
      key_len = 16
      salt = "saltysalt"
      iv = " " * 16

      return if encrypted_value.size < 3

      key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(pass, salt, iter, key_len)

      cipher = OpenSSL::Cipher::AES.new(128,:CBC)
      cipher.decrypt
      cipher.iv = iv 
      cipher.key = key

      data = encrypted_value
      data = data[3,data.size]

      return cipher.update(data) + cipher.final
    end

取り出したID/PASSはKeychainに

security コマンド を使うことで、パスワードをKeychainに書き込むことが出来ます

 security add-internet-password -a ユーザー名 -s サーバー -w パス   -p 対象のフルURL  -U

これで、しばらくは生き延びられそうです。

関連記事

ChromeのCookieをプログラムから使う - それマグで!

chrome のCookieを取り出す。(暗号化Cookieの複合化でデコード済テキストを取り出す) - それマグで!

MechanizeでChromeのCookieを扱って面倒なログインを飛ばす - それマグで!

httpOnlyなCookieとは? - それマグで!

Chrome のパスワードがOSX Keychains に保存・同期しなくなった - それマグで!