私はお仕事でSolarisサーバの管理をするときにBourneシェルのスクリプトを
よく作成するのですが、while (do~done)のブロック中に操作した変数の値が
変更されていない事象が発生して、頭を抱えていました。
その件についての議論をコピペします。
UNIX板 シェルスクリプト総合 その4 より
http://pc8.2ch.net/test/read.cgi/unix/1131026501/
【2011.6.18 追記】
gooブログの仕様で、半角の<で始まる英数字が連続すると(エスケープ
させても)文字化けする個所があるので、一部全角文字に置き換えています。
554 名無しさん@お腹いっぱい。 sage 2005/12/10(土) 17:46:47
Solaris8の/bin/shの変数の挙動について教えてください。
COUNTという変数があって
----
COUNT=0 ・・・0で初期化
while ほげ
do
:
(whileループ中にカウントアップ処理)
echo "COUNT=$COUNT" ・・・・(1)
:
done
echo "ループ終了後のCOUNT=$COUNT" ・・・・(2)
----
という処理を実行させたところ、whileループ中の(1)の処理では
正常にカウントアップされた値が表示されるのですが、
ループ終了直後の(2)の値は、何度実行させても0になります。(;´Д`)
こうなる原因はなんでしょうか?また改善方法についてヒントをください。
おながいします。
555 名無しさん@お腹いっぱい。 sage 2005/12/10(土) 18:01:22
>>554
サブシェルで実行されてるから
556 名無しさん@お腹いっぱい。 sage 2005/12/10(土) 18:08:24
サブシェルの問題だと思うが、そのコードだと説明不十分
たとえば下のコードならちゃんと 10 と出る。(Sol8)
#!/bin/sh
count=0
while [ $count -lt 10 ]
do
count=`expr $count + 1`
echo "count=$count"
done
echo "last:$count"
サブシェルの問題はUNIX FAQに載ってる
http://www.nurs.or.jp/~asada/FAQ/UNIX/section3.8.html
557 554 sage 2005/12/10(土) 23:17:02
>>555-556
ありがとうございますた。
とりあえず下記のサンプルで試してみました
558 554 sage 2005/12/10(土) 23:17:33
そうしたら、パターン1とパターン2のLast countは0のままで、
パターン3は正確な値がでました。
ちなみにパターン1のシェル指定を/bin/bashに変えたらあっさりと
正確な値が出たので、とりあえずはbashで逃げることにします。(;´Д`)
559 名無しさん@お腹いっぱい。 sage 2005/12/10(土) 23:33:09
>>558
bashismですね。
560 名無しさん@お腹いっぱい。 sage 2005/12/11(日) 01:13:02
bourne シェルの有名な欠点だな。
kshにすれば、パターン1もパターン2も値を引き継げる。
ちなみに、bashは変態仕様なんで、
パターン1は値を引き継げるけど、パターン2はダメ。
563 名無しさん@お腹いっぱい。 sage 2005/12/11(日) 03:40:29
>>560
bash はパイプライン中に変数を定義しても
ダメみたいだな。
$ bash
$ a=1
$ echo $a
1
$ ls | a=2
$ echo $a
1
561 sage 2005/12/11(日) 01:56:05
うん、Solaris決め打ちでいいならkshにすべきだよね。
562 名無しさん@お腹いっぱい。 sage 2005/12/11(日) 03:10:01
zsh も、ksh と同様パターン1~パターン3全て動くんだよね。
ksh だと対話的に使った場合の機能がちょっと足らんという場合、
bash を使う人が多いんだけど、zsh の方が幸せだと思う。
スクリプトで使う場合には、ksh の方が小さいからいいだろうね。
564 名無しさん@お腹いっぱい。 sage 2005/12/11(日) 10:11:03
kshって昔からSolaris(というかSVR4)に入っているのに
あんまり使われてないね。
メニュー表示(select)とかシェルスクリプトに向いてる機能があるのに
565 554 sage 2005/12/11(日) 10:43:33
コメントありがとうございます。
bashにもそういうクセがあるとは知らなかったし、Solaris標準のシェルであれば
何でもいいので、kshかzshを使ってみようと思います。
566 名無しさん@お腹いっぱい。 sage 2005/12/11(日) 10:59:04
zsh は Solaris 標準じゃねーぞ。
今は勝手に入ってることが多いのはたしかだけど、
インストールのしかたによっては zsh は入らないこともあったはず。
上記の話を総合すると、下記のようなプログラム構成の場合
--------------------
1行目 変数a=0 で初期化
2行目 while (条件式)
3行目 do
4行目 変数a=10と変更
5行目 done
6行目 変数aの値を表示
--------------------
通常のプログラムでは、1行目で宣言した変数aのスコープは全体に渡るので、
6行目を実行させると(1回はwhileループを通るとして) a=10と表示されるのが
本来想定される動きである。
ところが Bourneシェルでは、whileのような制御構造をリダイレクトすると
(引用文のサンプル1のように、whileに対して </etc/passwd のように
ファイルをリダイレクトで読み込ませると)、その部分がサブシェル(≒関数)
として動くため、上記でいう4行目の変数aは、1行目や6行目の変数aとは
別物として扱われる動きになるらしい。
だから6行目の変数aの値が0として表示されるとのこと。
ちなみにbashは上記のようなファイルリダイレクトだと正確な値が出るものの、
引用文のサンプル2のように、whileに対して cat /etc/passwd | とパイプライン
経由でファイルの中身を渡してあげるパターンでは、NGになるようである。
(これがbashの変態仕様らしい)
ともかく、お勉強になりました。
よく作成するのですが、while (do~done)のブロック中に操作した変数の値が
変更されていない事象が発生して、頭を抱えていました。
その件についての議論をコピペします。
UNIX板 シェルスクリプト総合 その4 より
http://pc8.2ch.net/test/read.cgi/unix/1131026501/
【2011.6.18 追記】
gooブログの仕様で、半角の<で始まる英数字が連続すると(エスケープ
させても)文字化けする個所があるので、一部全角文字に置き換えています。
554 名無しさん@お腹いっぱい。 sage 2005/12/10(土) 17:46:47
Solaris8の/bin/shの変数の挙動について教えてください。
COUNTという変数があって
----
COUNT=0 ・・・0で初期化
while ほげ
do
:
(whileループ中にカウントアップ処理)
echo "COUNT=$COUNT" ・・・・(1)
:
done
echo "ループ終了後のCOUNT=$COUNT" ・・・・(2)
----
という処理を実行させたところ、whileループ中の(1)の処理では
正常にカウントアップされた値が表示されるのですが、
ループ終了直後の(2)の値は、何度実行させても0になります。(;´Д`)
こうなる原因はなんでしょうか?また改善方法についてヒントをください。
おながいします。
555 名無しさん@お腹いっぱい。 sage 2005/12/10(土) 18:01:22
>>554
サブシェルで実行されてるから
556 名無しさん@お腹いっぱい。 sage 2005/12/10(土) 18:08:24
サブシェルの問題だと思うが、そのコードだと説明不十分
たとえば下のコードならちゃんと 10 と出る。(Sol8)
#!/bin/sh
count=0
while [ $count -lt 10 ]
do
count=`expr $count + 1`
echo "count=$count"
done
echo "last:$count"
サブシェルの問題はUNIX FAQに載ってる
http://www.nurs.or.jp/~asada/FAQ/UNIX/section3.8.html
557 554 sage 2005/12/10(土) 23:17:02
>>555-556
ありがとうございますた。
とりあえず下記のサンプルで試してみました
パターン1
----------------
#!/bin/sh
COUNT=0
while read i
do
COUNT=`expr $COUNT + 1`
echo "now count = $COUNT"
done </etc/passwd
echo "Last count = $COUNT"
----------------
パターン2
----------------
#!/bin/sh
COUNT=0
cat /etc/passwd | while read i
do
COUNT=`expr $COUNT + 1`
echo "now count = $COUNT"
done
echo "Last count = $COUNT"
----------------
558 554 sage 2005/12/10(土) 23:17:33
パターン3
----------------
#!/bin/sh
COUNT=0
exec 9<&0 < /etc/passwd
while read i
do
COUNT=`expr $COUNT + 1`
echo "now count = $COUNT"
done
exec 0<&9 9<&-
echo "Last count = $COUNT"
----------------
そうしたら、パターン1とパターン2のLast countは0のままで、
パターン3は正確な値がでました。
ちなみにパターン1のシェル指定を/bin/bashに変えたらあっさりと
正確な値が出たので、とりあえずはbashで逃げることにします。(;´Д`)
559 名無しさん@お腹いっぱい。 sage 2005/12/10(土) 23:33:09
>>558
bashismですね。
560 名無しさん@お腹いっぱい。 sage 2005/12/11(日) 01:13:02
bourne シェルの有名な欠点だな。
kshにすれば、パターン1もパターン2も値を引き継げる。
ちなみに、bashは変態仕様なんで、
パターン1は値を引き継げるけど、パターン2はダメ。
563 名無しさん@お腹いっぱい。 sage 2005/12/11(日) 03:40:29
>>560
bash はパイプライン中に変数を定義しても
ダメみたいだな。
$ bash
$ a=1
$ echo $a
1
$ ls | a=2
$ echo $a
1
561 sage 2005/12/11(日) 01:56:05
うん、Solaris決め打ちでいいならkshにすべきだよね。
562 名無しさん@お腹いっぱい。 sage 2005/12/11(日) 03:10:01
zsh も、ksh と同様パターン1~パターン3全て動くんだよね。
ksh だと対話的に使った場合の機能がちょっと足らんという場合、
bash を使う人が多いんだけど、zsh の方が幸せだと思う。
スクリプトで使う場合には、ksh の方が小さいからいいだろうね。
564 名無しさん@お腹いっぱい。 sage 2005/12/11(日) 10:11:03
kshって昔からSolaris(というかSVR4)に入っているのに
あんまり使われてないね。
メニュー表示(select)とかシェルスクリプトに向いてる機能があるのに
565 554 sage 2005/12/11(日) 10:43:33
コメントありがとうございます。
bashにもそういうクセがあるとは知らなかったし、Solaris標準のシェルであれば
何でもいいので、kshかzshを使ってみようと思います。
566 名無しさん@お腹いっぱい。 sage 2005/12/11(日) 10:59:04
zsh は Solaris 標準じゃねーぞ。
今は勝手に入ってることが多いのはたしかだけど、
インストールのしかたによっては zsh は入らないこともあったはず。
上記の話を総合すると、下記のようなプログラム構成の場合
--------------------
1行目 変数a=0 で初期化
2行目 while (条件式)
3行目 do
4行目 変数a=10と変更
5行目 done
6行目 変数aの値を表示
--------------------
通常のプログラムでは、1行目で宣言した変数aのスコープは全体に渡るので、
6行目を実行させると(1回はwhileループを通るとして) a=10と表示されるのが
本来想定される動きである。
ところが Bourneシェルでは、whileのような制御構造をリダイレクトすると
(引用文のサンプル1のように、whileに対して </etc/passwd のように
ファイルをリダイレクトで読み込ませると)、その部分がサブシェル(≒関数)
として動くため、上記でいう4行目の変数aは、1行目や6行目の変数aとは
別物として扱われる動きになるらしい。
だから6行目の変数aの値が0として表示されるとのこと。
ちなみにbashは上記のようなファイルリダイレクトだと正確な値が出るものの、
引用文のサンプル2のように、whileに対して cat /etc/passwd | とパイプライン
経由でファイルの中身を渡してあげるパターンでは、NGになるようである。
(これがbashの変態仕様らしい)
ともかく、お勉強になりました。
UNIXシェルスクリプトコマンドブック 第2版山下 哲典ソフトバンククリエイティブこのアイテムの詳細を見る |
UNIXシェルスクリプトハンドブック (Technical handbook series (001))関根 達夫ソフトバンククリエイティブこのアイテムの詳細を見る |
※コメント投稿者のブログIDはブログ作成者のみに通知されます