2012年5月19日土曜日

bashスクリプトでatexit(3)相当を行う

C 言語だと、atexit() という関数がありますが、bash にも似た機能があることを知りました。

bash スクリプトが Ctrl+C で途中中断される場合を想定し、スクリプトの処理途中で生成される一時ファイルを削除(ゴミ掃除)したい場合があります。そんな時には、trap "rm ..." SIGINT という具合に書くのが定石かと思っていました。しかし、そのような書き方だと、スクリプト全体の終了時にも rm の記述をすることになります。次のような具合に・・・
#!/bin/bash
#
# Name: test_trap_SIGINT.bash
#

TEMPFILE=$(mktemp /tmp/.${0##*/}.$RANDOM$RANDOM.XXXXXX)
trap "echo trapped ; rm -f $TEMPFILE" SIGINT

echo $TEMPFILE

# 一時ファイルを使った処理を行う ... 
sleep 10 # このサンプルでは、スリープ
#

rm -f $TEMPFILE
exit 0
このサンプルの実行イメージは次のようになります。
途中中断しない場合
# ./test_trap_SIGINT.bash 
/tmp/.test_trap_SIGINT.bash.2085610279.7jIDoS
# ls -l /tmp/.test_trap_SIGINT.bash.2085610279.7jIDoS
ls: /tmp/.test_trap_SIGINT.bash.2085610279.7jIDoS: No such file or directory

途中で Ctrl+C した場合
# ./test_trap_SIGINT.bash 
/tmp/.test_trap_SIGINT.bash.579227843.ewVGq4
trapped
# ls -l /tmp/.test_trap_SIGINT.bash.579227843.ewVGq4
ls: /tmp/.test_trap_SIGINT.bash.579227843.ewVGq4: No such file or directory
最近、このパターンでスクリプトを書いていたら、一時ファイル生成後に途中終了 (exit 1) させたい条件が複数あり、いちいち rm を記述しました。
しかしハテ?もしかして atexit(3) のような機能はないものか?
っと、help を見てみますと、、、
# help trap
trap: trap [arg] [signal_spec ...] or trap -l
    The command ARG is to be read and executed when the shell receives
    signal(s) SIGNAL_SPEC.  If ARG is absent all specified signals are
    reset to their original values.  If ARG is the null string each
    SIGNAL_SPEC is ignored by the shell and by the commands it invokes.
    If a SIGNAL_SPEC is EXIT (0) the command ARG is executed on exit from
    the shell.  If a SIGNAL_SPEC is DEBUG, ARG is executed after every
    command.  If ARG is `-p' then the trap commands associated with
    each SIGNAL_SPEC are displayed.  If no arguments are supplied or if
    only `-p' is given, trap prints the list of commands associated with
    each signal number.  Each SIGNAL_SPEC is either a signal name in 
    or a signal number.  `trap -l' prints a list of signal names and their
    corresponding numbers.  Note that a signal can be sent to the shell
    with "kill -signal $$".
あるではないですか・・・さすが bash ですね。
なので、次のように書けます。
#!/bin/bash
#
# Name: test_trap_EXIT.bash
#

TEMPFILE=$(mktemp /tmp/.${0##*/}.$RANDOM$RANDOM.XXXXXX)
trap "echo trapped ; rm -f $TEMPFILE" EXIT

echo $TEMPFILE

# 一時ファイルを使った処理を行う ... 
sleep 10 # このサンプルでは、スリープ
#

exit 0
注意点としては、もし trap "トラップ内容" SIGINT EXIT と書くと、Ctrl+C 中断の際にトラップ内容が2回実行されることになります。

2012年4月15日日曜日

Pythonには++演算子は無い

前に、Ruby に ++ 演算子(インクリメント演算子)が無いことを書きましたが、Python にも無いということを、認識しました。
もっとも、Python の場合にはループの抽象度が高いようで、これまでの自分の経験上では、for (i=0; i<10; i++) のようなコードが必要なことは、皆無ですが。

2012年2月11日土曜日

perl でオプション解析(Getopt::Long編)

前に、Getopt::Std編getoptコマンド編 を書きましたが、同じオプションを複数回指定するパターンが使いたかったので、Getopt::Long についても調べました。
#!/usr/bin/perl
#
# GetOptions-template.pl
#

use Getopt::Long ;

sub usage_exit {
        print STDERR "Usage: getopts-template.pl [-a] [-b] [-d dir] item1 item2 ...\n" ;
        exit 1 ;
}

print "@ARGV\n" ;       ####DEBUG
{
        local $SIG{__WARN__} = \&usage_exit ;
        GetOptions( "a" => \$opt_a, "b+" => \$opt_b, "d=s" => \$opt_d, "h" => \$opt_h ) ;
        ### see man Getopt::Long(3pm)
}

if (defined $opt_h) {
        usage_exit ;
}

$argc = scalar @ARGV ;
print "\$argc=$argc\n" ;        ####DEBUG
print "\@ARGV=@ARGV\n" ;        ####DEBUG
print "\$opt_a=$opt_a\n" ;      ####DEBUG
print "\$opt_b=$opt_b\n" ;      ####DEBUG
print "\$opt_d=$opt_d\n" ;      ####DEBUG
オンラインマニュアル(man Getopt::Long(3pm))によると、プラス記号を使うと、同じオプションの複数回指定に対応できるとのことなのですが、実際に試してみると、、、
# ./GetOptions-template.pl -a -bb
-a -bb
Usage: getopts-template.pl [-a] [-b] [-d dir] item1 item2 ...

# ./GetOptions-template.pl -a -b -b
-a -b -b
$argc=0
@ARGV=
$opt_a=1
$opt_b=2
$opt_d=
残念ながら、複数回指定可能と言っても、このように -bb とは指定できず、-b -b という具合に指定する必要があるようです。コードは少し長くなりますが、getoptコマンドを使う方法なら対応できます。
人気ブログランキングへ にほんブログ村 IT技術ブログへ