それマグで!

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

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

Evernoteにコードブロックが追加された

Evernoteにコードブックが追加されました。

念願のコードブロックを手に入れたぞ。

f:id:takuya_1st:20180818175610p:plain:w350

これまで、enmlを持ってきたり、HTMLを作り置きしておいてそこからコピペでスタイルを適用したり、マクロ(Automator)を作ってキーボードショートカットに割り当てたり、本当に苦労したけど。もうこれで苦労しなくて済むんですね。感動した。

使い方は簡単

メニューからコードブロックを選ぶか、Cmd-Ctrl-Bを押すだけ。

f:id:takuya_1st:20180818175615p:plain:w250

コードブロックの特徴

コードブロックが搭載されたことにより、次の2点が楽になります。

特に、これまでモノ・スペースのフォントを選ぶのがめんどくさかったので本当に助かる。

ブロック→スタイル削除で活用する

一旦、ブロックを作ってからスタイル削除(背景や文字装飾)を削除しても、等幅フォントが残る(バグ?)があるので、これを応用すると手軽に、等幅フォントを適用できて便利。

f:id:takuya_1st:20180818181549p:plain:w150

スタイルを消しても等幅フォントは残る

f:id:takuya_1st:20180818181531p:plain:w250

フォント設定はスタイル削除で消せないので、上手にやれば活用できる。

ドコモWiFiの0001docomoにつながらない時に試すこと

0001docomoにつながりません。

docomoWifiは優秀なんだけど癖があります。

ドコモの公衆無線LANは便利ですぐ繋がるし人通りが多くても通信エラーになるようなことがい優秀な無線LANを提供してくれてる。

Wi-Fiの認証は認証済みになるのに、DHCPIPアドレスが降ってこないことがあります。

1契約で1つしか同時に同じアクセスポイントに繋がりません。

1契約(1アカウント)で接続できる同時接続台数は1です。*1

そのため、iPhone / iPad / Androidなど複数のモバイルを機器を同時に使えません。スマホ・モバイル機器が同時に接続できるのは早い者勝ちですになってしまいます。

ドコモWifiにつなぐ対応方法

いくつか対応方法があります。

  1. 0000docomoを使う
  2. wifi をオフにする
  3. 契約数を増やす
  4. 一時的にアカウントを借りる。

対策1: 0000docomoを使う

0001docomoはほとんどの場合、0000docomo と2つのSSIDで提供されています。

f:id:takuya_1st:20180817192227p:plain:w250

そこで、代替策として 0000docomoを使います。 0001docomo と 0000docomo は接続が別計算なので大丈夫です。

0000docomoのWPAパスワードは次の通りです。

 0000docomo "B35D084737

対策:2 使わない機器のWiFiをチェックする

使わない機器のWifiを止めてしまえばいいのです。

使う機器以外のWifiの接続状態をチェックして、不用意に繋いでしまっている0001docomoの接続をオフにします。

そんなのは当たり前だろ。と思う人も多いかもしれませんが、WiFi接続パスワードは所有者が同じスマホ間で共有されています。なので、繋いだ記憶のない機器でも0001docomoのパスワードは同期されて接続可能状態になっています。気づかない間に繋がってることがあります。

つながらない場合は、真っ先に手持ちのデジタル機器の無線LANの接続状態を確認しましょう。預かり知らない間にパスワード設定が同期されてつながってることが多いですよ。

使いたい端末以外が先に0001docomoを使っていた場合は切断しておきましょう。

対策3: 契約数を増やす

docomoWifiは追加契約ができます。月額300円かかります。頻繁に使うのなら、追加してもいいでしょう。

f:id:takuya_1st:20180817194332p:plain:w250

また、moperaに変えるか、追加契約をすることで、無料の追加アカウントを一つもらえます。これも便利ですよ。

mopera足した方がお得

ドコモWifiを追加契約 300 円ですが、mopera UI スタンダード 500 円 ISPセット割 -150円 -150円なので、ドコモWifiを追加契約するなら、mopera足した方が安いですね。

ちなみにmoperaを契約している私の請求明細は次のようになっています。

f:id:takuya_1st:20180817195852p:plain:w250

対策4: 一時的にアカウントを借りる

一緒にお出かけしている家族のドコモアカウントを借りれば、一時しのぎをすることができます。

家族のスマホ契約のSPモードにもWifiアカウントが付いてますから、それを借りればいいわけです。

家族全員が同時にドコモWifiを使うという状況はあまり考えにくいですものね。

参考資料

https://www.nttdocomo.co.jp/service/wifi/docomo_wifi/about/bill_plan/u_publiclan/

*1: 多分だけど、全く違うAPで距離が離れていると同じアカウントで複数繋がるんだと思う

Evernoteで検索されないときに再インデックス化させる方法

Evernote で検索を再構築する。

Evernoteで、思ったように検索されないときがあります。

Web版だったら検索されるのに、iOSだと検索されるのに、Windowsだとだめ。macOSだとダメとかそういうことは起きます。 Evernoteは検索に通信を使っていません。検索するのにいちいちサーバーに問い合わせたりしてないので速いんですよね。

検索のインデックス(索引)はローカルにあるのでコレを再構築すると検索が復活します。

ヘルプをOption押しながら実行すると

検索の索引の再構築のメニューが出てきます。Mac全体のファイル検索にマッチさせるもの(MacなのでSpotlight)と、Evernoteの検索から検索したときにヒットさせるもの。 これらをそれぞれ再構成することができる。

f:id:takuya_1st:20180816020131p:plain

どういうときに検索されない事態が起きるのか

経験上ですが、Macを新しく移動したとき(新規ユーザーに移動したとき)、Evernoteをインストールし直したときですね。あとはEvernoteのメジャーバージョンアップ

これらのときに検索が壊れて、ノートが検索されないことが起こるようです。

参考資料

https://discussion.evernote.com/topic/110260-help-needed-cannot-search-my-old-notes/

date コマンドと bash でタイマー(カウントアップ)を作る

date を使うと整数値の秒が取れる

date +%s

整数値で秒が取れるなら、タイマーも作れそうですよね。

整数の四則演算 と 組み合わせる。

echo $(( $now - $start))

これで、現在の時刻が取れる。

改行(CRLF)が邪魔なので

CRLFをしてしまうと1行次に行ってしまうのでCRだけをしてLineFeedをしないようにしてカレントの行を上書きする

時刻をフォーマットする

date コマンドで時刻をフォーマットする

date +%H:%M:%S @$time

引数に @1345 と整数時間を入れることで UNIX Epoch 1980-01-01 :00:00:00 からの経過時間として解釈してくれる。

これを利用して時間をHH:MM:SS にフォーマットをできる。

全てを組み合わせる

  • date で 整数秒
  • printf で 先頭へ
  • date @ で時間をフォーマット

これらを組み合わせて sleep すると、タイマーができるやったね

タイマーサンプル

#!/bin/bash

start=$( date +%s )


while : ; do


  elapsed=$((  $( date +%s )  - $start ));

  # echo $elapsed
  printf "$( date -d@${elapsed} -u +%H:%M:%S)\r"

  sleep 0.1
done

ミリ秒も計算したいよね

これだと味気ないので、bashで少数を計算する方法と、ミリ秒の表示を組み合わせる。

bashで少数を計算する

bashexpr$(( / (( でも整数値の計算しかできないので、bcなどを使う。

## bc を使って小数点を含む計算をする
echo 0.32 + 0.22 | bc
## 0.0x 始まりにする
printf %03f $( echo 0.32 + 0.22 | bc )

ミリ秒を表示する

date コマンドでミリ秒表示

date +%s.%3N

これらも組み合わせる

タイマー改良

#!/bin/bash

start=$( date +%s.%N )


while : ; do 


  elapsed=$( printf "%0.3f" $( echo   $( date +%s.%N )  - $start | bc  ));

  # echo $elapsed
  printf "$( date -d@${elapsed} -u +%H:%M:%S.%3N )\r"

  sleep 0.1
done

サンプル

実際にタイマーを動かしてみたサンプル

f:id:takuya_1st:20180815210230g:plain

誤差

sleep しているだけなので、だいぶ誤差が出てきます。誤差を正確に扱ってタイマーを作るのは大変なので、コレくらいにとどめておくことにする。

動画

動画はこちら

youtu.be

参考資料

dateコマンドでタイムスタンプ(unix epoch)を表示する

bash で現在のタイムスタンプを取得する

unix時間などと呼ばれる int 秒ですね、int の timestamp をdate コマンドで取得する

date +%s

## => 1534329465

これで、int秒のタイムスタンプを表示するすることができる。%s は現在の時刻の整数値を返してくれるので、便利ですよね。整数値があればタイマーにも使えます。

man より

man を見ると、次のように記述されてる

 %s     seconds since 1970-01-01 00:00:00 UTC
 %N     nanoseconds (000000000..999999999)

ナノ秒も含めて、現在の時刻を表示する

date +%s.%N
#=>1534329695.149991000

ミリ秒がいいんだけど

大丈夫、ミリ秒でもいける、それは %05d 的なノリで %3N とすればいい

$ date +%s.%3N
#=>1534329697.436

GNU date です

もちろん GNU date です。macOS 等の場合は、 brewGNU core を入れてないと動かない。

追記

gnu date のフォーマットなので、strftime などでも使えると思います。

python で int秒で unix タイムスタンプを取得した例

import time
print(time.strftime("%s"))
Time.now.strftime('%s') #=> "1535055995"

などと strftime でも使えて便利。最近のタイム関数は nano秒を返したりするしさ。言語毎に関数の使い方を覚えるのは流石に面倒なのです。GNU date の strftime という言語を超えたOSを超えた共通規格ですから、これを覚えておくと便利ですよね。

参考資料

uwsgi で php を動かす - phpの複数バージョン起動のためにuwsgi-plugin-php をインストールして使う

uwsgi にphp プラグインがあります。

uwsgi とはPythonのイメージですが、php-fpm の代わりに使えます。(多分使うことは殆ど無いだろうけど)

uwsgi で管理するメリット

docker のイメージを作るときに php-fpmが混ざると共通化されないけど、uwsgi だとある程度は共通化されるかなと

準備

今回は debian で行います。

sudo apt install uwsgi
sudo apt install uwsgi-plugin-php

uwsgi の設定フォルダ

debian のお作法っぽく enabled と available が作られています。

takuya@:~$ ll /etc/uwsgi/
合計 8.0K
drwxr-xr-x 2 root root 4.0K 2018-08-07 23:47 apps-available
drwxr-xr-x 2 root root 4.0K 2018-08-08 00:11 apps-enabled

phpを起動する uwsgi設定を作ります。

今回は、ざざっと /var/www/virtualhosts/sample/ に作ったindex.phpを起動します。

apps-available/phpinfo.ini

; vim: set ft=dosini expandtab shiftwidth=2 tabstop=2 :

[uwsgi]

route = .* addheader:Server: my uWSGI server
plugins = http,0:php
php-sapi-name = apache

; set master process name and socket
; '%n' refers to the name of this configuration file without extension
procname-master = uwsgi %n
master = true
socket = /run/uwsgi/%n.sock
chown-socket = www-data:www-data

; drop privileges
uid    = www-data
gid    = www-data
; umask  = 027

; run with at least 1 process but increase up to 4 when needed
processes = 4
cheaper = 1

; reload whenever this config file changes
; %p is the full path of the current config file
touch-reload = %p
php-docroot     =  /var/www/virtualhosts/sample/
php-allowed-ext = .php
php-index = index.php
# php.ini
php-set = date.timezone=Asia/Tokyo
php-set = expose_php=false
## extensions
php-set = extension=gd.so
php-set = extension=iconv.so
php-set = extension=pdo_sqlite.so
php-set = extension=pdo_mysql.so
php-set = extension=pdo_pgsql.so
php-set = extension=curl.so
php-set = extension=bz2.so
php-set = extension=intl.so
php-set = extension=ldap.so
php-set = extension=ftp.so
php-set = extension=imap.so
php-set = extension=exif.so
php-set = extension=gmp.so
php-set = extension=imagick.so

; opcache
php-set = zend_extension=opcache.so
php-set = opcache.enable=1
php-set = opcache.enable_cli=1
php-set = opcache.interned_strings_buffer=8
php-set = opcache.max_accelerated_files=10000
php-set = opcache.memory_consumption=128
php-set = opcache.save_comments=1
php-set = opcache.revalidate_freq=2
; apcu
php-set = extension=apcu.so
php-set = apc.ttl=7200
php-set = apc.enable_cli=1

設定を有効にします。

$ cd /etc/uwsgi/apps-enabled
$ sudo ln -sr ../apps-available/phpinfo.ini ./
$ ll
合計 4.0K
-rw-r--r-- 1 root root 424 2018-03-17 17:05 README
lrwxrwxrwx 1 root root  29 2018-08-08 17:07 phpinfo.ini -> ../apps-available/phpinfo.ini

systemctl で起動します

sudo systemctl  restart  uwsgi.service

uwsgiログを確認します。

takuya@:apps-enabled$ sudo systemctl  status   uwsgi.service
● uwsgi.service - LSB: Start/stop uWSGI server instance(s)
   Loaded: loaded (/etc/init.d/uwsgi; generated; vendor preset: enabled)
   Active: active (running) since Wed 2018-08-08 18:15:55 JST; 1min 24s ago
     Docs: man:systemd-sysv-generator(8)
  Process: 11654 ExecStop=/etc/init.d/uwsgi stop (code=exited, status=0/SUCCESS)
  Process: 11694 ExecStart=/etc/init.d/uwsgi start (code=exited, status=0/SUCCESS)
    Tasks: 3 (limit: 4915)
   Memory: 31.4M
      CPU: 205ms
   CGroup: /system.slice/uwsgi.service
           ├─11806 uwsgi phpinfo
           ├─11812 uWSGI worker 1
           └─11813 uWSGI http 1

 8月 08 18:15:55 acid systemd[1]: Starting LSB: Start/stop uWSGI server instance(s)...
 8月 08 18:15:55 acid uwsgi[11694]: Starting app server(s): uwsgi -> . done.
 8月 08 18:15:55 acid systemd[1]: Started LSB: Start/stop uWSGI server instance(s).

設定したphpのログを確認します

ログファイルの場所は /var/log/uwsgi/app/%n.logです。

takuya@:~$ sudo tail -f  /var/log/uwsgi/app/phpinfo.log
Wed Aug  8 18:15:55 2018 - PHP document root set to /var/www/virtualhosts/sample
Wed Aug  8 18:15:55 2018 - PHP 7.0.27-0+deb9u1 initialized
Wed Aug  8 18:15:55 2018 - your server socket listen backlog is limited to 100 connections
Wed Aug  8 18:15:55 2018 - your mercy for graceful operations on workers is 60 seconds
Wed Aug  8 18:15:55 2018 - mapped 363840 bytes (355 KB) for 4 cores
Wed Aug  8 18:15:55 2018 - *** Operational MODE: preforking ***
Wed Aug  8 18:15:55 2018 - *** no app loaded. going in full dynamic mode ***
Wed Aug  8 18:15:55 2018 - *** uWSGI is running in multiple interpreter mode ***
Wed Aug  8 18:15:55 2018 - spawned uWSGI master process (pid: 11806)
Wed Aug  8 18:15:55 2018 - spawned uWSGI worker 1 (pid: 11812, cores: 1)
Wed Aug  8 18:15:55 2018 - spawned uWSGI http 1 (pid: 11813)
[pid: 11812|app: -1|req: -1/1] 192.168.2.107 () {40 vars in 969 bytes} [Wed Aug  8 18:16:11 2018] GET / => generated 107674 bytes in 116 msecs (HTTP/1.1 200) 2 headers in 84 bytes (0 switches on core 0)
[pid: 11812|app: -1|req: -1/2] 192.168.2.107 () {40 vars in 950 bytes} [Wed Aug  8 18:16:12 2018] GET /favicon.ico => generated 9 bytes in 0 msecs (HTTP/1.1 404) 3 headers in 96 bytes (0 switches on core 0)
[pid: 11812|app: -1|req: -1/3] 192.168.2.107 () {42 vars in 1000 bytes} [Wed Aug  8 18:16:13 2018] GET / => generated 107761 bytes in 6 msecs (HTTP/1.1 200) 2 headers in 84 bytes (0 switches on core 0)

ブラウザでチェックします。

Server API uWSGI/php になっているはずです。

f:id:takuya_1st:20180808181938p:plain

index.php に全部渡す

uwsgi はプラグイン構成なので、プラグインrewrite で /aaa を index.php に渡してあげる。

[uwsgi]
plugins = router_rewrite,http,0:php
route = ^ rewrite:/index.php

もちろん PATH_INFOしてもいいんだけど、rewriteが楽かな。

f:id:takuya_1st:20180808184221p:plain

フロントサーバーと連携する

これだけだと、何の役に立つのかわからないんので nginx と連携させてみる。

uwsgi を使うメリットの一つは、UNIXドメインソケットをListenできるところ。

uwsgi/app-enabled/phpinfo.php

[uwsgi]
plugins = router_rewrite,php
php-sapi-name = apache

route = ^ rewrite:/index.php
route = .* addheader:Server: my uWSGI server
master = true
socket = /run/uwsgi/%n.sock
chown-socket = www-data:www-data

nginx

 location ~ {
      include uwsgi_params;
      uwsgi_modifier1 14;
      uwsgi_pass unix:/run/uwsgi/phpinfo.sock;
  }

これで、ngix -----unix:sock ---> uwsgi が完成する

php-plugin を使うと

uwsgi 単独で実行環境が作成されます。

uWSGI というアプリケーションのコンテナとして統一感があるのが魅力だと思います。

  • docker php の配布アプリに nginx を含めなくて良くなる。
  • phpアプリケーションの起動を ugwsgi に任せられる。
  • phpバージョンを変えられる。
  • httpdの実行ユーザーを変えられる。

などあります。

要は、phpが多数起動できるのでアプリケーション独立には本当に便利だと思います。

Dockerfileでちまちまやってられるか、とかdocker-compose で複数イメージを連携させるよりずっと管理が楽です。

php-fpm で2つ設定書いて2つ起動すればいいだろ?」といわれた何もいえねー。

phpにはいろいろな実行環境があります。

ほんとカオスですね。

uwsgiを使っておけば rubypython とかjs とかすべて同じように扱えるしノウハウも流用しやすいのでいいんじゃないかって思ったり。

参考資料

certbotで証明書をぱぱっと更新する

nginx や apacheが起動していても大丈夫。

certbothttpd が起動しても、ちゃんとなんとかしてくれる。

/usr/bin/certbot -q renew --pre-hook "service nginx stop" --post-hook "service nginx start"

pre-hook や post-hookで再起動を仕込めるので全く問題ないんですね。

man によると

          renew:
            The 'renew' subcommand will attempt to renew all certificates (or more
            precisely, certificate lineages) you have previously obtained if they are
            close to expiry, and print a summary of the results. By default, 'renew'
            will reuse the options used to create obtain or most recently successfully
            renew each certificate lineage. You can try it with `--dry-run` first. For
            more fine-grained control, you can renew individual lineages with the
            `certonly` subcommand. Hooks are available to run commands before and
            after renewal; see https://certbot.eff.org/docs/using.html#renewal for
            more information on these.

            --pre-hook PRE_HOOK   Command to be run in a shell before obtaining any
                                  certificates. Intended primarily for renewal, where it
                                  can be used to temporarily shut down a webserver that
                                  might conflict with the standalone plugin. This will
                                  only be called if a certificate is actually to be
                                  obtained/renewed. When renewing several certificates
                                  that have identical pre-hooks, only the first will be
                                  executed. (default: None)
            --post-hook POST_HOOK
                                  Command to be run in a shell after attempting to
                                  obtain/renew certificates. Can be used to deploy
                                  renewed certificates, or to restart any servers that
                                  were stopped by --pre-hook. This is only run if an
                                  attempt was made to obtain/renew a certificate. If
                                  multiple renewed certificates have identical post-
                                  hooks, only one will be run. (default: None)
            --renew-hook RENEW_HOOK
                                  Command to be run in a shell once for each
                                  successfully renewed certificate. For this command,
                                  the shell variable $RENEWED_LINEAGE will point to the
                                  config live subdirectory containing the new certs and
                                  keys; the shell variable $RENEWED_DOMAINS will contain
                                  a space-delimited list of renewed cert domains
                                  (default: None)
            --disable-hook-validation
                                  Ordinarily the commands specified for --pre-hook
                                  /--post-hook/--renew-hook will be checked for
                                  validity, to see if the programs being run are in the
                                  $PATH, so that mistakes can be caught early, even when
                                  the hooks aren't being run just yet. The validation is
                                  rather simplistic and fails if you use more advanced
                                  shell constructs, so you can use this switch to
                                  disable it. (default: False)

hook

hook組み合わせるをシンプルなコマンドで定期的な証明書の更新をできるので楽ですね。

いつまで certbot と letsencrypt は無料でSSL配ってくれるんでしょうね。。。。

数年後には有料化されちゃったり、登録の煩雑化されたりしないですよね。心配だ

BettersnapTool とワークスペース移動が競合するのを防ぐ(ワークスペース間のウインドウ移動発動を遅くする)

bettersnaptool 愛用しています。

Macワークスペースと最大化が、SpacesからMissionControlになって以来、最大化でいつも苦労するし、切り替えでアニメーションが発動するのが面倒くさい。

BetterSnapTool にも設定はあるのですが。

BtterSnapToolにも設定こそあるのですが、細かい部分でちょっと気に入らない。

f:id:takuya_1st:20180807172522p:plain:w200

明示的にタイミングを変更する

Windowの移動はミッションコントロールを起動して画面をドラッグしたり、最大化でワークスペースをつくったりできるので、そのほうが確実な気がします。

ウインドウを右端(左端)にドラッグして移動する機能は暴発のほうが多い気がします。

defaults write com.apple.dock workspaces-edge-delay -float 1.0 && killall Dock

ワークスペースのエッジ(境界)にカーソルを移動してウインドウを移動する機能が発動するのを1000ms にしました。

エッジでBetterSnapToolが起動するまでの時間を500ms に設定してあるので暴発は限りなく減った気がします。

ただ、これをしてしまうと、マルチディスプレイをしているときに、ウインドウの移動が不便になったので痛し痒し。

Macbookだけで作業しているならワークスペース移動は邪魔、マルチディスプレイだとワークスペースは重要。難しいですね。

dm-cache(lvm-cache)を作って使う設定をやったのでキャッシュ作成まとめ

lvm cache を作ってみよう

ssd 買ったけど容量持て余しました。大きい容量はNAS/HDDに突っ込んでるし。バックアップはGoogle Drive(Gsuite 無限)にあるし、普段遣いのファイルはNextCloud に置いていて20GB程度だし。SSDがだいぶ余った。

lvm-cache でSSDをキャッシュ化する。

dm-cache と呼ばれるやつですね。

似た機能にbache などがありますが、今回設定していくのは lvm のみで完結するSSDキャッシュです。

前提条件

SSDとHDDが同じVGに入っていること。

もし違うVG間でVGの境界を超えてキャッシュを作ろうとすると

lvconvert cache not same vg

とエラーになる。

インストールは不要です。lvm は殆どの場合最初から用意されています。

HDDとSSDを同じVGに入れる。

HDDとSSDを同じVGに突っ込んで単純なスパニングの容量拡張をする。

sudo pvcreate /dev/hdd #HDD
sudo pvcreate /dev/ssd # SSD
## vg にする 
sudo vgextend data /dev/hdd
sudo vgextend data /dev/ssd

もし、SSDを単独でVGにしてしていたら、LVをPVに再帰的に指定すればいい。

sudo lvcreate -n from_ssd_for_hyrid -L 50GB my_ssd_vg
sudo pvcreate /dev/my_ssd_vg/from_ssd_for_hyrid
sudo vgextend data /dev/my_ssd_vg/from_ssd_for_hyrid

LV を再帰的にLVMのPVに指定できる柔軟性ってすごい便利だよね。

VG からキャッシュ用のLVを切り出す

VGからLVに切り出す。このときに、どのPVから作るのかを指定する

lvcreate -n lv_cache -L 20G data /dev/ssd

VG からキャッシュ・メタデータ用のLVを切り出す。

同じようにメタデータ用のLVを作る

lvcreate -n lv_cache_meta -L 1G data /dev/ssd

LVM ってVGでまとめておいて、そこからLV切り出すときにPVを透過的に指定できるのでホント便利。

CachePool を作る

作ったキャッシュ用LV( lv_cache ) と キャッシュメタデータ用LV ( lv_cache_meta ) を1つに結合して、キャッシュプールを作る

lvconvert --type cache-pool --poolmetadata data/lv_cache_meta data/lv_cache

これが成功すると lvs -a で lv_cache_meta が [] の中に消える。

キャッシュLVを作る

ここまでで作ったキャッシュ・プールを元データのLVに乗っけて統合する

lvconvert --type cache --cachepool data/lv_cache data/my_server_data

出来上がりを確認する。

lvs では 見れないので -a を付けて確認する。lvは大量になるのでVGで絞り込んでいます。

絞り込みは lvs $VG_NAME である。併せて  lvs -a $VG_NAME です、

takuya$ sudo lvs  -a data
  LV               VG   Attr       LSize  Pool       Origin        Data%  Meta%  Move Log Cpy%Sync Convert
  [lv_cache]       data Cwi---C--- 30.00g                          89.87  0.19            0.00
  [lv_cache_cdata] data Cwi-ao---- 30.00g
  [lv_cache_cmeta] data ewi-ao----  2.00g
  [lvol0_pmspare]  data ewi-------  2.00g
  server_data            data Cwi-aoC--- 10.01t [lv_cache] [server_data_corig] 89.87  0.19            0.00
  [server_data_corig]    data owi-aoC--- 10.01t

後片付け

不要になったら、次のコマンドです。取り外します。

lvconvert --splitcache data/my_server_data

で一旦キャッシュを取り外すことができます。

再度取り付けるときは、さっきのコマンドをもう一度

lvconvert --type cache --cachepool data/lv_cache data/my_server_data

このとき

Do you want wipe existing metadata of cache pool volume data/lv_cache? [y/n]: y

この様に、今のキャッシュのデータはワイプして消すけど問題ない?って聞かれる。

これで、取り外して(デタッチ)と取り付け(アタッチ)が出きるようになるので、速度を調べたり容量を変化させたり自由にできますね。

さて、速いのか

多分速くないです。キャッシュなので、ベンチマークを使ってもあまりうまく取れない. だってキャッシュなので一回はファイルを読まないといけない。

というわけで、適当な1GBくらいのファイルを連続して2回読み込んでみましょう

takuya$ dd if=./sample.dd of=/dev/null bs=1k
1411902+1 レコード入力
1411902+1 レコード出力
1445787812 bytes (1.4 GB, 1.3 GiB) copied, 15.7419 s, 91.8 MB/s
takuya@$ dd if=./sample.dd of=/dev/null bs=1k
1411902+1 レコード入力
1411902+1 レコード出力
1445787812 bytes (1.4 GB, 1.3 GiB) copied, 2.04984 s, 705 MB/s

ぉ、割と速い。。。?SSDの限界値超えて速すぎる?うちの安物SSDは500MB/s以上でないと思う、きっとこれはメモリキャッシュが効いてる

うーん、メモリキャッシュには敵わないので、アクセスするファイルが多いときに有効に働きそうですね。

10GBで試してみる。

takuya@:video$ dd if=10GB.dd of=/dev/null
19521704+1 レコード入力
19521704+1 レコード出力
9995112872 bytes (10 GB, 9.3 GiB) copied, 126.673 s, 78.9 MB/s
takuya@:video$ dd if=10GB.dd of=/dev/null
19521704+1 レコード入力
19521704+1 レコード出力
9995112872 bytes (10 GB, 9.3 GiB) copied, 124.038 s, 80.6 MB/s

うーんあまり効いてる気がしない。

キャッシュなので、HDDのベンチをとってもあまり意味のない数字が出ていました。 徐々に転送速度が下がるのはキャッシュがなくなってHDDに取りに行ってそれを待つからでしょうか?

f:id:takuya_1st:20180806021737p:plain

キャッシュは、キャッシュヒット率とかも考慮しないといけないわけですし、その一方でメモリは潤沢に搭載できるわけですし。メモリがあるところへの2次キャッシュと考えると、SSDを1TB買ってキャッシュに使うなら、DDRメモリを32GBまで積んだほうがいいわけですし。やっぱり答えはいつも同じ。メモリは限界まで積め。できる限りメモリ詰め込め。なるほど、流行らないわけだわ。

WriteBackでHDDの書き込み待ちをせず、Write Troughにしたら書き込み速度はSSDと同等になるはずだろうが、WriteThroughは停電でデータ消えるし用途が限られる.

NFSやSMBををLVMキャッシュしたらいいのかと思ったけどそれってLVMでやることだろうか。

うーん。キャッシュヒット率・・・

ちなみに我が家のSSDの速度を測ったところ READで 450MB/s でした。

takuya@:~$ dd if=/data/ssd10G.dd  of=/dev/null
20971520+0 レコード入力
20971520+0 レコード出力
10737418240 bytes (11 GB, 10 GiB) copied, 23.7774 s, 452 MB/s

これも連続して読み出すとメモリキャッシュだ。

takuya@:~$ dd if=10G.dd  of=/dev/null 
10240+0 レコード入力
10240+0 レコード出力
10737418240 bytes (11 GB, 10 GiB) copied, 1.21843 s, 8.8 GB/s

メモリマジはやい。。。

2018-08-06 追記

dm-cacheはwrite through のときでも、シーケンシャルIO(Write)はキャッシュしないって書いてある。

バッファリングはシステムに任せてメモリでやってくれってことね。

Re: [linux-lvm] Testing the new LVM cache feature

By default dm-cache (as is currently upstream) is not going to cache sequential IO, and it also isn't going to cache IO that is first written. It waits for hit counts to elevate to the promote threshold. So dm-cache effectively acts as a hot-spot cache by default.

2018-11-13 追記

キャッシュのメタ情報を扱いだすと。再起動時にlvmetadで時間がかかる

WARNING: Failed to connect to lvmetad. Falling back to internal scanning.

起動の時間が3分くらいかかるようになったので調べてたらどうもLVMのキャッシュらしい。 やっぱキャッシュいらないかもしれない。

2018-11-30 追記

LVMのキャッシュをSSDにするとやっぱり速い.。停電でデータ消えるとか思ってたけど、よく考えるとSSDにデータ残る。RAMDiskやってたときの思考が残ってた。

関連資料

http://takuya-1st.hatenablog.jp/entry/2017/03/06/192708

参考資料

man lvmcache(7)

https://rwmj.wordpress.com/2014/05/22/using-lvms-new-cache-feature/

いまさらcrontabのメリットと書き方を見直す

crontab って便利だけどわかりにくさがある

crontab の書き方がおかしくてうまく実行されないとか、そんな書き方があったのか!となることがある。

なので、systemd を使って定期実行することが多いんだけど、そうすると今度は管理が大変になる。

なんだこの定期実行は!となってしまう。

crontab で敢えて書くことの利点

「なんで、いまらさ cron なんだよ。このオッサンは!!」と思われるかもしれない。でもcronを使うメリットは少なからずあると思います。

メリット

cronで書くと次のようなメリットがあると思います。

  • 一覧ですぐ確認できる
  • /etc/cron.d に絶対ある
  • コマンドの書き方がシンプル
  • メール通知がかならずある。
  • 書式はパターン化される。

最初に cron は「クロン」と呼びます。

ときどき、「くぅーろん」とか「くろーん」とか余計な母音や長音を含めている人が多いです。くぅーろんだと coulomb、くろーんだとcloneになってしまいます。カタカナで表記するならクロンがベターだと思います。

目次

メリット1:cron は一箇所に集まります。

cron を編集するには crontab -e コマンドで行います。

crontab -e 

ユーザーごとにcrontab -e を起動してcron を定義することができます。

crontab -e で登録内容を一覧できる。

crontab -e を見ておけば、どんなコマンドを登録したか一目瞭然ですね。

しかも、編集結果はファイルとして保存されています。

ユーザーのcronの場所

ユーザーが作った cron 定義は次の場所に集まります。

/var/spool/cron/crontabs

一般ユーザーtakuya の場合なら次になります。

/var/spool/cron/crontabs/takuya 

root ユーザーのcron (つまり sudo crontab -e ) のファイルは次に保管されています

/var/spool/cron/crontabs/root

システムのcron の場所

root ユーザーでもなく 一般ユーザーでもない cron のファイルがあります。それはシステムに登録したcrontab です。

おもに、.deb や .rpm などのパッケージを入れたときに作られます。代表的なものを挙げると logrotate と php です。logrotate はその名の通り。php は セッションキャッシュを破棄しています。

/etc/cron*

パッケージのファイルは /etc/ に集められています。

ココさえ見ておけばシステムに登録された定期実行をすべて知ることができる。

/etc にあるcron の例

root@sakura:~# tree  /etc/cron*
/etc/cron.d
├── php
└── sysstat
/etc/cron.daily
├── bsdmainutils
├── dpkg
├── logrotate
├── mlocate
├── ntp
└── sysstat
/etc/cron.hourly
/etc/cron.monthly
└── 0anacron
/etc/crontab 
/etc/cron.weekly
├── 0anacron
├── fstrim
└── man-db

システムのcron のファイルは /etc/cron* に集められています。

逆に言えば、crontab は ユーザーとして登録するだけではなく、自分で自由にファイル単位に分割して管理することができるのです。

cronファイルの各行を用途別に分割すれば、バージョン管理と合わせ技ができてとても便利だと言えます。

システムの cron.d へ登録

前述したファイルの構造を知っていれば、システムにcronを追加したいとき、次のように書くことができまsy.

sudo  sh -c "echo 0 * * * * command_a > /etc/cron.d/mycron" 
sudo  sh -c "echo 0 * * * * command_b >> /etc/cron.d/mycron" 

cronに登録するコマンドを用意できます。 こうすれば、「手作業でsudo crontab -e から編集してください」などと、ミスが起きやすい糞マニュアルを書かず済みますね。

/etc/cron.weekly /etc/cron.daily /etc/cron.hourlyについて

ここには定期実行したい「スクリプト」を設置します。

実行フラグ(+x)が付与されたスクリプトを設置すれば実行されます。

メリット2:コマンドの記述がシンプル

たとえば、毎日1回 0:00 に コマンドを実行したいのであれば、次のように書くことができます。

@daily /path/to/my_command > /dev/null

楽ですね。<時間・コマンド>を記述するだけです。シンプルです。コマンドだけです。

もし複雑なコマンドを実行したいのであれば、コマンドをファイルに記述してchmod +x するだけです。

コマンドに書くのではなく、ファイルに独立させると管理とテストが容易になるので楽ですね。

cronはSHELL=sh で実行される。

基本的に cron は sh -c で実行されます。なので先程のコマンドの記述例であればつぎのように実行される。

sh -c '/path/to/my_command > /dev/null ' 

sh として実行されると思っておけば大丈夫です。

sh → bash に変える

もし、bashで cron を実行したいのであれば、次のように書くことができます。

SHELL=bash
@daily /path/to/my_command > /dev/null

bash に変えるメリット

bash に変えておけば、いくつかの bash らしい機能が使えます。

次のように、複数のコマンドを必ず2回実行する

@daily { /path/to/my_command ; /path/to/my_command ;  }> /dev/null

条件分岐

条件分岐はシェルスクリプトそのままなので if を使わずに && でやるのがシンプル

@daily assert_that && do_this 

前半で条件判定を書きます。コマンドが正常終了すると次が実行される。コマンドの正常終了(return 0)を以て次のコマンドに制御が移ります。

これも、bashでcrontを書いていれば少し複雑なコマンドを書けますね

@daily ( echo 1 && aaaaaaa ) && echo ok

シェルなので、 if 文も書けるでしょう。。があまりやらないよね。

@daily if echo 1 ; then echo 2 ; fi

シェルなのでシンプル

シェルなので、シンプルに書くことができる。crontab の書き方がわからないという場合は、cronよりも、たぶんシェルの書き方がわからないだけだと思います。

メリット3:メール通知

cron はその実行結果がメールで飛ばされます。しかし受け取ってない人がほとんどなのではないでしょうか。

受取先を指定しましょう。

MAILTO=takuya@example.com
@monthly  /home/takuya/repos/report  -t au_wallet 

上記のようにメール送信先を指定すると、実行結果がメアドに送信されてきます。

この送信はsendmail コマンドで行われるので、システムのsmtp を適切に設定しておく必要があります。

エラーだけほしい→シェル機能

エラーだけメールが欲しい場合は、リダイレクトするだけで解決

MAILTO=takuya@example.com
@monthly  /home/takuya/repos/report  -t au_wallet > /dev/null

これもシェルですね。

シェルの知識はLinuxでの基礎知識ですから、自信がないなら覚えましょう。

ChatOpsしたい

たとえば、slack に通知したいときはメールをトリガーにSlack投稿するフックを仕込んでおけばいいでしょう。メールをトリガーに実行するサービスはやまほどあります。

もちろんLinux内部で完結させることも可能です。

Linux でメールのローカル配送(外部に送らないで内部→内部ユーザへ送信)した後にフックを起動することももちろんできます。旧来からの メールの.forward 書くだけです。

メリット4:書式はパターン化してある。

この記事で書いていた @montly という時刻の指定方法はあまり出てこないのですが、覚えやすいので覚えておきましょう。

@daily まず最初に覚えておけばあまり困らないと思います。

書式は難しくない。

cron の書式が面倒くさいと感じる人もいるでしょう、逆です。書式があるからわかりやすいのです。

最初の 5 つのフィールドの代わりに、 8 つの特殊な文字列のいずれか 1 つを指定してもかまわない:

              文字列         意味
              ------         ----
              @reboot        起動時に一度だけ実行。
              @yearly        年に一度だけ実行 ("0 0 1 1 *" と等価)。
              @annually      (@yearly と同様)
              @monthly       月に一度だけ実行 ("0 0 1 * *" と等価)。
              @weekly        週に一度だけ実行 ("0 0 * * 0" と等価)。
              @daily         一日に一度だけ実行 ("0 0 * * *" と等価)。
              @midnight      (@daily と同様)
              @hourly        一時間に一度だけ実行 ("0 * * * *" と等価)。

ね?簡単でしょ?

日付時間を指定するには

crontab の書式は左から順番につぎのようになっています。

m h dom mon dow
0 0  10 * * path/to/command

これは単に英語で書いてあるだけです。

m: minute
h: hour
dom: day of month
mon: month
dow: day of week

覚えにくいときはこのように見てください

 ┌────────── minute (0 - 59)
 │ ┌──────── hour (0 - 23)
 │ │ ┌────── day of month (1 - 31)
 │ │ │ ┌──── month (1 - 12)
 │ │ │ │ ┌── day of week (0 - 6 => Sunday - Saturday, or
 │ │ │ │ │                1 - 7 => Monday - Sunday)
 ↓ ↓ ↓ ↓ ↓
 * * * * * command to be executed

ね?簡単でしょ?

書式を 日本語に訳するとこうです。

英語 日本語 指定可能な値
m 0-59
h 0-23
dom 月内日 1-31
mon 1-12 (もしくは名前。下記を参照
dow 曜日 0-7 (0 と 7 は日曜日。もしくは名前)

具体例を覚えておけば大丈夫です

ぐだぐだ書きましたが、書式が決まってるのだからテンプレです。

1 時間おきに実行したい

m h dow mon dow
0  *  *    *   *  command

これで毎時0分に実行されます。

毎日1回実行したい

m h dow mon dow
0  0  *    *   *  command

これで毎日0時0分に実行されます。

毎土曜1回実行したい

m h dow mon dow
0  0 *    *   SAT  command

これで毎週土曜日0時0分に実行されます。

曜日の指定はちょっと特殊です。

数字 英語名 日本語
0 SUN
1 MON
2 TUE
3 WED
4 THU
5 FRI
6 SAT
7 SUN 日(*2回目 Linuxだけ)

GNU/Linuxだけ0と7の両方が日曜日です. xBSDなどのUNIXでは、7(sun)は存在しないようです。

Linuxは0から数えるプログラマのほかに、1から数えるプログラマが存在することに対応しているんんでしょうね。優しいね。

まぁ0=SUN だと覚えておけばいいです。

でも、面倒くさいのでアルファベットで書くほうが楽です。chmod 755 より chmod u=rwx,g=rx,o=rx  がわかりやすいのと同じことです。

曜日は英語名も数字もOK

曜日は英語で指定しても数字でもオッケらしいです。

###次の例は、全て「毎週日曜日に実行」です。
0  0 *    *   SUN  
0  0 *    *   sun
0  0 *    *   0
0  0 *    *   7

ここだけは表記が揺れるので困りますね。

その他サンプル

といっても覚えにくいのはあるのでテンプレを書いていきましょう。

m h dow mon dow
### 毎週日曜日に実行
0  0 *    *   SUN  
### 3時間おきに実行
*/3 * * * * 
#### 毎月25 日の0:00に実行
0 0 25 * * 
#### 毎月5日の毎時に実行
0 * 5 * * * 
### 土日以外
0 0 * * 1-5
### 月金だけ
0 0 * * 1,5
### 奇数時間 1,3,5,7,9 に実行
0 1-9/2 * * * 

それでも覚えられない?

ちなみに、これはman を読めば書いてあります。 man 5 です。

man 5 crontab

こまってググってもネットには、この記事のようなポエムしかありません。まずman をみましょう。

cron がわからないのではなく、man の読み方を知らないのでありませんか。

man見ても、なぜか man crontab(1) を見てしまっていませんか?

takuya@:~$ man -k cron
cron (8)             - 予定されたコマンドを実行するデーモン(Vixie Cron)
crontab (1)          - 各ユーザーのための crontab ファイルを管理する (V3)
crontab (5)          - cron を駆動するための一覧表
dh_installcron (1)   - cron スクリプトを etc/cron.* へインストールする
anacrontab (5)       - configuration file for anacron
anacron (8)          - runs commands periodically

man -k cron で cron の man を探すと crontab(1) と crontab (5)があります。crontab(5)を見ると詳しく書いてあります。

書き方に迷ったらまずman を見ることをオススメします。

まとめ

cron について見直してみました。

cronのメリットは

  • ファイル単位
  • シェルコマンド書くだけ
  • 場所が決まってる
  • 書式が決まってる

の点に良さを感じています。

これらはLinux基礎知識だけで理解できます。基礎知識をちゃんと知っていれば問題なく扱えると思います。また /etc にファイルが集まりバージョン管理をしやすいところも良きところではないでしょうか。

systemd で書けると言われれば困るのですが、

おまけ

crontabのジョブが終わらないうちに、つぎのジョブが起動しちゃって困る?

そういうときは、taskspooler を使うと便利らしいですよ→ cron + α が欲しい時には ts(Task Spooler) のご利用をご検討下さいというメモ - ようへいの日々精進XP

わたしは、すでに実行中なら(pidファイルがあるなら)実行キャンセルにしてますけど。

関連資料

過去に書いた cron 関連の記事が散らかっているので、この記事にまとめて整理しました。よろしければ過去記事も見てやってください

そうだった、

うろ覚えなのですが「曜日と日付の同時設定はorで動いてしまう」みたいな制約ありませんでしたっけ(昔だけ?) - ngswのコメント / はてなブックマーク

間違えてましたすいません。ご指摘感謝です。

     注意:  コマンド実行の日は 2 つのフィールドで指定できる — 月内日および曜日である。 もし両方のフィールドが制限指
       定 (* 以外) であると、 いずれかのフィールドが現在時刻と合った時にコマンドが実行される。 例えば、
       ``30 4 1,15 * 5''
       とすると、毎月 1 日と 15 日および毎週金曜日の 午前 4:30 にコマンドが実行される。

おすすめのLinux書籍

Linuxの教科書とシェルコマンドの教科書が、いま一番でしょう。入門書として旬だと思います。ぜひ読んでみてください。

三宅さん、この本の著者とは知らず、ボドゲ会でハトクラやって遊んでたのです。ハトクラやりながらもっと訊いておけばよかったよ。素晴らしい書籍をありがとう御座います。

新しいLinuxの教科書

新しいLinuxの教科書

新しいシェルプログラミングの教科書

新しいシェルプログラミングの教科書

コマンドで重複ファイルを探す。

重複ファイルを探すコマンド

find . -type f -exec md5sum {} + | sort | uniq -w32 -dD

find して md5sum にかけて sort したら、uniq を取る

解説

コマンドがズラッと並ぶのでひとつずつ見ていく

現在のディレクトリ以下のエントリ(Dir/File)を列挙

find .

現在のディレクトリ以下のファイルを列挙

find . -type f 

現在のディレクトリ以下のファイルを探し、それぞれに md5sum コマンドを実行する

find . -type f -exec md5sum {} +

結果をアルファベット順にソートする(uniq は実行前にソートが必要です)

find . -type f -exec md5sum {} + | sort 

uniq で重複している行を探す。ただし行頭から32文字が一致したら同一とみなす

uniq -w32 

重複している行を全て表示する

uniq -w32 -dD

このコマンドのコツ

uniq -w32 がポイントです。

find -exec md5sum の実行結果は次のようになります。

d41d8cd98f00b204e9800998ecf8427e  ./2018-07-19/fileA
e171a382b037094552b57633061ee8b0  ./2018-05-31/fileB
e171a382b037094552b57633061ee8b0  ./2018-05-31/fileC

とfind の結果が並ぶのです。ここから各行の「先頭 32 文字」は md5sum の計算結果です。

ここから、cut (awk)して join してもいいのですが。

md5sum_outout | awk '{ print $1 } ' | uniq -d > dups
join dups md5sum_outout

ファイルを作ってしまうのが美しくない。

uniq のオプション

行の比較を最初のN文字で比較する物を使います。

      -w, --check-chars=N
              行の比較を最初の N 文字で行う

これを使うと、md5sum の結果の一覧を -w32 と書くことで 32バイトのmd5sum の結果をもれなくダブリを探せるわけです。

uniq のその他のオプション

       -d, --repeated
              重複した行のみ出力する。出力はグループ毎に 1 回行われる
       -D     重複する行を全て出力する

md5sum でいいの?

十分だと思います。ファイル数がせいぜい1M個以下でコリジョンする可能性は低いのではないでしょうか。

参考資料

https://unix.stackexchange.com/questions/277697/whats-the-quickest-way-to-find-duplicated-files/277767

realpathコマンドがなくても諦めずに、代替コマンドを使う

realpath コマンドが無い。

Ubuntuの最小インストールに、realpath コマンドがなかったんです。まぁalpineとかDockerイメージはちょっと便利なコマンドがなくて苦労する。

相対パス絶対パスに変換する目的でめっちゃ便利なんだけど

readlink でなんとかする

readlink なら 絶対にあるのでreadlink でなんとかする。

alias realpath=readlink -f

相対パス絶対パスに変換する目的であればこれでなんとかなる。

関連資料:その他の realpath の使い方

realpath は相対パスから相対パスを作れます。

bashでファイルパスの相対パスを得る(絶対パスの取得やパスの正規化も)/realpath利用 - それマグで!

source したファイルで自身のファイル名$0を知る

source my_bashrc.sh

bashの設定を読み込むときに、カレントディレクトリを知りたいというか、実行されている自分自身のファイルを知りたい

$0 で取れるだろうと思ったら甘かった。

my_bashrc

#!/bin/bash
echo $0

source してみる。

$source my_bashrc.sh
-bash

あっ、、駄目っぽい。

替わりの環境変数を使う

__FILE__ があればいいんだけど、bashにはないので別の環境変数を探すことに

my_bashrc

ファイルで使える変数をプリントして

#!/bin/bash
declare -p 
printenv

使えそうなものを探す

$source my_bashrc.sh | grep bashrc
declare -a BASH_ARGV=([0]="./my_bashrc.sh")
declare -a BASH_SOURCE=([0]="./my_bashrc.sh")
declare -r SCRIPT_NAME="/Users/takuya/Desktop/my_bashrc.sh"
declare -- _="my_bashrc.sh"

なるほど、この辺が使える。

使ってみた

my_bashrc

#!/bin/bash
echo ${SCRIPT_NAME}
echo $(readlink -f ${BASH_SOURCE[0]})
echo $(readlink -f ${BASH_ARGV[0]} )

行けそう。

$ source my_bashrc.sh 
/Users/takuya/Desktop/my_bashrc
/Users/takuya/Desktop/my_bashrc
/Users/takuya/Desktop/my_bashrc

結論

source されるファイルで、自分自身($0 /  __FILE__  ) を知るためには次を使う。

echo ${SCRIPT_NAME}

参考資料

シェルのshebang が実行されるときの引数を見てみる。

linux でコマンドを実行する shebang

shebang を指定すると、そのコマンドでプログラムが実行されますよね。

#!/usr/bin/env ruby
puts 1 

このとき、shebangの1行目に指定したコマンドにはどのように引数が渡されているのだろうか。 ちょっと試してみよう。

shebangで実行できるもの

shebang に指定できるものは、実行可能な「バイナリ」だけ。*1
shebang に指定した shebang を使ったファイルは無効になる。

そこで、バイナリを用意する

引数を表示するsample.c

#include <stdio.h>


int main ( int argc, char** argv ){

  for(  int idx=0; idx<argc; idx++){
    printf("argv[%d]= %s \n" , idx, argv[idx]);
  }
}

これをコンパイルして実行可能なプログラムを作る

gcc sample.c -o sample

shebang に指定

今作ったプログラムをshebang に指定する。

shebang-test

#!./sample

This is test. 
This body will not be executed 

shebang の書き方がよく見るものと違っていますが、これは相対パスです。フルパスで指定するのが面倒くさいので相対パスで指定しています。

実行してみる

それでは、このファイルに実行可能フラグをくっつけて実行してみましょうか。

takuya@~$ chmod a+x   shebang-test

実行した結果

takuya@~$ ./shebang-test
argv[0]= ./sample
argv[1]= ./shebang-test
  • 引数0に、作ったプログラムが表示されており、
  • 引数1に、作ったファイル名が指定されている。

オプションを付けた場合

オプションを付けてみましょう

takuya@~$ ./shebang-test this is argument from command line
argv[0]= ./sample
argv[1]= ./shebang-test
argv[2]= this
argv[3]= is
argv[4]= argument
argv[5]= from
argv[6]= command
argv[7]= line

ファイル名の後ろに追加されたのがわかります。

shebang 行にオプションを付けてみましょう

では、shebang行にオプションを付けてみます。

#!./sample from-shebang arguments 1 2 3

this is test

これで実行してみます。

shebang 行にオプションを付けた場合

takuya@~$ ./shebang-test
argv[0]= ./sample
argv[1]= from-shebang
argv[2]= arguments
argv[3]= 1
argv[4]= 2
argv[5]= 3
argv[6]= ./shebang-test

すると、コマンドー引数ーファイル名の順番になっていることがわかります。

両方に引数を付けてみる。

最後に両方にオプションを付けてみる。

takuya@~$ ./shebang-test a bc
argv[0]= ./sample
argv[1]= from-shebang
argv[2]= arguments
argv[3]= 1
argv[4]= 2
argv[5]= 3
argv[6]= ./shebang-test
argv[7]= a
argv[8]= bc

今度は、コマンド-引数ーファイル名ー引数になっていることがわかります。

shebang が実行されるまで

シェル(sh/bash)はexecv でコマンドを実行する。execv がコマンドを実行するときに、 shebang があればインタプリタだとして1行目を処理してくれる。

execv を直接コールすれば少しわかる気がする

execv-test-01.c

execvを使って最初に作ったコマンド sample を実行してみます。

#include <stdio.h>
#include <unistd.h>

int main (){
  char* argv[2] = {"aaaaaaaaa", NULL };
  execv("./sample", argv );
}

実行結果

takuya@~$ gcc  exec-test-01.c && ./a.out
argv[0]= aaaaaaaaa

shebangファイルをexecvで実行

shebang ファイルをexecv で実行してみると。

exec-test-02.c

#include <stdio.h>
#include <unistd.h>

int main (){

  char* argv[2] = {"aaaaaaaaa", NULL };
  execv("./shebang-test", argv );

}

これを実行すると

takuya@~$ gcc  exec-test02.c && ./a.out
argv[0]= ./sample
argv[1]= ./shebang-test

引数 aaaaaa を与えたはずだけど消えてしまった。ということは 最初の引数を重ね合わせはシェルの機能だったぽい (実験環境がmacOSで試したからこうなったという可能性もあるが、unistdがそんなにきょどうかわる??)

shebang の重ね合わせ

Since Linux 2.6.28, the kernel permits the interpreter of a script to itself be a script. This permission is recursive, up to a limit of four recursions, so that the interpreter may be a script which is interpreted by a script, and so on.

Linuxの最新版に限り、Shebangの重ね合わせが可能になっている。

参考資料

http://man7.org/linux/man-pages/man3/exec.3.html

広告

*1: 後述しますが、一般的なUNIXはそうなってる。Linux 2.68 以降はそうでもない。

SQLite のデータベース(テーブル)を取り出す。

sqliteの指定したテーブルをdump したい。

sqlite3 ってファイルをコピーしたら管理できるから、あんまり複雑なテーブルを作らないのですが。 あるテーブルだけを持ち出して移動したいときにどうするか。と考えたらファイルをコピーでは間に合わなかった。

sqlite でテーブルを取り出す。

sqlite3 file.db

データベースのファイルを開いて

sqlite > .dump table_name

これだけで、テーブルの中身が取り出せる。 しかし件数が多い場合はどうするのか

出力先を指定ファイルにする

out コマンドで結果の出力先を指定する。

sqlite > .out  my_table_dump.sql
sqlite > .dump table_name

余計なのも全部出ちゃうんだけどね。

別解:シェルから直接。CLIを経由しない

sqlite3 file.db '.dump table_name' > out.sql

出力先を切り替えればいいのと、SQLの代わりにSQLiteコマンドを実行さえすればいいのだから、シェルから与えておくので良い。

参考資料

http://www.sqlitetutorial.net/sqlite-dump/

広告