質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
bash

bash(Bourne-again-Shell)は sh(Bourne Shell)のインプリメンテーションに様々な機能が追加されたシェルです。LinuxやMac OS XではBashはデフォルトで導入されています。

シェルスクリプト

シェルスクリプトは、UNIX系のOSもしくはコマンドラインインタプリタ向けに記述されたスクリプト。bash/zshといったシェルによって実行されるため、このように呼ばれています。バッチ処理などに使用されており、テキストファイルに書かれた命令を順に実行します。

Q&A

解決済

2回答

2546閲覧

シェルスクリプトのローカルスコープでグローバル変数を代入する方法について

makkuro

総合スコア57

bash

bash(Bourne-again-Shell)は sh(Bourne Shell)のインプリメンテーションに様々な機能が追加されたシェルです。LinuxやMac OS XではBashはデフォルトで導入されています。

シェルスクリプト

シェルスクリプトは、UNIX系のOSもしくはコマンドラインインタプリタ向けに記述されたスクリプト。bash/zshといったシェルによって実行されるため、このように呼ばれています。バッチ処理などに使用されており、テキストファイルに書かれた命令を順に実行します。

0グッド

0クリップ

投稿2019/06/13 02:25

#やりたいこと
bashの並列処理をしたいので、以下のようなスクリプトを書きました。

bash

1( 2 ( 3 sleep 3s 4 echo start 5 )& 6 wait 7 ( 8 sleep 3s 9 str="hello" 10 )& 11 wait 12)& 13wait 14( 15 ( 16 sleep 3s 17 echo end 18 ) 19 wait 20)& 21wait 22 23echo "hello: ${str}"

うまく動作してくれるのですが、ローカルで代入した値を最終的に取り出す方法が分からず困っています。

あらかじめグローバルで宣言してみたり、関数で代入しようとしましたが、自分の力不足でうまく実装することができませんでした。

よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

一般論としては、シェルスクリプトの独立したプロセス間の通信はファイル経由になります。
具体的な個々のケースではパイプやコマンド置換など別の方法が使えるケースもあります。

質問のコードの場合は、waitしているので並列処理されておらず、サブシェルで実行する意味が無いので、

sh

1 sleep 3s 2 echo start 3 sleep 3s 4 str="hello" 5 sleep 3s 6 echo end 7echo "hello: ${str}"

と書けばいいだけで、工夫の余地がありません。

投稿2019/06/14 13:41

otn

総合スコア84423

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KSwordOfHaste

2019/06/14 13:59 編集

自分は質問者さんが並列動作の予備練習として手始めに実験コードを書いていてその段階で疑問が生じ質問に至ったと解釈しました。 実際に作ろうとしているのはsleepの代わりに何か時間のかかるコマンドを起動しようとしていて、最終的には ( long-time-cmd-1 ) & ( long-time-cmd-2 ) & wait といったコードを想定したいたのではないかと思ったわけです・・・ しかしotnさん回答を拝見して「質問者さんがwaitの意味を把握し損ねている可能性についても一応指摘しておいたほうがよかった」と思い至り、そういう意味でotnさんのご指摘はごもっともと感じました。
otn

2019/06/14 14:07

個々の具体的なケースで考えるしか無いので、本当にやりたいことを書いてもらうしか無いですね。
makkuro

2019/06/14 19:05

otnさん、回答ありがとうございます。 私が意図していたのは、KSwordOfHasteさんがおっしゃっているように、「AとBを並列に処理したいが、CはAとBの変数に依存する」ような状況でした。 質問は丁寧にするように心がけてはいるのですが、私の今回の質問はotnさんがおっしゃっているように具体性を欠いていて誤解を生むものだったと反省しています。 これからはなるべく具体的な質問をするよう心がけます。今後ともよろしくお願いいたします。
otn

2019/06/14 22:14 編集

> 「AとBを並列に処理したいが、CはAとBの変数に依存する」ような状況でした。 いや、そういう抽象的なことは、質問文から分かります。問題は「具体的に何をしたいか」です。 例えば、「カレントディレクトリ以下にある、jpg png gif の拡張子のファイルの数を並列に調べて、全部終わったらその数を表示する」という処理は、 wc -l <(find -name "*.jpg") <(find -name "*.png") <(find -name "*.gif") と簡潔に書けます。 この例だと、findは並列実行されますが、ディスクアクセスがネックなのであまり速くはなりませんが。
guest

0

ベストアンサー

シェル変数は一般のプログラム言語における「スコープ」だけで捉えることはできず「その変数が存在するプロセスがどこになるか」を意識せねばなりません。

bash

1# (A) function 2function foo() { 3 g=$1 4 local l=$2 5} 6 7# (B) script in child process 8( 9 sleep 3s 10 str="hello" 11)&

上の(A)のようにfunctionを用いるとそのfunctionの実行は現在のbashプロセス内で行われるのでグローバル変数を更新することができます。ローカル変数も用いることができてその場合は関数の実行が終わると変数は消えます。
一方(B)のように()で挟んでスクリプトを記述すると、子プロセスを生成しそこで実行されることになります。子プロセスは親プロセスの変数を引き継ぎますが子プロセスで変数を更新したりしても親プロセスにそれは反映されません。

子プロセスから親プロセスへ情報を伝える典型的な方法は標準出力を通じて親へ渡すことです。親は子供の標準出力を受け取りそれを変数へ代入することができます。

bash

1r=`echo foo` 2r=$(echo foo)

上記のようなコードを見たことがあると思います。どちらもechoは子プロセスで実行されますがバッククォートや$(...)で囲むとその中で実行されるコマンドの標準出力結果を受け取り展開してくれるため子供プロセスの実行結果を親で利用することができるわけです。

あるいは複雑なことをするなら結果をファイルへ出力しておき後からそれを取り出すといったこともできるでしょう。

つまり子供プロセスで様々な変数的な情報を更新し、それを一つ一つ親へ反映させるような機構は「ない」と思います。

例えば次のように書けば意図しておられることに近いことができると思います。

bash

1( 2 ( 3 sleep 3s 4 echo start 5 )& 6 wait 7 ( 8 sleep 3s 9 echo "hello" >&4 10 )& 11 wait 12) 4> file1 & 13( 14 ( 15 sleep 3s 16 echo end 17 ) 18 wait 19)& 20wait 21 22echo "hello: $(cat file1)"

投稿2019/06/13 03:28

KSwordOfHaste

総合スコア18392

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

makkuro

2019/06/14 19:00

回答ありがとうございます。 まさに私が求めていた通りの回答で、自分のやりたいこと並列処理を実現することができました。 プロセスとローカル変数の関係などの話も丁寧に書いてくださったので、理解しやすかったです。 とても勉強になりました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問