2010年9月19日日曜日

オプション解析 (getoptsとgetoptの使い分け)

この話題はきっとポピュラーで、ネット上に多数の情報があるものと思いますが、、、

これまで、bash スクリプトでオプション解析をする際には、bash 組み込みコマンドのgetopts を使っていて、それで十二分と思っていました。しかし、getopts だと、コマンドラインの後半でオプションを指定できないことに気がつきました。 具体的には、

Usage: command.bash [-a] [-d dir] item1 item2 ...

のような構文の場合に、-d dir を item の後ろに指定しても、認識されません。(もちろん、工夫すればできそうではありますが)

そこで、調べたところ、外部コマンドの getopt なら期待通り処理してくれることを知りました。自分の中の整理のため、テンプレートを作りました。

まずは、getopts 版。いつもこのパターンを使ってました。
#!/bin/bash
#
# getopts-template.bash (use builtin getopts)
#
export LANG=C
usage_exit() {
        echo "Usage: getopts-template.bash [-a] [-d dir] item1 item2 ..." 1>&2
        exit 1
}
#
# Options
#
echo "$@"       ####DEBUG
while getopts ":ad:h" GETOPTS
do
  case $GETOPTS in
  a)    A_FLAG=yes
        ;;
  d)    OUTDIR=$OPTARG
        ;;
  h)    usage_exit
        ;;
  *)    usage_exit
        ;;
  esac
done
#
shift $((OPTIND - 1))
#
echo \$#=$#             ####DEBUG
echo \$@="$@"           ####DEBUG
echo A_FLAG=$A_FLAG     ####DEBUG
echo OUTDIR=$OUTDIR     ####DEBUG
次に、getopt 版です。
#!/bin/bash
#
# getopt-template.bash (use getopt utility)
#
export LANG=C
usage_exit() {
        echo "Usage: getopt-template.bash [-a] [-d dir] item1 item2 ..." 1>&2
        exit 1
}
#
# Options
#
echo "$@"       ####DEBUG
GETOPT=`getopt -q -o ad:h -- "$@"` ; [ $? != 0 ] && usage_exit
eval set -- "$GETOPT"
echo "$@"       ####DEBUG
while true
do
  case $1 in
  -a)   A_FLAG=yes      ; shift
        ;;
  -d)   OUTDIR=$2       ; shift 2
        ;;
  -h)   usage_exit
        ;;
  --)   shift ; break
        ;;
  *)    usage_exit
        ;;
  esac
done
#
echo \$#=$#             ####DEBUG
echo \$@="$@"           ####DEBUG
echo A_FLAG=$A_FLAG     ####DEBUG
echo OUTDIR=$OUTDIR     ####DEBUG
こちらの、getopt 版であれば、次のように -d オプションの指定を後半に行うことができます。
# ./getopt-template.bash item1 item2 -d dir
item1 item2 -d dir
-d dir -- item1 item2
$#=2
$@=item1 item2
A_FLAG=
OUTDIR=dir
一方、getopts の場合には、次のようになってしまいます。
# ./getopts-template.bash item1 item2 -d dir
item1 item2 -d dir
$#=4
$@=item1 item2 -d dir
A_FLAG=
OUTDIR=

2010-09-25追記
まとめを書き忘れていました。getopts のほうがスッキリすると思うので、itemが不要な場合(将来も不要かどうかも一考はしつつ)は、getoptsで書き、item 指定がある構文を使う場合には、getopt を使う。というガイドラインでやっていこうと思います。

2011-02-13追記
下記も参照ください。ロングオプションの場合です。
getoptによるロングオプション解析

2011-08-31追記
perl の場合についても書きましたので、興味があるようでしたら、参照ください。
perl でオプション解析

2012-05-20追記
Python の場合についても書きました。
Python でオプション解析

2010年9月12日日曜日

perl 5.12

少し遅れをとってしまいましたが、Fedora 14 Alpha を試しました。 このバージョンでは、perl が 5.12 にアップしていて、手持ちのスクリプトを試したら、一部動かなくなっていました。 調べたところ、split した結果が @_ に入ることを期待したコードがNGでした。ラクダ本VOLUME 2 28章 特殊変数によると、旧式の機能なので使うなとのこと。 実際にやっちゃっていたコードは次のうようなものです。
...
  split("\n", $temp) ;
  return @_ ;
}
これを、
...
  return split("\n", $temp) ;
}
と修正しました。今考えれば、なぜ、分割していたのかと思えますが、perl の理解が足りなかったということですね。

2010年8月31日火曜日

kdump に lzop を使う方法

kdump にはダンプファイルを圧縮する設定があり、普通は core_collector ディレクティブに makedumpfile -c を指定するという方法をとる。ただ、圧縮処理には結構な時間がかかり、これを短縮する方法はないかと考えてみた。

makedumpfile -c を指定した場合の initrd内のinit には、次のような記述が埋め込まれる。
mount -t ext3 $DUMPDEV /mnt
if [ $? == 0 ]
then
  mkdir -p /mnt///127.0.0.1-$DATE
  VMCORE=/mnt///127.0.0.1-$DATE/vmcore
  export VMCORE
  makedumpfile -c /proc/vmcore $VMCORE-incomplete >/dev/null
つまるところ core_collector に指定するフィルタプログラムは cp のシンタックスを満たせば良いようだ。 数ある圧縮プログラムの中で、高速であることを特徴とするものに lzop というプログラムがあり、これを使いたい。ただ、コマンドラインオプションを細工しても、cp のシンタックスを満たすようにはできないので、一皮かぶせる。
#! /sbin/busybox msh
/usr/bin/lzop -o $2 $1
こいつを、/etc/kdump.conf に次のような形で指定する。
ext3 LABEL=/1
path /var/crash
extra_bins /usr/bin/lzop /usr/bin/lzop.copy
core_collector lzop.copy
そうすると、initrd 内に lzop 関連のファイルが全部入る。
# zcat /boot/initrd-2.6.18-194.el5kdump.img | cpio -tv | grep lzo
-rwxr-xr-x   1 root     root       125672 Aug 31 23:19 usr/lib64/liblzo2.so.2
-rwxr-xr-x   1 root     root           44 Aug 31 23:19 usr/bin/lzop.copy
-rwxr-xr-x   1 root     root        67288 Aug 31 23:19 usr/bin/lzop
17070 blocks
initrd内のinitは次のように展開される。
mount -t ext3 $DUMPDEV /mnt
if [ $? == 0 ]
then
  mkdir -p /mnt//var/crash/127.0.0.1-$DATE
  VMCORE=/mnt//var/crash/127.0.0.1-$DATE/vmcore
  export VMCORE
  lzop.copy /proc/vmcore $VMCORE-incomplete >/dev/null
これでうまいこと、lzop 圧縮でダンプファイルが生成できました。
# pwd
/var/crash/127.0.0.1-2010-08-31-23:27:08
# file vmcore 
vmcore: lzop compressed data - version 1.020, LZO1X-1, os: Unix
gzip や bzip2 でも、同様にできるものと思いますが、速度の面から考えて、有用なのは lzop だけなのではと思います。
なお、lzop は RHEL6 Beta2 には含まれてましたが、RHEL5 には含まれていませんので、rpmfind などから拾ってくる必要があります。

2010-12-05追記
lzop で採取されたダンプの参照方法と、lzop を用いた場合の効果を書いていなかったので、まとめたいと思います。
まず、lzop で採取されたダンプの解凍方法とダンプ参照方法。
# ls -l vmcore
-r-------- 1 root root 922628641 Dec  5 11:50 vmcore.lzo

# file vmcore 
vmcore: lzop compressed data - version 1.020, LZO1X-1, os: Unix

# mv vmcore vmcore.lzo

# lzop -d vmcore.lzo 

# ls -l vmcore
-r-------- 1 root root 4122392804 Dec  5 11:50 vmcore

# crash /boot/System.map-2.6.18-194.el5 /usr/lib/debug/lib/modules/2.6.18-194.el5/vmlinux vmcore

  SYSTEM MAP: /boot/System.map-2.6.18-194.el5                          
DEBUG KERNEL: /usr/lib/debug/lib/modules/2.6.18-194.el5/vmlinux (2.6.18-194.el5)
    DUMPFILE: vmcore
        CPUS: 2
        DATE: Sun Dec  5 11:49:50 2010
      UPTIME: 00:43:27
LOAD AVERAGE: 2.03, 1.71, 1.33
       TASKS: 144
    NODENAME: my39
     RELEASE: 2.6.18-194.el5
     VERSION: #1 SMP Fri Apr 2 14:58:14 EDT 2010
     MACHINE: x86_64  (1196 Mhz)
      MEMORY: 3.9 GB
       PANIC: "SysRq : Trigger a crashdump"
         PID: 7544
     COMMAND: "bash"
        TASK: ffff81011dbf7040  [THREAD_INFO: ffff81010fdf2000]
         CPU: 1
       STATE: TASK_RUNNING (SYSRQ)

crash> 
次に同じマシンで makedumpfile -c -d 1 で採取したダンプの情報です。こちらの場合は、圧縮したまま crash に渡すことができます。
# ls -l vmcore 
-rw------- 1 root root 848124021 Dec  5 12:03 vmcore

# file vmcore 
vmcore: data

# crash /boot/System.map-2.6.18-194.el5 /usr/lib/debug/lib/modules/2.6.18-194.el5/vmlinux vmcore

  SYSTEM MAP: /boot/System.map-2.6.18-194.el5                          
DEBUG KERNEL: /usr/lib/debug/lib/modules/2.6.18-194.el5/vmlinux (2.6.18-194.el5)
    DUMPFILE: vmcore  [PARTIAL DUMP]
        CPUS: 2
        DATE: Sun Dec  5 11:56:53 2010
      UPTIME: 00:04:52
LOAD AVERAGE: 0.23, 0.29, 0.13
       TASKS: 144
    NODENAME: my39
     RELEASE: 2.6.18-194.el5
     VERSION: #1 SMP Fri Apr 2 14:58:14 EDT 2010
     MACHINE: x86_64  (1197 Mhz)
      MEMORY: 3.9 GB
       PANIC: "SysRq : Trigger a crashdump"
         PID: 6310
     COMMAND: "bash"
        TASK: ffff8101371d07e0  [THREAD_INFO: ffff81013ad80000]
         CPU: 1
       STATE: TASK_RUNNING (SYSRQ)

crash> help -n | grep dump_level
          dump_level: 1 (0x1) (DUMP_EXCLUDE_ZERO)
lzop で採取した場合と、makedumpfile -c -d 1 で採取した場合のデータを整理すると、次のようになります。

■ lzop で採取した場合
ダンプ採取所要時間:1分22秒(kdump_post を使ってタイムスタンプを記録して測定)
ダンプファイルサイズ:922628641

■ makedumpfile -c -d 1 で採取した場合
ダンプ採取所要時間:6分34秒(kdump_post を使ってタイムスタンプを記録して測定)
ダンプファイルサイズ:848124021

このように、ダンプファイルサイズが少し大きくなる程度で、ダンプ採取時間が大幅に短縮(=ダウンタイムが大幅に短縮)でき、LZO の威力(高速圧縮性能)が発揮された結果となりました。 最後になりましたが、実験は、ThinkPad X300 メモリ4GB + CentOS 5.5 x86_64 で行いました。環境情報を示します。
# uversion 
CentOS release 5.5                      647818J

BIOS version  : 7TET36WW (1.10 )  05/11/2009
System serial : xxxxxxx

CPU model  : Intel(R) Core(TM)2 Duo CPU     L7100  @ 1.20GHz
Processors : 2  (1 sockets, 2 cores per CPU, HT: not supported or disabled)

Memory : 3780 MB

Linux : 2.6.18-194.el5  x86_64  (my39)

この環境で、システム起動直後にテストデータ(Webサーバを想定してテキストデータ)でキャッシュを満タンにした状態でダンプ採取して、比較しました。

2011-06-19追記
dumpコマンドのLZO圧縮オプション
よければ、こちらも、参照ください。

2011-06-26追記
EPEL というのがあることを知り、確かめてみたら、lzop も入ってました。
http://download.fedora.redhat.com/pub/epel/5/

2012-11-25追記
64bit版の CentOS 6 または RHEL6 であれば、lzop が収録されています。

2013-12-07追記
CentOS 6.5 で、makedumpfile (kexec-tools パッケージに収録) に -l オプション (LZO 圧縮サポート) が追加されましたので、上のような小細工は必要なくなりました。同時に Snappy のサポート (-p オプション) も追加されています。どちらが効果的かは、サーバの使い方によるかと思います。
人気ブログランキングへ にほんブログ村 IT技術ブログへ