unix

killできれいに死ぬシェルスクリプト

止めるまで動きつづける統計値を延々と出力するようなプログラムの出力を加工するスクリプトというのがあるとする。

たとえばeth0のトラフィックをkB/sを表示するもの:

[sar.sh]
#!/bin/sh
/usr/bin/sar -n DEV 1 | awk '$2=="eth0"{print $6,$5}'

で、これをつかってあるプログラムを実行したときのトラフィックを記録したいとする。

[test.sh]
#!/bin/sh
./sar.sh >sar.out &
pid_sar=$!

long_program

kill $pid_sar
cat sar.out

プログラムの実行が終わったらkillしているのだが、シェルが終了するだけでsarやawkは動いたままになる。

シェルは死ぬときにフォアグラウンドのプロセスをkillしてはくれないのだ。

コマンドラインから ./sar.sh を直接実行した場合はシェル(/bin/sh ./sar.sh)だけでなくsarもawkも終了するのは、Ctrl-Cを押したときに端末ドライバ(tty)がフォアグラウンドプロセスグループに対してSIGINTを送ってくれるためだ。UNIXは今となっては無駄に端末まわりの機能が豊富なのだ。プロセスグループをつくってくれるのはコマンドラインを解釈しているログインシェルである。

スクリプト1の中からスクリプト2を実行した場合にスクリプト2がきれいに終了するには、そういうふうにシェルスクリプトを書けばよいとおもうところだが、pipeでつないでいるので個別のプログラムのpidがわからないという問題が。

ということで、一番簡単なプロセスグループをつくってしまえばokという解決方法をとることにした。

こんなかんじで自分をプロセスグループリーダにしてプロセスグループをつくる。

[setpgid.c]
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
int
main(int ac, char **av)
{
        char *prog = av[0];
        if (ac < 2)
                return 1;
        if (setpgid(0, 0) < 0) {
                fprintf(stderr, "%s:setpgid:%s\n", prog, strerror(errno));
                return 1;
        }
        execvp(av[1], av+1);
        fprintf(stderr, "%s:exec:%s\n", prog, strerror(errno));
        return 1;
}

つかいかたはこんかかんじ。コマンドを起動するときにsetpgidをはさむのと、killするときにプロセスグループにシグナルを送るためにpidにマイナスをつける。

[test.sh]
#!/bin/sh
./setpgid ./sar.sh >sar.out &
pid_sar=$!

long_program

kill -TERM -$pid_sar
cat sar.out

でもいちいち呼出側にsetpgidを入れてまわるのは頭わるいのでshebangに入れる。

[sar.sh]
#!./setpgid /bin/sh
/usr/bin/sar -n DEV 1 | awk '$2=="eth0"{print $6,$5}'
[test.sh]
#!/bin/sh
./sar.sh >sar.out &
pid_sar=$!

long_program

kill -TERM -$pid_sar
cat sar.out

プロセスグループにシグナルを送るのは自分でやるならシグナルをつかまえて自分のプロセスグループにシグナルを送りなおせばいいはず。

[sar.sh]
#!./setpgid /bin/sh
die() {
        echo "bye"
        trap - TERM INT
        kill -TERM -$$
}
trap die TERM INT
/usr/bin/sar -n DEV 1 | awk '$2=="eth0"{print $6,$5}'

とおもってやってみるとシェルがシグナルをうけとっても、シェルレベルのシグナルハンドラを実行してくれない。どうもシェル依存のようだ。バックグランドで実行してwaitで待てばシグナルハンドラを実行してくれるようだが、POSIX規格で決まっているわけではないので絶対安全というわけではないらしい。

#!./setpgid /bin/sh
die() {
        echo "bye"
        trap - TERM INT
        kill -TERM -$$
}
trap die TERM INT
/usr/bin/sar -n DEV 1 | awk '$2=="eth0"{print $6,$5}' & wait

とりあえずの妥協点としてはこんなところか。

http://togetter.com/li/942376

koie

autoconfで構造体のサイズを調べる方法

多言語からCのライブラリのラッパーを実装するときに、構造体の大きさを知りたいということは、ちょくちょくあるとおもう。

configure.ac

AC_PREREQ(2.59)
AC_INIT([oreore], [0.1], [info@example.com])
AC_CONFIG_HEADERS([config.h])
AC_CHECK_SIZEOF([pthread_mutex_t], [], [#include <pthread.h>])
AC_SUBST([HOGE], [$ac_cv_sizeof_pthread_mutex_t])
AC_CONFIG_FILES([boke.txt])
AC_OUTPUT

config.h.in

#undef SIZEOF_PTHREAD_MUTEX_T

boke.txt.in

sizeof pthread_mutex_t is @HOGE@.

実行例

% autoconf
% ./configure
checking for gcc... no
checking for cc... cc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether cc accepts -g... yes
checking for cc option to accept ISO C89... none needed
checking size of pthread_mutex_t... 8
configure: creating ./config.status
config.status: creating boke.txt
config.status: creating config.h
% cat config.h
/* config.h.  Generated from config.h.in by configure.  */
#define SIZEOF_PTHREAD_MUTEX_T 8
% cat boke.txt
sizeof pthread_mutex_t is 8.
%

koie

シェルスクリプトで一時的代入の挙動(2)

ここからの続き

name=value command 構文

commandのところをいろいろ変えて、コマンド行での変数代入が永続的かどうか調べてみた。

  • nullコマンド(built-in)
  • trueコマンド(freebsd shとdashではbuilt-in、bashでは外部コマンド→うそでしたbuilt-inでした)
  • ユーザ定義関数
  • 外部コマンド
テストスクリプト
X1=YES :
echo "X1=${X1-NO}"
printenv X1

X2=YES true
echo "X2=${X2-NO}"
printenv X2

g() {
    :
}
X3=YES g
echo "X3=${X3-NO}"
printenv X3

X4=YES cal >/dev/null
echo "X4=${X4-NO}"
printenv X4

f() {
    X=FUNC
}
X=CMD f
echo "X=${X-NO}"
printenv X
fbsd sh
X1=YES
X2=NO
X3=NO
X4=NO
X=NO
bash
X1=NO
X2=NO
X3=NO
X4=NO
X=NO
bash -o posix
X1=YES
YES
X2=NO
X3=YES
YES
X4=NO
X=FUNC
FUNC
dash
X1=YES
X2=NO
X3=YES
X4=NO
X=FUNC
結果

永続的か?

Shell nullコマンド 関数 trueコマンド 外部コマンド
fbsd sh YES NO NO NO
bash NO NO NO NO
bash -o posix YES+環境変数 YES+環境変数 NO NO
dash YES YES NO NO

コマンド行の代入と呼ばれた関数内での代入の競合は?

Shell 結果
fbsd sh 永続的でない
bash 永続的でない
bash -o posix 永続的+環境変数
dash 永続的

koie

シェルスクリプトで一時的代入の挙動

シュルスクリプトで、コマンドの前に代入を書くとコマンドを実行するときに一時的にシェル変数と環境変数を設定できて便利。

よくある用法

readで空白も読みたいときにIFS=""を指定する場合に

echo "*** WITHOUT IFS="
cal | while read X; do
    echo "$X"
done

echo "*** WITH IFS="
cal | while IFS= read X; do
    echo "$X"
done

実行結果

*** WITHOUT IFS=
September 2015
Su Mo Tu We Th Fr Sa
1  2  3  4  5
6  7  8  9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30

*** WITH IFS=
   September 2015
Su Mo Tu We Th Fr Sa
       1  2  3  4  5
 6  7  8  9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30

てっきりこの代入は一時的だとおもっていたのだが、呼ばれるコマンドがシェル関数かコマンドかで挙動が違うらしい。


http://tiswww.case.edu/php/chet/bash/POSIX

31. Assignment statements preceding shell function calls persist in the
    shell environment after the function returns, as if a POSIX
    special builtin command had been executed.


試した環境
  • FreeBSD /bin/sh
  • CentOS /bin/dash
  • bash
  • bash -o posix
テストスクリプト
f() {
    echo "$X"
    /usr/bin/printenv X
}
g() {
    X="$X.g1" f
    X="$X.g2" f
}
h() {
    X="$X.h1" g
    X="$X.h2" g
}
X=m1 h
X="$X.m2" h
sh
m1.h1.g1
m1.h1.g2
m1.h2.g1
m1.h2.g2
.m2.h1.g1
.m2.h1.g2
.m2.h2.g1
.m2.h2.g2
dash
m1.h1.g1
m1.h1.g1.g2
m1.h1.g1.g2.h2.g1
m1.h1.g1.g2.h2.g1.g2
m1.h1.g1.g2.h2.g1.g2.m2.h1.g1
m1.h1.g1.g2.h2.g1.g2.m2.h1.g1.g2
m1.h1.g1.g2.h2.g1.g2.m2.h1.g1.g2.h2.g1
m1.h1.g1.g2.h2.g1.g2.m2.h1.g1.g2.h2.g1.g2
bash
m1.h1.g1
m1.h1.g1
m1.h1.g2
m1.h1.g2
m1.h2.g1
m1.h2.g1
m1.h2.g2
m1.h2.g2
.m2.h1.g1
.m2.h1.g1
.m2.h1.g2
.m2.h1.g2
.m2.h2.g1
.m2.h2.g1
.m2.h2.g2
.m2.h2.g2
bash -o posix
m1.h1.g1
m1.h1.g1
m1.h1.g1.g2
m1.h1.g1.g2
m1.h1.g1.g2.h2.g1
m1.h1.g1.g2.h2.g1
m1.h1.g1.g2.h2.g1.g2
m1.h1.g1.g2.h2.g1.g2
m1.h1.g1.g2.h2.g1.g2.m2.h1.g1
m1.h1.g1.g2.h2.g1.g2.m2.h1.g1
m1.h1.g1.g2.h2.g1.g2.m2.h1.g1.g2
m1.h1.g1.g2.h2.g1.g2.m2.h1.g1.g2
m1.h1.g1.g2.h2.g1.g2.m2.h1.g1.g2.h2.g1
m1.h1.g1.g2.h2.g1.g2.m2.h1.g1.g2.h2.g1
m1.h1.g1.g2.h2.g1.g2.m2.h1.g1.g2.h2.g1.g2
m1.h1.g1.g2.h2.g1.g2.m2.h1.g1.g2.h2.g1.g2

まとめると

Shell 一時的か? 環境変数か?
fbsd sh Yes No
dash No No
bash Yes Yes
bash -o posix No Yes

続く

koie

sshでリモートログインしてlsするまで

                          _____________
                         |             |
     ____________        | xterm       |           
    |            |       |__________   |           
    | Xserver    |       |          |  |           
    |_________   |       | Xtoolkit |  |     ________        ________    _______  fork  _______ 
    |         |  |       |----------|  |    |        |      |        |  |       | exec |       |
    | xdriver |  |       | Xlib     |  |    | ssh    |      | sshd   |  | shell |----->| ls    |
    |_________|__|       |__________|__|    |________|      |________|  |_______|      |_______|
    |            |       |             |    |        |      |        |  |       |      |       |
    | libc       |       | libc        |    | libc   |      | libc   |  | libc  |      | libc  |
    |____________|       |_____________|    |________|      |________|  |_______|      |_______|
      |        |            |        |         |  |            |  |         |            |  |
USER  |        |            |        |         |  |            |  |         |            |  |
______|________|____________|________|_________|__|____________|__|_________|____________|__|__________
      |        |            |        |         |  |            |  |         |            |  |       
KERN  |        |   event    |        |   "ls"  |  |   "xxx"    |  |  "ls"   |____________|  |       
      |        |  --------> |        | ------> |  |  ------->  |  | ------> |   <------     |readdir
      |        +--(socket)--+        +--(pty)--+  +--(socket)--+  +--(pty)--+    "core"     |       
    [driver]      <-------             <------       <-------       <------                 |       
      |            request              "core"        "yyy"          "core"            [FileSystem] 
      |          X protocol                        SSH protocol                             |
      |                                                                                     |
______|_____________________________________________________________________________________|__________
     _|_____                                                                             ___|___
HW  |       |                                                                           |       |
    | KBD   |                                                                           | disk  |
    | Mouse |                                                                           |_______|
    | Video |
    |_______|

koie

記事検索
月別アーカイブ
アクセスカウンター

    タグ絞り込み検索
    ギャラリー
    • 今日の練習 2018-11-15
    • 今日の練習 2018-11-15
    • 今日の練習 2018-11-13
    • 今日の練習 2018-11-11
    • 今日の練習 2018-11-11
    • 今日の練習 2018-11-10
    Amazon
    楽天市場
    adby google
    LINE読者登録QRコード
    LINE読者登録QRコード
    • ライブドアブログ