それマグで!

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

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

tailf と grep を組合せて Slackにログを通知する。パイプでワンライナー

ログファイルを監視して、slack などに通知したい。

ログを監視して Slackに投げる方法で、かんたんなのを考えた。

ログファイルに何か変更があって、それが特定のときに メールや slack のチャンネルに通知を出したら便利だと思う。

あれこれログ監視や通知のソフトウェアやパッケージがあるけれど、一番大事なのは手軽で何も知らなくても使えることだと思う。

ファイルを監視して grep して HTTP POST するだけなら、パイプで十分に使える。

ログ監視の仕組み

要は、次のコマンドパイプを呼び出せればいい。ただしいくつかポイントが有るので、それを見ておく

目指す形

このエントリでは次のようなコマンドを作る

tail -f path/to/file | grep some_thing |  curl -X POST url

このままでは動かない。

コマンドの組合せならすぐ思いつきそうなパイプですが、単純にこのコマンドだけでは動きません。tai や grepcurl コマンドの特性上、このままではパイプで動かない。すこし変更が必要でした。

コマンドの問題点

tailf / tail -f ではログがローテーションしたときにOpenしたファイルを追尾しない。新しいログファイルを開いてくれない。そのため、このままでは動きませんでした。

grepは、そのままでは次のパイプがリアルタイムに起動しませんでした。

curl コマンドは、この書き方すると標準入力行を全部受け取ってしまいます。

これらを防ぐために若干のコツというか、オプションを調べて見直したので、まとめておきます。

tail -F でパスを監視する

tail コマンドには -F というオプションがあり、これで該当PATHのファイル監視を出来る。 もし tail -fで監視していると、ファイルが削除・移動後に追跡できない。そのため、tail を使った長期間ログ監視なら、次のコマンドが望ましい。

ログ監視の一番かんたんなコマンド

tail -F logs/lastest.log

追加された行だけを取得する

tail を起動すると、最終5行を表示して -f : follow モードに入る。そのため起動すると過去ログが何度も通知されるのが望ましくない。

tail -n0 -F logs/lastest.log

tail と grep の組合せ

tail -n0 -F logs/lastest.log | grep takuya

tail -f とコマンドの組合せが便利。

パイプ先で必要情報だけを取出して、残りを捨てたフィルリングや書き換えが出来る。

tail と grep を組合せれば、ログをフィルタできる。また-f / -Fをつけたtail は、パイプがブロックIOになるので、ログが更新にリアルタイムで追従してくれる。

tail | grepにさらにパイプをつなぐ

### リアルタイム動作しない
tail -n0 -F logs/lastest.log | grep  takuya  | grep login

tail | grep は正しくフィルタするが、 tail | grep | grep は画面出力が得られない。 なぜか?。

grep tail -f の組合せは、grep パイプ後パイプが動かない時がある。これはgrep がバッファリングしてしまう事が原因です。

実は grep が内部的にバッファリングして、高速化してくれる。そのため tail -f | grep | grepでは、中間のgrepがIOをバッファリングして、最後のgrepに即時にログが届かない。

line-buffered のオプション

次のオプションで、複数行バッファリングを一時停止(行単位で出力バッファ)するようにする。

man grep
      --line-buffered
              Use line buffering on output.  This can cause a performance penalty.

tail | grep | grep するときは次のようにする。

そのため tailf を grep して grep したり、sed したりする場合は、 次のようにする。

tail -F logs/lastest.log | grep --line-buffered takuya  | grep --line-buffered  login

ログを受け取ったら、何かする。

行単位でログがやってくる。

行単位で受け取ったらPOST処理をするとしたら、curl でPOSTしようとすると、行を標準入出力から受け取る必要がある.

出来ない

grep something | curl -X POST -d - ... 

grep した結果をcurl で送信しようとしても、curl で標準入出力をパイプで扱うと、1度しか起動できない。行毎に起動するには xargs を使う

xargs で行が来るたびに送信する。

 grep --line-buffered takuya  | xargs -I@ curl -X POST http://example.com/ -d message="@"

xargs を -I で1行単位で起動するようにして、@` という文字で指定して、1行をコマンドの文字列に展開する。

slack のURLを用意する

Slack にはPOST用の webhook が用意されていて、最近はチャンネル毎にPOST用のWebhook のURLを生成できる 生成したURLは次のように、ディレクトリが3段になっていた (2017/05/09)

https://hooks.slack.com/services/Axxx/Bxxx/Cxxx

ここに curl で post / json すれば、 Slack に拾われる。

curl -s  \
  https://hooks.slack.com/services/Axxx/Bxxx/Cxxx \
   -X POST \ 
   -H 'Content-type: application/json' \
    --data '{"username":"curl", "text":"#{my message}"}'

パイプにまとめる。

ここまで見てきたそれぞれのコマンドとパイプを全部まとめると、次のようになる。

tail -n0 -F logs/lastest.log | grep --line-buffered takuya  |  xargs -I @ curl -s  \
  https://hooks.slack.com/services/Axxx/Bxxx/Cxxx \
   -X POST \ 
   -H 'Content-type: application/json' \
    --data '{"username":"curl", "text":"@"}'

これで、ログが更新されたら、 grep で フィルタをかけ、条件にあったログが来たら Slack にPOSTすることが出来る。

さらにコレを関数か、シェルスクリプトファイルにする

このままでは、ファイルの指定とエンドポイントのPOSTのURLが変更が不便なのでシェルスクリプトにしておくと便利だと思います。

さらに、systemd に登録

これらのコマンド(シェルスクリプト)をsystemd.unitの service に登録して、Daemonと同時に起動しておけば、だいぶ楽だよね。

マインクラフトのログを監視する

たとえば、マインクラフトサーバーで「死んだ」人をSlackに晒し上げるには

ログファイルが lastest.log で、grep で slain を取れば死んだことが解り、最後にSlackで通知したら出来上がり。

tail -n0 -F logs/lastest.log | \
grep --line-buffered slain  | \
xargs -I @ curl -s  \
  https://hooks.slack.com/services/Axxx/Bxxx/Cxxx \
   -X POST \ 
   -H 'Content-type: application/json' \
    --data '{"username":"minecraft-serverl", "text":"@"}'

コマンドで出来てるので、たとえば、死んだ回数や死因を累計とか、ログイン・ログアウトを監視してプレイ時間を通知するとかね。

まとめ

tail と grepcurl だけで、ログを監視して通知することが出来ます。

tail -F で ファイルのパスの状態を監視できます

grep –line-buffered で行ごとにgrep をすることが出来ます。

xargs curl で行の内容をPOSTすることが出来ます。

個人的な

ちょっとしたログ監視を調べたら、ちょっとね・・・

「最強の○○のログ監視」と「ログ監視パッケージをdocker コンテナ」を導入してる 某キ◯タさんの意識高すぎるエントリに辟易して調べました。

開発とかちょっとしたことにわざわざDockerコンテナだのAWSとか、ちょっと重たすぎだよね。手順やリソースが多すぎる。

ログ監視ってのは単一のログファイルを見て何かするのと、「複数サーバー(インスタンス)」のログを一箇所に集めて何かするのとだいぶ違うと思うんだよね。

Slackに投稿したり、ログを投げたり、まとめたりっていろいろな方法がある。パッケージを入れてしまえばたしかにかんたんなのですが。後始末が面倒になる。後始末が面倒になってたくさんのインスタンスを使い潰したりコンテナから起動したりで、時間や学習コストは抑えられるのですが。金銭的に不利だったり、学習コストを抑えたために、応用が効かなかったり。。。うーんって思うことも有る。

google chrome が google アカウントのパスワードを補完(入力しない問題

Google Chrome がパスワードを利用させてくれない

ChromeGoogleアカウントでログインしてる場合、そのアカウントのパスワードは補完されない。

そのために、Chromeでパスワードを何度も入力することになり面倒くさい。

原因と理由

ちょっと実験してみた。

Chromeブラウザにログインしてるアカウントのパスワードを「バックアップ同期」している場合に起きる。つまり、Chromeブラウザからログアウトするとパスワード補完は復活する。

なぜこの仕様、これが鬱陶しく面倒になるのか?ここ最近のGoogleアカウントはパスワードの再確認が頻繁に起きるようになった。いぜんは手軽にアカウントを切り替えなどが出来たのに。最近はアカウントを切り替えることも面倒くさい。

解決策1

Chromeをログインせずに使う。Chromeにログインせずに利用すれば、いかなるGoogle アカウントでもパスワード自動入力が動く。

解決策2

Chrome にログインしてる場合、パスワードの同期を外す。

これはChromeにログインして、拡張機能やブックマークやセッションなどをバックアップ同期したい場合に有効

[f:id:takuya_1st:20170507184544p:plain:300]

解決策3

Chromeにログインし同期する専用のアカウントを作る。

GmailやOAuthのログイン連携で常用するアカウントと、Chromeにログインしてバックアップを取るアカウントを分けてしまう。

参考資料

https://productforums.google.com/forum/#!msg/chrome/xjXc0i-bqNs/-js6NHZACAAJ

素因数分解をしてくれるコマンド factor

prime numbers ( 素数 ) に分解してくれるコマンドがあった。

takuya@Desktop$ factor 123456
123456: 2 2 2 2 2 2 3 643

factor とは 要因の意味で数学的に因数で、factor コマンドは 素数を使って因数を表現してくれるコマンド

何に使うのか。

特に何に使うわけでもないけれど、コマンド呼び出しやexec 系やパイプの実験のサンプルに丁度いいと思います。

takuya@Desktop$ irb
>> exec 'factor 10'
10: 2 5

標準入出力を使った例

コマンドはブロッキングIOで逐次処理されるので、IOを使った実験に向いている。

takuya@Desktop$ factor
43
43: 43
22
22: 2 11
11
11: 11
1111
1111: 11 101

このようにコマンドの実行サンプルの例として使うのに向いてるのかもしれない。

何処に入ってるのか?

coreutils に入っています。

mac なら 次のようにするとインストール出来ます。

brew install coreutils

シェルスクリプトの改行コードを修正する

改行コードが原因でエラーになる。

改行コードを \r\n が原因でエラーになった。

takuya@:~$ bash -n apt
apt: 行 2: 予期しないトークン `$'\r'' 周辺に構文エラーがあります
'pt: 行 2: `_apt()

一瞬何のエラーかわからなかったけどよく見るとエラーに改行文字が含まれてた。

改行コードを修正するコマンド dos2unix

インストール

sudo apt install dos2unix

実行

dos2unix an.sh

とても便利ですね

似たような仲間には

  • /usr/bin/dos2unix
  • /usr/bin/unix2dos
  • /usr/bin/unix2mac
  • /usr/bin/mac2unix

などがあります。CRLF : dos/windows 、 LF : UNIX / bsd / linux , OS X(macOS) , CR : Mac OS(9) などですね。

過去資料

改行コードを置換する専用コマンド - それマグで!

標準出力と標準エラーのリダイレクト(コピー)について

標準出力と標準エラー出力を扱うときに

たまに間違って 1 というファイルが出来てしまって悲しいことになる。

takuya@Desktop$ ll 1
-rw-r--r-- 1 takuya staff 20 2017-04-27 14:51 1

これを防ぐためにちょっと標準出力と標準エラーの扱いをまとめておこうと思う。

標準出力とエラーの出力先の転送(コピー)について

最初に、エラーと出力をするかんたんなプログラムを用意しておきます。

cmd.rb

$stdout.puts "to stdout"
$stderr.puts "to stderr"

普通に実行した場合

takuya@~$ ruby cmd.rb
to stdout
to stderr

エラー出力の転送先を指定する。

takuya@~$ ruby cmd.rb 2>/dev/null
to stdout

標準出力の転送先を指定する。

takuya@~$ ruby cmd.rb 1>/dev/null
to stderr

両方の出力先を指定する。

takuya@~$ ruby cmd.rb 1> stdout.log 2>stderr.log
takuya@~$

画面には何も出ない。

ここまでのおさらいとさらなること

なんとなくわかってるようで、ここまでをシッカリ覚えた上で、どうなってるかをもう少し突っ込んでみてみないと失敗する。

プロセスの実行は、STDOUT/STDERRをそれぞれ繋いで(ファイルディスクリプタのコピー)になることを覚えておく。左が変数名、右が変数の中身(FDが指している先)

cmd.rb の fd 中身(出力先)
1 stdout
2 stderr

エラー出力を /dev/null に

エラー出力を捨てた場合。

takuya@~$ ruby cmd.rb 2>/dev/null
fd 中身(出力先)
1 stdout
2 /dev/null

上記の様に考えると、次からの転送をもう少し理解できる。

標準エラー出力と標準出力を同じにする

エラーと標準出力を合流させる。

takuya@~$ ruby cmd.rb 2>&1
to stdout
to stderr

これはこう考えると良い

fd 中身(出力先)
1 stdout
2 stdout

fd2 に fd1 の内容がコピー(同じに)された。

標準エラーを合流させて、標準出力をファイルへ

takuya@~$ ruby cmd.rb 2>&1 1>/dev/null
to stderr

これは、次のように一つずつの処理が2回動作している。そう考えると理解できる。

fd 中身(出力先)
1 stdout
2 stdout stderr

&1 で 1が指している stdout を2へコピーしている。

fd 中身(出力先)
1 /dev/null stdout
2 stdout

1 が指している内容を /dev/null に書き換えた

標準出力をファイルに出して2つを合流させる

takuya@~$ ruby cmd.rb > /dev/null 2>&1
takuya@~$

これは、次のように一つずつ、処理が2回動作していた。

そう考えると理解しやすい

1個めの処理

fd 中身(出力先)
1 /dev/null stdout
2 stderr

2個めの処理

1 が指している内容を /dev/null に書き換える

fd 中身(出力先)
1 /dev/null
2 /dev/null stderr

&1 で 1が指している内容(/dev/null) を2へコピーしている。

出力先を入れ替える.。

よくあるお題の、出力先の入れ替えも見てみる。

標準エラー出力と標準出力を入れ替えるには?」をここまでの表で見ておくことにする。

この状態を

fd 中身(出力先)
1 stdout
2 stderr

こうしたい

fd 中身(出力先)
1 stderr
2 stdout

つまりコピーしてしまうと。。。

takuya@~$ ruby cmd.rb  2>&1 1>&2
to stdout
to stderr
最初の指定 2>&1 でこうなり
fd 中身(出力先)
1 stdout
2 stdout stderr
2番目の指定 1>&2 でこうなる
fd 中身(出力先)
1 stdoutstdout
2 stdout

あぅ、、、だめじゃんおなじになっちゃう。

コピーするなら、一旦tmp を経由する。

追記コピーのようなことは出来ないので、いったん別の場所に対比しておく

任意のfd へ

ruby cmd.rb  3>&2

最初の状態の以下を書き換えたい

fd 中身(出力先)
1 stdout
2 stderr
3

こうした

fd 中身(出力先)
1 stdout
2 stderr
3 stderr
次にfd2 を書き換える
ruby cmd.rb  3>&2 2>&1
fd 中身(出力先)
1 stdout
2 stderr
3 stderr
最後に、fd1 を書き換える。
ruby cmd.rb  3>&2 2>&1 1>&3
fd 中身(出力先)
1 stderr stdout
2 stdout
3 stderr
出来上がり
fd 中身(出力先)
1 stderr
2 stdout
3 stderr

リダイレクトの指定順に注意する

1を〇〇へ、2を〇〇へとおぼえてしまうと、間違うことが多いので、 1 の指している先を2へコピーなど、言葉を少し変えて記憶したほうが無難でしょうね。

&1 記号の意味

2>&1 
2>/dev/null

これは &11 の指している先 を示すポインタで、 ファイル名の代わりに使っているだけだと考えて

2 >  &1 
2 >  /dev/null

このように敢えてスペースを入れて、対比させると覚えやすい ( >& にスペースを入れられないのだけど、スペースを入れ対比したほうが直感的)

まれに間違って教えてる人や間違って覚えてしまう人がいて

1 と 2 を合流させるから &アンド だとか、1 and 2 で 1 の内容に2 をプラス(追加)するなどと簡便な説明をしてしまってあとで混乱してしまうことになる。

省略記法

1と2の出力先をどちらも /dev/null にしてしまうなら

ruby cmd.rb   >& /dev/null
ruby cmd.rb   &> /dev/null
ruby cmd.rb   &>> /dev/null

此のような書き方も出来る。 此の書き方であれば、1+2を合流したと説明しても差し支えないと思う。

まとめ

## 標準出力の fd1 の指定は省略できる
ruby cmd.rb   1> /dev/null
ruby cmd.rb   > /dev/null

## 両方の出力先を同じにする
ruby cmd.rb   2>&1
ruby cmd.rb   1>&2

## 両方の出力先を同じしてファイルへ
ruby cmd.rb  >/dev/null  2>&1
ruby cmd.rb  1>/dev/null  2>&1
ruby cmd.rb  2>/dev/null  1>&2
ruby cmd.rb   >& /dev/null
ruby cmd.rb   &> /dev/null
ruby cmd.rb   1> /dev/null 2>/dev/null
ruby cmd.rb   1> /dev/null 2>/dev/null


## 出力先を入れ替える。
ruby cmd.rb   3>&1 1>&2  2>&1
ruby cmd.rb   3>&2 2>&1 1>&2 

github issue のラベルを編集する

github issue のラベルを編集してフレンドリーにする

github のIssueのラベルを編集して名前をつけたら楽しい

https://github.com/$(repo_name)/labels

編集した結果

f:id:takuya_1st:20170427142816p:plain

ラベルをアレコレつけると感情が伝わってフレンドリーになる。

ただし、ラベルも使いすぎるとワケガワカラナイヨってなるので、締め切り毎のラベルを作るようなことはせずに、マイルストーンでやったほうが良いと思った。

参考資料

Issuesのラベルを管理(編集)する方法 - 22時に寝ようと思って2時に寝る。

Bashでシンボリックリンクのディレクトリの補完(上位ディレクトリが辿れない

シンボリックリンクされたディレクトリから上位が辿れない・・・

シンボリックリンクされたディレクトリがあって、その内部から、補完を試みても、ディレクトリがうまく補完されない。

takuya@vim$ ll  -d /usr/local/opt/vim
lrwxr-xr-x 1 takuya admin 26 2017-04-24 14:32 /usr/local/opt/vim -> ../Cellar/vim/HEAD-b7637c4
takuya@vim$ cd  /usr/local/opt/vim
takuya@vim$ cd ../<TAB>#押しても反応なし

解決策(一時的)

complete -r cd  

cd の補完で、ディレクトリに限定するのが complete cd なのです。シンボリックリンクはファイルなので補完されない。。

ディレクトリではなく、全ファイル(+ディレクトリ)を補完対象にすると一時的にはうまくいく。

ただし、これはcd の補完をオフにしているだけなので完璧とはいえない。

complete -r cd  # 補完オフ
complete -d cd  # ディレクトリ名で補完

解決策( bash-completion )

bash-completion で cd の補完を独自に定義するとまぁ、なんとかなる。

流石にコレを全部書くのはちょっと・・・なので、最初から用意されている _cd を少しカスタマイズする

type _cd  # この結果をカスタマイズする
complete -F _cd cd 

具体的にはこうする

function_cd_with_symlinks(){
  local cur opts;
  cur="${COMP_WORDS[COMP_CWORD]}";
  _cd; ## 既存の _cd を呼び出しておいて
  opts="${COMPREPLY[@]} path/to/c";      ## 追加する
  COMPREPLY=($(compgen -W "${opts}" -- ${cur})); 
}

解決策(諦める)

シンボリックリンクの解釈を諦める。

set -P

これで、cd を含めすべてのPATHの取扱が、シンボリックリンクにきたら物理的なアドレスに変えてくれる。

これと言った解決策がない

cd が解決したとしても ls findなどのコマンドはやっぱりシンボリックリンクではなくreadlink後の物理パスに対して作用するので、どうしようもない感じで詰んだ。

参考資料

https://askubuntu.com/questions/155696/how-to-tab-complete-directories-when-in-symlinked-directory

http://stackoverflow.com/questions/26550327/bash-completion-to-make-cd-command-complete-working-directories-from-other-run

Bash completion to make 'cd' command complete working directories from other running shells? - Stack Overflow

google drive にsync してディレクトリをバックアップを取る

google drive にファイルをバックアップする

インストールと使い方の基本

google drive を コマンドから使うgdrive が早くて便利 - それマグで!

バックアップを取る。

複製を取るには、幾つかのポイントを気にする。

  • Google Driveどのフォルダ(ディレクトリ)にアップロードをするか?
  • フォルダのIDを取得する
  • sync する
gdrive mkdir backups
# fileId をメモする。
gdrive sync upload  /var/path/to/src/dir <fileId>

速度は?

15MB/b くらい出た。結構速い。

以前は速度が出なくて挫折したけどコレくらい速度出るなら、ディスク移動中にとりあえずキープして置いていく場所に結構便利に使えそう

Mac のアプリケーションの識別文字列 CFBundleIdentifier ( bundle Identifier )を探す

2018-09-01 mdls を使うのがいい方法みたいなので、別の記事にしました。

takuya-1st.hatenablog.jp

   

   

Macのアプリケーションの識別

BundleIdentifier を使って Identifier ( 識別子)を取り出す。

kMDItemCFBundleIdentifier = "com.microsoft.rdc.mac" 

bundle identifier をとりだして、アプリ名と com.evernote のような ドメイン名的な名前を探す。

CFBundleIdentifierを探す

Karabiner の設定をするのに、UTI文字列が必要なのでね。com.vivaldi.Vivaldi のような文字列が必要になるので。

plutil  -extract CFBundleIdentifier xml1  path/to/Info.plist  -o  - | xmllint - --xpath "//string/text()"

Bundle identifier ( CFBundleIdentifier ) は UTI文字列 を入れている。

UTI 文字列を identifier (識別子)として使うので、例えば、次のようになる。

takuya@~$ plutil  -extract CFBundleIdentifier xml1  /Applications/PhpStorm.app/Contents/Info.plist  -o  - | xmllint - --xpath "//string/text()"
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<string>com.jetbrains.PhpStorm</string>
</plist>

/Applications からすべてのIDを探すなら、bash で簡単

ループで回せば結構簡単にわかってくる。

for i in /Applications/*/Contents/Info.plist; do
  plutil  -extract CFBundleIdentifier xml1  "$i"   -o  - | xmllint - --xpath "//string/text()"; 
done \
 | grep string

ruby も書いた

#!/usr/bin/env ruby


require 'mechanize'
require 'fileutils'
require 'pp'
list = Dir.glob('/Applications/*/Contents/Info.plist')
len = list.map{|e| e.match(%r|(?<name>[^/]+app)|)[:name].size}.max()
list.each{|e|
  begin
    appname = e.match(%r|[^/]+app|)
    xml = `plutil  -extract CFBundleIdentifier xml1  "#{e}"   -o  -`
    xmldoc = Nokogiri::XML( xml )
    identifier =  xmldoc.search('//string[1]/text()')[0].text
  rescue => e
    appname ||= ''
    identifier ||= ''
  ensure
    puts "%-#{len}s\t%s" % [appname, identifier]
  end

}



参考資料

http://www15.plala.or.jp/NovemberKou/documentBasedProject/setCFBundleIdentifier.html

http://harafuji0613.hatenablog.com/entry/2015/03/22/002953

http://qiita.com/trakwkbys/items/a94c4d43342e96352bde

2017-07-21 追記

セルフ検索に引っかからないので、キーワード追加した

2018-05-10

ぱぱっと使いたいのでrubyのコマンドにした。

vim の置換で改行をいれる

改行を入れるのをどうするのか悩んだ

改行コードを書き込んだら、あとはファイルの改行方式に従ってうまくやってくれる

%s/^name/name^M/

^M Ctrl-V, Ctrl- M で入力する。もしくは Ctrl - V , Enter と入力する

直接指定する場合

^M で出来るんだから、改行は \r じゃなのかなとおもったら \r でいけた

%s/^name/name\r/

Vimにおける改行はモードによって見え方が異なるので大変ね。

参考資料

http://d.hatena.ne.jp/teramako/20061003/p1

vimの置換で改行を(挿入|削除)する - Qiita

Chromeのヘッドレスモードで快適生活

chrome のヘッドレスモードで起動して遊ぶ

Chromeのヘッドレスモードは便利ですよね。

macOS にも Chrome のヘッドレスモードがやってきます。

MacOSだと長らく動かなかったので、あれこれトリックを使っててめんどくさかったんです。ようやく陽の光が!

準備したもの

Chrome >=59 であればいいので、Dev チャンネルから持ってきた。そのうち通常リリースに含まれると思う。

f:id:takuya_1st:20170418185344p:plain

インストール

/Applications/Chrome-dev-59.app

という名前で保存した。この名前はあとで open -a するのに必要

起動

とりあえず。ヘッドレスモードで起動する

open -a  Chrome-dev-59.app --args  --headless chrome://help/

起動した!

CMD-Q で終了すら出来ない、単純なAppが起動する。

f:id:takuya_1st:20170418185733p:plain f:id:takuya_1st:20170418185749p:plain

remote-debugging-port を使って遊ぶ

open -a  Chrome-dev-59.app --args  --headless --remote-debugging-port=9222 chrome://help/

node で遊ぶ

npm install chrome-remote-interface

コードを書く

const CDP = require('chrome-remote-interface')

CDP( ( client ) => {
    //Extract used Devetools domains.
    const { Page, Runtime } = client;
    // Enable events on domains we are intersted in.
    Promise.all( [
      Page.enable()
    ]).then(  () => {
      return Page.navigate( { url: "http://t.co"});
    })

    // Evaluate outerHTML after page has loaded.
    Page.loadEventFired( () => {
      Runtime.evaluate( {expression: 'document.body.outerHTML'} ).then( (result) =>{
          console.log( result.result.value );
          client.close();
        })
    })

}).on('error', (err) => {
  console.error('Cannot connect to Browser:', err);
})

サンプルコードはここから → https://github.com/cyrus-and/chrome-remote-interface

実行結果

takuya@headless-test$ node  sample.js
<body>

    <div id="main">
      <a href="http://twitter.com" class="bird"><img src="/static/images/bird.png" alt="Twitter"></a>
      <p><a href="http://twitter.com">Twitter</a> uses the <strong>t.co</strong> domain as part of a service to protect users from harmful activity, to provide value for the developer ecosystem, and as a quality signal for surfacing relevant, interesting Tweets.</p>
      <a class="back" href="http://twitter.com">Back to Twitter</a>
      <a class="more" href="http://help.twitter.com/entries/109623">Learn more</a>
    </div>

    <div class="footer">
      <span>© <script type="text/javascript">document.write(new Date().getFullYear());</script>2017 Twitter</span>
      <a href="http://twitter.com/about">About Us</a>
      <a href="http://twitter.com/about/contact">Contact</a>
      <a href="http://blog.twitter.com/">Blog</a>
      <a href="http://status.twitter.com/">Status</a>
      <a href="http://dev.twitter.com">API</a>
      <a href="http://help.twitter.com/">Help</a>
      <a href="http://twitter.com/jobs">Jobs</a>
      <a href="http://twitter.com/tos">TOS</a>
      <a href="http://twitter.com/privacy">Privacy</a>
    </div>


</body>

あとは、普通に遊べる。

ああ、これでずっと楽にCookie必須なWEBサイトの中で遊べるわ。WEBブラウザを操縦するのは遅いからスクレイピングには向かいないけど。。。自動実行とかだとうまくいく。

pdf で逆向きの画像をまとめて回転させる(qpdf編

スキャンして、上下逆さに入れちゃったPDFをまとめて回転させる

スキャンした画像が上下逆の場合にどうするんだろうか。1ページずつ切出して jpeg 取り出して、pdftk 回転させたけど、すごく時間がかかってi7 6700 でも400ページ処理するのが辛かった。qpdf を調べることにした。

こういうPDFを回転させたい

中身はJPEGが埋まったPDFなのですが、逆になったらそれはそれで辛い。

f:id:takuya_1st:20170417223524p:plain:w200

適当にスキャンしてると、結構やらかして天地逆転することがある。

PDF だけで180度転換する。

原理としては、中身のJPEGを180度回転させずに、PDF側で埋め込み表示するときJPEGを180度回転させて表示する感じ。なのでJPEGは一切触らない。

回転方法

pdf-rotate.cc という qpdf のサンプルコマンドがあるのでそれをぱぱっと取り出して使う。

qpdf を用意する

brew install qpdf 

ソースを取ってくる

mkdir work$(date -I)
cd work$(date -I)
wget https://raw.githubusercontent.com/Quasilyte/qpdf\
/74618e7b337537706204e83e05f27493fa6d6eb2/examples/pdf-rotate.cc

ソースを持ってきたらコンパイルする。

$ g++ -L/usr/local/lib  -lqpdf pdf-rotate.cc -std=c++11  -o pdf-rotate

回転する。

./pdf-rotate in.pdf 180 out.pdf

楽ちんですね。

1ページずつメタデータを書き換えても良いんだけどさすがにPDFの構造を知らないと難しいのでおすすめしない。

スキャンされた画像データは触ってない

繰り返しになりますが、スキャンされた画像データは一切触ってないです。PDFが画像をページに埋め込むときに回転しています。これはHTMLに画像を埋め込んでスタイルシートで translate rotate してるのと同じです。

参考資料

逆順に保存されたPDFのページを反対の順番にする。qpdf編

スキャンしたら、ページが逆!?

スキャンしたら、ページが逆になったことありませんか?わたしは適当にスキャンするのでよくあります。

ページが逆順になったPDFを、1ぺーじから順に反対順に印刷し、正順化するには、どうするのか。

PDFプリンタを使いますか?もっと簡単な方法があります。コマンドです。

qpdf でページを逆順に。

qpdf を使えば、かんたんにページを後ろから出力して、reverse することが出来ます。

qpdf --empty out.pdf --pages in.pdf z-1 --

とってもかんたん。そして速い!!

qpdf のインストール

macOS / OS X

brew install qpdf 

Debian GNU/Linux

apt install qpdf 

pdftk との速度比較

たった2ページを逆順にするだけでもコレくらい違います。

pdftk で処理した場合。

takuya@ページ順が逆$ time pdftk sample.pdf cat end-1 output out.tk.pdf

real    0m0.497s
user    0m0.156s
sys 0m0.093s

qpdf で処理した場合

takuya@ページ順が逆$ time qpdf --empty out.pdf --pages sample.pdf z-1 --

real    0m0.050s
user    0m0.008s
sys 0m0.025s

出来上がるファイルも特に差は無いようです。

takuya@ページ順が逆$ ls -l   out*
-rw-r--r-- 1 takuya staff 2391678 2017-04-17 22:06 out.qpdf.pdf
-rw-r--r-- 1 takuya staff 2391978 2017-04-17 22:06 out.tk.pdf

関連資料

PDFを逆順にソートして並べ直す(最初から最後のページを全部逆順に整列) - それマグで!

参考資料

https://inconsolation.wordpress.com/2014/03/24/qpdf-still-more-pdf-wizardry/

PDFを各ページに分割(切出)するコマンド pdfseparate

PDF から 指定ページを切出・分割したい。

スキャンしたPDFや、配布されたPDFを扱いたい。数百ページのPDFを1ページ毎に分割したい。指定ページだけのPDFを作成したい。

Nページ目からMページ目までのPDFを作りたい、NページをNファイルのPDFにしたいときにどうするか

pdfseparate コマンドを使うと便利

pdfseparate コマンドが便利。 poppler 由来のこのコマンドが目的に合致していて、確実にきれいな処理をしてくれる。

インストール

ぱぱっとコマンドで入れるのが楽で早い。

## macOS /OS X
brew info poppler
## GNU/Linux
apt install poppler-utils
## Windows
choco install xpdf-utils

使い方

超簡単にPDFをファイルに分割できる。覚え方もかんたん。

全ページをファイルに

pdfseparate 2017年04月10日17時43分54秒.pdf out.%d.pdf

指定ページをファイルにする。 ( 10-20 )

pdfseparate 2017年04月10日17時43分54秒.pdf  -f 10 -l 20 out.pdf

指定ページから先をファイルにする。

pdfseparate 2017年04月10日17時43分54秒.pdf  -f 10 out.pdf

ある1ページだけを切出したい

pdfseparate 2017年04月10日17時43分54秒.pdf  -f 10 -l 10  out.pdf

PDF の分割ができる

PDFを手作業で処理することは、MacOSXならプレビューで、Adobe Readerなどでも出来る。

ただしそれらは手作業でファイルを選んだりクリップボードを経由したりとても大変になって、自動化が追いつかない。

そのために、コマンドで自動化が出来るので是非試してください

その他のコマンド

pdftocairo pdftk を使っても同様のことが実現できます。

参考資料

github.comでサブディレクトリをダウンロードする。

git archive が通らない。

git レポジトリなら git archive で取り出せるのですが、github.com は現在(2017-04-07) のところ、git archive コマンドをサポートしてない模様

Invalid command: 'git-upload-archive 'takuya/sample.git''
  You appear to be using ssh to clone a git:// URL.
  Make sure your core.gitProxy config option and the
  GIT_PROXY_COMMAND environment variable are NOT set.

というわけで、サブディレクトリをまるっとダウンロードすることが出来ない。

svn を経由する

github.com のレポジトリは svn でもアクセスできるようになっているので svn export を使うと取り出せた。

svn exprot http://github.com/takuya/sample.git/trunk/path/to/directory 

これが現状マシな解決策のように思える。

git だけなら

git read-tree でバージョン管理をしながらいらないフォルダを不可視にしたり、git archive でtgz で取り出せたり、出来るんだけどねぇ

パスの指定方法

https://github.com/takuya/sample.git

のときは、master を指定して

https://github.com/takuya/sample.git/trunk
https://github.com/takuya/sample.git/trunk/path/to/sub/dir

と指定する。

関連資料

http://takuya-1st.hatenablog.jp/entry/2015/02/26/022139