ログインしてさらにmixiを楽しもう

コメントを投稿して情報交換!
更新通知を受け取って、最新情報をゲット!

#!/bin/shコミュのsh bash でスレッド管理するには、どうするのが一番スマート?

  • mixiチェック
  • このエントリーをはてなブックマークに追加
こんにちわ、shを使ってダウンロードツールを作ってみようとしております。
仕様としては、MAXCOUNT(10)個同時にダウンロードが出来るように考えております。
書生の乏しい経験から考えた作戦としては
(1) kill USR1 USR2 を使ってダウンロード数を管理する
(2) 関数をバックグランド実行することにより、同時実行を実現

Perl だったら forkが使えるんだけど、JavaだったらThreadが使えるんだけど、shでやろう
とした場合、どうするのが一番スマートなのでしょうか?諸兄の方々の御意見をいただけま
せんでしょうか?

----- ここからスタート ------
#!/bin/sh

MAXCOUNT=10
PROGNAME=$0
PROCESSID=$$

function clean_up
{
return
}

function signal_handle
{
case $1 in
USR1)
MAXCOUNT=`expr $MAXCOUNT + 1`
echo "$PROGNAME: count inc => $MAXCOUNT..." >&2
return
;;
USR2)
MAXCOUNT=`expr $MAXCOUNT - 1`
echo "$PROGNAME: count dec => $MAXCOUNT ..." >&2
return
;;
*) error_exit "$PROGNAME: Terminating on unknown signal"
;;
esac
}

function wgeturl {

## ここでダウンロードコマンドを記述

kill -USR1 $PROCESSID
return
}

trap "signal_handle USR1" USR1
trap "signal_handle USR2" USR2

if [ $# -lt 1 ]
then
echo "usage: getyourfilehost2.sh [url1] [url2] .... "
exit -1
fi

for URL in $@
do
cont_flg=1
kill -USR2 $PROCESSID
while [ $cont_flg -eq 1 ]
do
if [ $MAXCOUNT -gt 0 ]
then
wgeturl $URL &
cont_flg=0
else
echo "Too match ...." >&2
sleep 10
fi
done
done

exit 0

-------- END ---------

コメント(6)

bashですが、こんなのでどうでしょ。全角空白でインデントしてます。

#!/bin/bash
MAXCOUNT=10

wgeturl()
{
・・・・・・
}

for URL in $@
do while test `jobs|wc -l` -ge $MAXCOUNT
  do sleep 10
  done
  wgeturl $URL &
done
wait
#end of file

shだとjobsが使えないので、今のところ、お書きのようにシグナルを使うか、あるいはファイルを作るかぐらいしか思いつかないですが、なんかもっといい手がありそうな気もしています。
SIGCHLD を trap して、その中から次のダウンロードを起動、でどうでしょうか。
これなら、ダウンロードが一つ終わる度に、即座に新しいダウンロードが始まります。

zsh の例ですが、こんな感じで…(bash でも原理は同じです)

#!/bin/zsh

MAXCOUNT=10

targets=($argv)

max_sleep=10

function spawn_dl {
  # $targets and $RANDOM should be refered in parent process.
  local url=$targets[1]; shift targets
  waiturl $[$max_sleep * ($RANDOM / 32768.0)] $url &
}

function waiturl {
  integer sec=$1
  echo invoking $2
# このサンプルでは、乱数秒待つタスクを download の代わりに用いる
  sleep $sec
  echo $2 by $$ after $sec
}

function TRAPCHLD {
  echo -n in TRAPCHLD ' '
  if (($#targets)); then
   spawn_dl
  else
   echo ... No more targets.
  fi
}

while (($#targets)); do
  spawn_dl
  # When number of jobs exceeds MAXCOUNT
  if ((${(%):-%j} >= $MAXCOUNT)); then
   wait
  fi
done

wait
sh に jobs は無いと思っていましたが、FreeeBSD の sh にはありますね。

>h小林さん

>if ((${(%):-%j} >= $MAXCOUNT)); then
この部分は、jobの数を求めて比べてるんですよね。

bashだと、子プロセスを起こさずにjobの数を求めるのが大変。jobsの結果をファイルに書いてそれをwhile readで読んで数をカウントするんでしょうね。

sh だと数のカウントで子プロセスの起動が必要だから駄目ですね。それ以前に配列変数が使えないのでそこの解決も難しい。
返事が遅くなってすみません〜

> otn さん
> この部分は、jobの数を求めて比べてるんですよね。

はい、その通りです。

*: プロンプト展開機能を変数に対して呼び出す ${(%)var}
*: 未知変数の代替値 ${:-str}
*: プロンプトの、ジョブ数置換文字 %j

の組み合わせになっています。

確かに言われてみると、SIGCHLD を使うアプローチは、shell 単体で
他の処理を全て賄えないと、非常に厳しいですね。う〜ん…

個人的には、『shell を使うのは開発効率・生産性のため。
移植性のためではない』と割り切っています。sh/bash に留まって
生産性が落ちる(コードに無理が出る)なら、何のための shell かなぁ、みたいな。

お客様の要求なら仕方がないですが、その場合は、Extra Fee を頂きたいな、な〜んて…(^^;
「とある環境で動けばいい」なら、Perlなりzshなりインストールすればいいのですが、
「ツールとして作って提供する」ということであれば、どこにでもある環境でがんばれるところまでがんばるというのも必要です。
「とある環境」も、サーバー数百台あったりするかもしれないし。

それはそれとして、zshのマニュアル読んでもこんな展開書いてないと思ったら、
>プロンプト展開機能を変数に対して呼び出す
こういう技があったとは・・・・確かにプロンプト展開の所に%jは書いてありました。勉強になった。
> どこにでもある環境でがんばれるところまでがんばるというのも…

そういう世界もあるのでしょうね…

> こういう技があったとは・・・

(%) 自体は昔から知っていたんですが、${:-str} と組み合わせるのは、今回
/usr/share/zsh/$ZSH_VERSION/functions で grep '{(%' * して、はじめて知りました。

いろんな書き方があるなぁと。zsh でスクリプトを書くようになって 18年経ちますが、
未だに学ぶことが… (この書き方自体は10年以上前から使えたと思われます)

ログインすると、みんなのコメントがもっと見れるよ

mixiユーザー
ログインしてコメントしよう!

#!/bin/sh 更新情報

#!/bin/shのメンバーはこんなコミュニティにも参加しています

星印の数は、共通して参加しているメンバーが多いほど増えます。

人気コミュニティランキング