カメニッキ

カメとインコと釣りの人です

最近覚えたシェルスクリプトの小ネタ

シェルスクリプトから教えていただいた。忘れないように書いとく

(追記)聞いて、自分の記憶した内容をそのまま書いちゃったので、ちゃんとマニュアル通りか確認してなかったので反省

$ man bash

目次

  • xargsでfunctionを叩く
  • 連想配列もどき
  • 変数間接参照
  • なんでもかんでもawkで整形しない
  • 文字列の末尾から数えて○文字目を△文字取り出す
  • 番外編:やたら if [ ]; then を使わない

Markdownで目次の書き方がわからんかった

xargsでfunctionを叩く


下の例だと find xxxx | xargs cp xxxx ってかけば良さそうでイマイチだけど、もっと複雑な処理やらせたいときに。

  • 前はこう書いてた
  #!/bin/bash

  for x in `find /var/www -name xxx`
  do
    cmd="cp -v /hoge/fuga/xxx $x"
    [[ `md5sum $x | awk '{print $1}'` != "xxxxxxxxxxxxxxxxxxxxxxx" ]] && eval $cmd | :
  done
  • xargsで5多重で同じことやらせる
#!/bin/bash

function sample() {
  cmd="cp -v /hoge/fuga/xxx $1"
  [[ `md5sum $1 | awk '{print $1}'` != "xxxxxxxxxxxxxxxxxxxxxxx" ]] && eval $cmd | :

}

# これしたらxargsから呼べることをしらなかった
export -f sample

find /var/www -name xxx | xargs -P5 -I{} bash -c "sample {}"

連想配列もどき


# 連想配列もどきのHを定義
declare -A H
H["a"]="682f7xxxxxxxxxxxxxxxxxxx0"
H["b"]="7808axxxxxxxxxxxxxxxxxxx2"
H["c"]="c6ba1xxxxxxxxxxxxxxxxxxx6"
H["d"]="64186xxxxxxxxxxxxxxxxxxxb"
H["e"]="50d1dxxxxxxxxxxxxxxxxxxxc"

for x in a b c d e
do
# とりだす
echo ${H["$x"]}
done

ちなみに、

  • こっちは配列見える
declare -A H
H["a"]="682f7xxxxxxxxxxxxxxxxxxx0"
H["b"]="7808axxxxxxxxxxxxxxxxxxx2"
H["c"]="c6ba1xxxxxxxxxxxxxxxxxxx6"
H["d"]="64186xxxxxxxxxxxxxxxxxxxb"
H["e"]="50d1dxxxxxxxxxxxxxxxxxxxc"

function sample(){
  # 682f7xxxxxxxxxxxxxxxxxxx0 
  echo ${H["a"]}
}

sample
  • こっちは配列見えない
#!/bin/bash

declare -A H
H["a"]="682f7xxxxxxxxxxxxxxxxxxx0"
H["b"]="7808axxxxxxxxxxxxxxxxxxx2"
H["c"]="c6ba1xxxxxxxxxxxxxxxxxxx6"
H["d"]="64186xxxxxxxxxxxxxxxxxxxb"
H["e"]="50d1dxxxxxxxxxxxxxxxxxxxc"

function sample(){
  # 682f7xxxxxxxxxxxxxxxxxxx0
  echo ${H["a"]}
  echo "hoge"
}
# ここで配列はexportされないらしいけど、あんまり詳しく調べてない。当たり前の動き?
export -f sample
find ~/test -type f  | xargs bash -c "sample"

変数間接参照


#!/bin/bash

var1="xxxxxxxxxxxxxxxx"
var2="yyyyyyyyyyyyyyyy"
var3="zzzzzzzzzzzzzzzz"

for i in `seq 1 3`
do  
  # 変数を使って作る変数名は一度変数に入れなきゃだめ
  tmp="var${i}"
  echo ${!tmp}
done

なんでもかんでもawkで整形しない


/home/hoge/fuga/piyo/php5.2.cgi のようなパスから、末尾の php5.2.cgi のみ抽出し、かつその値を利用して色々処理したい時に、つい以下のようなことを書いてた。

[root@hoge ~]# cat test.sh
#!/bin/bash

for x in `find ~/home/users/php-bin/ -path "*/.php-bin/php*.cgi"`
do
echo $x | awk -F/ '{print $10}'
done

このくらいのことをawk出してるとコストが高いので、bashの変数操作を使って以下の様に書く

[root@hoge ~]# cat test2.sh
#!/bin/bash

for x in `find ~/home/users/php-bin/ -path "*/.php-bin/php*.cgi"`
do
echo ${x##/*/}
done

すると

[root@hoge ~]# time ./test.sh
・・・
real    0m8.316s
user    0m2.367s
sys 0m7.828s

[root@hoge ~]# time ./test2.sh
・・・
real    0m0.763s
user    0m0.357s
sys 0m0.394s

圧倒的に早い!!!

捕捉 この例だと

find ~/home/users/php-bin/ -path "*/.php-bin/php*.cgi" | awk -F/ '{print $10}'

でいいけど、今回のこれは、①findしたパス②awkで整形した値両方を使用して、いろいろ処理する必要があって、forを利用してた

文字列の末尾から数えて○文字目を△文字取り出す

以下は乱暴な例ですが、 5.xx の値に応じて処理したい要件があった。 先頭の文字はファイルによってバラバラの長さのため、cutコマンドでは取得できないため、「末尾から何文字目」という指定をしたかった

[user ~/work/test]$ ll
total 0
-rw-r--r--  1 user  staff     0B  6 28 00:49 hogehogeho_ver5.2.sh
-rw-r--r--  1 user  staff     0B  6 28 00:49 xxxx.yyyy-abcdefghijk_ver5.2.sh
-rw-r--r--  1 user  staff     0B  6 28 00:49 xxxxxxxxxxxxxxxxxxxxxxxxxxx_ver5.2.sh
-rw-r--r--  1 user  staff     0B  6 28 00:49 yyyyyyyyyyyy-aaaaaaaaaaaaaaaa_ver5.3.sh
[user ~/work/test]$ for x in `find . -type f`
> do
> echo ${x:${#x}-4:1}
> done
2
2
2
3

番外編:やたら if [ ]; then を使わない


testコマンドを使うとフォークしてしまって大量に叩くとパフォーマンス悪いとのこと。

[PMAC226S ~/work]$ cat test.sh
#!/bin/bash

for i in `seq 1 1000000`
do
  if [ "$a" = "hoge" ]; then
    echo "hoge"
  else
    echo "fuga"
  fi
done

[PMAC226S ~/work]$ cat test2.sh
#!/bin/bash

for i in `seq 1 1000000`
do
  [[ "$a" = "hoge" ]]  && echo "hoge" || echo "fuga"
done

があった時、後者の方が結構はやい 

[PMAC226S ~/work]$ time ./test.sh
・・・
real    0m30.164s
user    0m20.777s
sys 0m2.917s

[PMAC226S ~/work]$ time ./test2.sh
・・・
real    0m25.246s
user    0m17.777s
sys 0m1.812s

第9回 コンテナ型仮想化の情報交換会@福岡で「Alpine Linux 入門した」という発表をした

人生初しゃべりでした。声が震えてた

speakerdeck.com

内容のレベルは正直低くて、まだまだ勉強不足感がありあり。
だけど、特性を知った上で計測をしつつ、良い使い道を模索できたので、そんなに悪い結果ではなかったと思いたい。


スライド内で使用したDockerfileはGithub

github.com


(追記)サイズの話ばかりしてたけど、小さいってことは余計なものが入ってないから、セキュアなのでした。 公式サイトにも記載あるし。

よく使うGitHubのリポジトリは、ブラウザのカスタム検索設定をしておくと便利

よくあるアレ 設定が面倒くさくて放置してたけど、便利

やりたいこと

ブラウザのアドレスバーから https://git.tahira.com/hoge/fuga のissue検索を行いたい

通常のフローだと、3ステップ

  1. https://git.tahira.com/hoge/fuga へアクセス
  2. 左上の検索欄にキーワード入力して検索
  3. issueタブを選択

設定すると、1ステップ

  1. ブラウザのアドレスバーから直接ジャンプ

設定方法

GoogleChromeの場合

(1) GoogleChromeの設定画面から 検索エンジンの管理... を選択

f:id:tapira:20160323113031p:plain

(2) 検索エンジンを追加 より新規追加

f:id:tapira:20160323113606p:plain

  • 検索エンジンを追加・・・任意の名前
  • キーワード・・・アドレスバーからカスタム検索を実行するためのキーワード(なんでもよい)
  • URL・・・通常のフローで検索した際のアドレスバーからコピる。サンプル: https://git.tahira.com/hoge/fuga/search?q=検索したいキーワード&type=Issues&utf8=%E2%9C%93 これの、 q=XXXXXXq=%s に修正して記載

使用方法

(1) アドレスバーに設定したキーワード hogefuga と入力

f:id:tapira:20160323113659p:plain

(2) スペースを開けると GH:E hogefuga を検索 と表示が切り替わるので続けて検索したいキーワードを入力しEnter

f:id:tapira:20160323113747p:plain

以上!

timestampを見易い形式に変換するAlfredワークフローを作った

nagiosのログを眺めてたら日時がタイムスタンプで表示されて辛かった

雑なので中身は見ないでほしい

github.com

https://cloud.githubusercontent.com/assets/12387636/13593175/6830d89e-e53c-11e5-889d-494cadec1190.gif

※ Cmd + Opt + Tで選択したタイムスタンプを渡すようにしてる ※ Macなら date -r timestamp で変換してくれるから別にいらんかった

Apache拡張モジュール「mod_resource_checker」のソースを眺めてる (途中)

はじめに

  • Cのお勉強のいっかんとして取り組んでいます
  • なので、誤った情報である可能性があります
  • 自分用の覚え書き情報が主なので、役に立たないかもしれません

お題

github.com

mod_resource_checker?

Process Resource Logging Module using JSON format into file or piped program.

アクセス毎にApacheプロセスの使用したOSのリソース情報を、JSON形式でログ出力することができます。

[PMAC226S ~/Downloads]$ tail -1 resource.log | jq .
{
  "module": "mod_resource_checker",
  "date": "Sun Feb 28 22:53:58 2016",
  "type": "RCheckALL",
  "unit": null,
  "location": "/var/www/*/*/web/",
  "remote_ip": "123.456.789.000",
  "filename": "/var/www/web/wp/index.php",
  "scheme": "http",
  "method": "GET",
  "hostname": "hoge.com",
  "server_ip": "192.168.0.111",
  "uri": "/wp/index.php",
  "real_server_name": null,
  "uid": 1234,
  "size": 418,
  "content_length": 0,
  "status": 200,
  "pid": 16431,
  "threshold": null,
  "response_time": 0,
  "result": {
    "RCheckUCPU": 0.552367,
    "RCheckSCPU": 0.00806,
    "RCheckMEM": 0.644531
  }
}

mod_resource_checker.c 読んでいきます

1. 末尾のモジュールをApacheへ登録する部分

722
723 #ifdef __APACHE24__
724 AP_DECLARE_MODULE(resource_checker) = {
725 #else
726 module AP_MODULE_DECLARE_DATA resource_checker_module = {
727 #endif
728     STANDARD20_MODULE_STUFF, (void *)resource_checker_create_dir_config, /* create per-dir    config structures */
729     NULL,                                                                /* merge  per-dir    config structures */
730     (void *)resource_checker_create_config,                              /* create per-server config structures */
731     NULL,                                                                /* merge  per-server config structures */
732     resource_checker_cmds,                                               /* table of config file commands       */
733     resource_checker_register_hooks                                      /* register hooks                      */
734 };

__APACHE24__ で判定しているのは、Apache2.4とそれ以前で定義方法に差異があるからでしょうか。
728〜732行は http://blog.matsumoto-r.jp/?p=603 を読んでお勉強。
とりあえず今は設定がちゃんとされてますか、の面倒をApache起動時に見るための記述という理解。
リクエストを処理する際に気にするところは、 resource_checker_register_hooks の中で設定されたhook関数群かな

2. 登録されたhook関数

715 static void resource_checker_register_hooks(apr_pool_t *p)
716 {
717   ap_hook_post_config((void *)resource_checker_init, NULL, NULL, APR_HOOK_MIDDLE);
718   ap_hook_access_checker(before_resource_checker, NULL, NULL, APR_HOOK_LAST);
719   ap_hook_fixups(resource_checker_handler, NULL, NULL, APR_HOOK_LAST);
720   ap_hook_log_transaction(after_resource_checker, NULL, NULL, APR_HOOK_LAST);
721 }

ap_hook_XXXX はhookさせたいタイミングに応じて、様々な種類がある。詳細は http://d.hatena.ne.jp/dayflower/20081029/1225266220http://blog.matsumoto-r.jp/?p=1625 にまとまっている。 今回のものでいくと、

  • ap_hook_post_config - 設定初期化時
  • ap_hook_access_checker - アクセス制御を行う前(なので、アクセス制御をおこなう)
  • ap_hook_fixups - よくわからん・・・
  • ap_hook_log_transaction - レスポンス後のロギング処理時

今回の mod_resource_checker はリソース情報をロギングすることが目的なので、 ap_hook_log_transaction が重要な仕事をやってそう。

3. ap_hook_log_transaction でやっていること

577 static int after_resource_checker(request_rec *r)
578  {
579    mod_rc_dir_conf *dconf = (mod_rc_dir_conf *)ap_get_module_config(r->per_dir_config, &resource_checker_module);
580    mod_rc_conf *sconf = (mod_rc_conf *)ap_get_module_config(r->server->module_config, &resource_checker_module);
581    mod_rc_rusage *before_resources = dconf->before_resources;
582
583    if (dconf->cpu_utime == INITIAL_VALUE && dconf->cpu_stime == INITIAL_VALUE && dconf->shared_mem == INITIAL_VALUE &&
584        dconf->check_status == OFF && dconf->check_all == OFF)
585      return DECLINED;
586
587    int match;
588    struct stat sb;
589    mod_rc_rusage *after_resources;
590    after_resources = (mod_rc_rusage *)apr_pcalloc(r->pool, sizeof(mod_rc_rusage));
591    mod_rc_rusage *use_resources;
592    use_resources = (mod_rc_rusage *)apr_pcalloc(r->pool, sizeof(mod_rc_rusage));
593
594    mod_rc_client_data *cdata;
595    cdata = (mod_rc_client_data *)apr_pcalloc(r->pool, sizeof(mod_rc_client_data));
596
597    if (resource_checker_initialized == 0) {
598      return OK;
599    }
600
601    if (r->main && (stat(r->filename, &sb) == -1) && errno == ENOENT) {
602      return OK;
603    }
604
605    cdata->access_uri = r->uri;
606    cdata->access_file = r->filename;
607  #ifdef __APACHE24__
608    cdata->access_src_ip = r->connection->client_ip;
609  #else
610    cdata->access_src_ip = r->connection->remote_ip;
611  #endif
612    cdata->access_dst_host = r->server->server_hostname;
613
614    if (dconf->check_status == ON) {
615      _mod_resource_checker_logging(r, 0, 0, NULL, dconf, cdata, MODULE_NAME, "RCheckSTATUS", NULL, r->pool);
616    }
617
618    // threashould check
619    if (before_resources == NULL) {
620      ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, "%s NOTICE %s: Can not check resource of the request: file = %s",
621                    MODULE_NAME, __func__, r->filename);
622      return DECLINED;
623    }
624
625    match = 0;
626    after_resources->cpu_utime = INITIAL_VALUE;
627    after_resources->cpu_stime = INITIAL_VALUE;
628    after_resources->shared_mem = INITIAL_VALUE;
629
630    if (dconf->cpu_utime > INITIAL_VALUE || dconf->check_all == ON) {
631      match = 1;
632      if (dconf->check_all == ON)
633        after_resources->cpu_utime = _get_rusage_resource(r->pool, "ALL", "cpu_utime");
634      else
635        after_resources->cpu_utime = _get_rusage_resource(r->pool, dconf->utime_process_type, "cpu_utime");
636    }
637
638    if (dconf->cpu_stime > INITIAL_VALUE || dconf->check_all == ON) {
639      match = 1;
640      if (dconf->check_all == ON)
641        after_resources->cpu_stime = _get_rusage_resource(r->pool, "ALL", "cpu_stime");
642      else
643        after_resources->cpu_stime = _get_rusage_resource(r->pool, dconf->stime_process_type, "cpu_stime");
644    }
645
646    if (dconf->shared_mem > INITIAL_VALUE || dconf->check_all == ON) {
647      match = 1;
648      if (dconf->check_all == ON)
649        after_resources->shared_mem = _get_rusage_resource(r->pool, "ALL", "shared_mem");
650      else
651        after_resources->shared_mem = _get_rusage_resource(r->pool, dconf->mem_process_type, "shared_mem");
652    }
653
654    if (match == 0) {
655      return OK;
656    }
657
658    use_resources->cpu_utime = after_resources->cpu_utime - before_resources->cpu_utime;
659    use_resources->cpu_stime = after_resources->cpu_stime - before_resources->cpu_stime;
660    use_resources->shared_mem = after_resources->shared_mem - before_resources->shared_mem;
661
662    // unexpected value; resource is negative number
663    if (use_resources->cpu_utime < 0) {
664      use_resources->cpu_utime = 0;
665    }
666    if (use_resources->cpu_stime < 0) {
667      use_resources->cpu_stime = 0;
668    }
669    if (use_resources->shared_mem < 0) {
670      use_resources->shared_mem = 0;
671    }
672
673    if (dconf->cpu_utime > INITIAL_VALUE && use_resources->cpu_utime >= dconf->cpu_utime) {
674      _mod_resource_checker_logging(r, use_resources->cpu_utime, dconf->cpu_utime, dconf->utime_process_type, dconf,
675                                    cdata, MODULE_NAME, "RCheckUCPU", "sec", r->pool);
676    }
677
678    if (dconf->cpu_stime > INITIAL_VALUE && use_resources->cpu_stime >= dconf->cpu_stime) {
679      _mod_resource_checker_logging(r, use_resources->cpu_stime, dconf->cpu_stime, dconf->stime_process_type, dconf,
680                                    cdata, MODULE_NAME, "RCheckSCPU", "sec", r->pool);
681    }
682
683    if (dconf->shared_mem > INITIAL_VALUE && use_resources->shared_mem >= dconf->shared_mem) {
684      _mod_resource_checker_logging(r, use_resources->shared_mem, dconf->shared_mem, dconf->mem_process_type, dconf,
685                                    cdata, MODULE_NAME, "RCheckMEM", "MiB", r->pool);
686    }
687
688    if (dconf->check_all == ON && dconf->json_fmt == ON) {
689      _mod_resource_checker_logging_all(r, use_resources, dconf, sconf, cdata, r->pool);
690    }
691
692    return OK;
693  }

アクセス元IP・CPU使用量・メモリ使用量などかきあつめて、どこまで記録するかの設定に応じてロギング関数に投げてるのかな。

メモ

  • 下部のモジュール定義の書き方、処理させたいタイミングに応じてhook関数を設定する、というやり方は他のApacheモジュールでも同じっぽくて、今後読むときはそこから見ていこう
  • 様々なhookさせるポイントがあって、むずかしい。
  • 調べてもあんまり情報でてこなくて辛い

ほとんど独り言みたいな内容になってしまった。おしまい